mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-12-04 01:04:26 +08:00
1998-09-21 Ben Elliston <bje@cygnus.com>
* New directory. Moved files from ../gcc/objc. From-SVN: r22514
This commit is contained in:
parent
2325c774f8
commit
88e17b57eb
4
libobjc/ChangeLog
Normal file
4
libobjc/ChangeLog
Normal file
@ -0,0 +1,4 @@
|
||||
1998-09-21 Ben Elliston <bje@cygnus.com>
|
||||
|
||||
* New directory. Moved files from ../gcc/objc.
|
||||
|
44
libobjc/NXConstStr.h
Normal file
44
libobjc/NXConstStr.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* Interface for the NXConstantString class for Objective-C.
|
||||
Copyright (C) 1995 Free Software Foundation, Inc.
|
||||
Contributed by Pieter J. Schoenmakers <tiggr@es.ele.tue.nl>
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files
|
||||
compiled with GCC to produce an executable, this does not cause
|
||||
the resulting executable to be covered by the GNU General Public License.
|
||||
This exception does not however invalidate any other reasons why
|
||||
the executable file might be covered by the GNU General Public License. */
|
||||
|
||||
#ifndef __nxconstantstring_INCLUDE_GNU
|
||||
#define __nxconstantstring_INCLUDE_GNU
|
||||
|
||||
#include "objc/Object.h"
|
||||
|
||||
@interface NXConstantString: Object
|
||||
{
|
||||
char *c_string;
|
||||
unsigned int len;
|
||||
}
|
||||
|
||||
-(const char *) cString;
|
||||
-(unsigned int) length;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
42
libobjc/NXConstStr.m
Normal file
42
libobjc/NXConstStr.m
Normal file
@ -0,0 +1,42 @@
|
||||
/* Implementation of the NXConstantString class for Objective-C.
|
||||
Copyright (C) 1995 Free Software Foundation, Inc.
|
||||
Contributed by Pieter J. Schoenmakers <tiggr@es.ele.tue.nl>
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files
|
||||
compiled with GCC to produce an executable, this does not cause
|
||||
the resulting executable to be covered by the GNU General Public License.
|
||||
This exception does not however invalidate any other reasons why
|
||||
the executable file might be covered by the GNU General Public License. */
|
||||
|
||||
#include "objc/NXConstStr.h"
|
||||
|
||||
@implementation NXConstantString
|
||||
|
||||
-(const char *) cString
|
||||
{
|
||||
return (c_string);
|
||||
} /* -cString */
|
||||
|
||||
-(unsigned int) length
|
||||
{
|
||||
return (len);
|
||||
} /* -length */
|
||||
|
||||
@end
|
124
libobjc/Object.h
Normal file
124
libobjc/Object.h
Normal file
@ -0,0 +1,124 @@
|
||||
/* Interface for the Object class for Objective-C.
|
||||
Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled
|
||||
with GCC to produce an executable, this does not cause the resulting
|
||||
executable to be covered by the GNU General Public License. This
|
||||
exception does not however invalidate any other reasons why the
|
||||
executable file might be covered by the GNU General Public License. */
|
||||
|
||||
#ifndef __object_INCLUDE_GNU
|
||||
#define __object_INCLUDE_GNU
|
||||
|
||||
#include <objc/objc.h>
|
||||
#include <objc/typedstream.h>
|
||||
|
||||
/*
|
||||
* All classes are derived from Object. As such,
|
||||
* this is the overhead tacked onto those objects.
|
||||
*/
|
||||
@interface Object
|
||||
{
|
||||
Class isa; /* A pointer to the instance's class structure */
|
||||
}
|
||||
|
||||
/* Initializing classes and instances */
|
||||
+ initialize;
|
||||
- init;
|
||||
|
||||
/* Creating, freeing, and copying instances */
|
||||
+ new;
|
||||
+ alloc;
|
||||
- free;
|
||||
- copy;
|
||||
- shallowCopy;
|
||||
- deepen;
|
||||
- deepCopy;
|
||||
|
||||
/* Identifying classes */
|
||||
- (Class)class;
|
||||
- (Class)superClass;
|
||||
- (MetaClass)metaClass;
|
||||
- (const char *)name;
|
||||
|
||||
/* Identifying and comparing objects */
|
||||
- self;
|
||||
- (unsigned int)hash;
|
||||
- (BOOL)isEqual:anObject;
|
||||
- (int)compare:anotherObject;
|
||||
|
||||
/* Testing object type */
|
||||
- (BOOL)isMetaClass;
|
||||
- (BOOL)isClass;
|
||||
- (BOOL)isInstance;
|
||||
|
||||
/* Testing inheritance relationships */
|
||||
- (BOOL)isKindOf:(Class)aClassObject;
|
||||
- (BOOL)isMemberOf:(Class)aClassObject;
|
||||
- (BOOL)isKindOfClassNamed:(const char *)aClassName;
|
||||
- (BOOL)isMemberOfClassNamed:(const char *)aClassName;
|
||||
|
||||
/* Testing class functionality */
|
||||
+ (BOOL)instancesRespondTo:(SEL)aSel;
|
||||
- (BOOL)respondsTo:(SEL)aSel;
|
||||
|
||||
/* Testing protocol conformance */
|
||||
- (BOOL)conformsTo:(Protocol*)aProtocol;
|
||||
|
||||
/* Introspection */
|
||||
+ (IMP)instanceMethodFor:(SEL)aSel;
|
||||
- (IMP)methodFor:(SEL)aSel;
|
||||
+ (struct objc_method_description *)descriptionForInstanceMethod:(SEL)aSel;
|
||||
- (struct objc_method_description *)descriptionForMethod:(SEL)aSel;
|
||||
|
||||
/* Sending messages determined at run time */
|
||||
- perform:(SEL)aSel;
|
||||
- perform:(SEL)aSel with:anObject;
|
||||
- perform:(SEL)aSel with:anObject1 with:anObject2;
|
||||
|
||||
/* Forwarding */
|
||||
- (retval_t)forward:(SEL)aSel :(arglist_t)argFrame;
|
||||
- (retval_t)performv:(SEL)aSel :(arglist_t)argFrame;
|
||||
|
||||
/* Posing */
|
||||
+ poseAs:(Class)aClassObject;
|
||||
- (Class)transmuteClassTo:(Class)aClassObject;
|
||||
|
||||
/* Enforcing intentions */
|
||||
- subclassResponsibility:(SEL)aSel;
|
||||
- notImplemented:(SEL)aSel;
|
||||
- shouldNotImplement:(SEL)aSel;
|
||||
|
||||
/* Error handling */
|
||||
- doesNotRecognize:(SEL)aSel;
|
||||
- error:(const char *)aString, ...;
|
||||
|
||||
/* Archiving */
|
||||
+ (int)version;
|
||||
+ setVersion:(int)aVersion;
|
||||
+ (int)streamVersion: (TypedStream*)aStream;
|
||||
|
||||
- read: (TypedStream*)aStream;
|
||||
- write: (TypedStream*)aStream;
|
||||
- awake;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
387
libobjc/Object.m
Normal file
387
libobjc/Object.m
Normal file
@ -0,0 +1,387 @@
|
||||
/* The implementation of class Object for Objective-C.
|
||||
Copyright (C) 1993, 1994, 1995, 1997 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled
|
||||
with GCC to produce an executable, this does not cause the resulting
|
||||
executable to be covered by the GNU General Public License. This
|
||||
exception does not however invalidate any other reasons why the
|
||||
executable file might be covered by the GNU General Public License. */
|
||||
|
||||
#include <stdarg.h>
|
||||
#include "objc/Object.h"
|
||||
#include "objc/Protocol.h"
|
||||
#include "objc/objc-api.h"
|
||||
|
||||
extern int errno;
|
||||
|
||||
#define MAX_CLASS_NAME_LEN 256
|
||||
|
||||
@implementation Object
|
||||
|
||||
+ initialize
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
- init
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
+ new
|
||||
{
|
||||
return [[self alloc] init];
|
||||
}
|
||||
|
||||
+ alloc
|
||||
{
|
||||
return class_create_instance(self);
|
||||
}
|
||||
|
||||
- free
|
||||
{
|
||||
return object_dispose(self);
|
||||
}
|
||||
|
||||
- copy
|
||||
{
|
||||
return [[self shallowCopy] deepen];
|
||||
}
|
||||
|
||||
- shallowCopy
|
||||
{
|
||||
return object_copy(self);
|
||||
}
|
||||
|
||||
- deepen
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
- deepCopy
|
||||
{
|
||||
return [self copy];
|
||||
}
|
||||
|
||||
- (Class)class
|
||||
{
|
||||
return object_get_class(self);
|
||||
}
|
||||
|
||||
- (Class)superClass
|
||||
{
|
||||
return object_get_super_class(self);
|
||||
}
|
||||
|
||||
- (MetaClass)metaClass
|
||||
{
|
||||
return object_get_meta_class(self);
|
||||
}
|
||||
|
||||
- (const char *)name
|
||||
{
|
||||
return object_get_class_name(self);
|
||||
}
|
||||
|
||||
- self
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
- (unsigned int)hash
|
||||
{
|
||||
return (size_t)self;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:anObject
|
||||
{
|
||||
return self==anObject;
|
||||
}
|
||||
|
||||
- (int)compare:anotherObject;
|
||||
{
|
||||
if ([self isEqual:anotherObject])
|
||||
return 0;
|
||||
// Ordering objects by their address is pretty useless,
|
||||
// so subclasses should override this is some useful way.
|
||||
else if (self > anotherObject)
|
||||
return 1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
- (BOOL)isMetaClass
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isClass
|
||||
{
|
||||
return object_is_class(self);
|
||||
}
|
||||
|
||||
- (BOOL)isInstance
|
||||
{
|
||||
return object_is_instance(self);
|
||||
}
|
||||
|
||||
- (BOOL)isKindOf:(Class)aClassObject
|
||||
{
|
||||
Class class;
|
||||
|
||||
for (class = self->isa; class!=Nil; class = class_get_super_class(class))
|
||||
if (class==aClassObject)
|
||||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isMemberOf:(Class)aClassObject
|
||||
{
|
||||
return self->isa==aClassObject;
|
||||
}
|
||||
|
||||
- (BOOL)isKindOfClassNamed:(const char *)aClassName
|
||||
{
|
||||
Class class;
|
||||
|
||||
if (aClassName!=NULL)
|
||||
for (class = self->isa; class!=Nil; class = class_get_super_class(class))
|
||||
if (!strcmp(class_get_class_name(class), aClassName))
|
||||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isMemberOfClassNamed:(const char *)aClassName
|
||||
{
|
||||
return ((aClassName!=NULL)
|
||||
&&!strcmp(class_get_class_name(self->isa), aClassName));
|
||||
}
|
||||
|
||||
+ (BOOL)instancesRespondTo:(SEL)aSel
|
||||
{
|
||||
return class_get_instance_method(self, aSel)!=METHOD_NULL;
|
||||
}
|
||||
|
||||
- (BOOL)respondsTo:(SEL)aSel
|
||||
{
|
||||
return ((object_is_instance(self)
|
||||
?class_get_instance_method(self->isa, aSel)
|
||||
:class_get_class_method(self->isa, aSel))!=METHOD_NULL);
|
||||
}
|
||||
|
||||
+ (IMP)instanceMethodFor:(SEL)aSel
|
||||
{
|
||||
return method_get_imp(class_get_instance_method(self, aSel));
|
||||
}
|
||||
|
||||
// Indicates if the receiving class or instance conforms to the given protocol
|
||||
// not usually overridden by subclasses
|
||||
//
|
||||
// Modified 9/5/94 to always search the class object's protocol list, rather
|
||||
// than the meta class.
|
||||
|
||||
+ (BOOL) conformsTo: (Protocol*)aProtocol
|
||||
{
|
||||
int i;
|
||||
struct objc_protocol_list* proto_list;
|
||||
id parent;
|
||||
|
||||
for (proto_list = ((Class)self)->protocols;
|
||||
proto_list; proto_list = proto_list->next)
|
||||
{
|
||||
for (i=0; i < proto_list->count; i++)
|
||||
{
|
||||
if ([proto_list->list[i] conformsTo: aProtocol])
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
if ((parent = [self superClass]))
|
||||
return [parent conformsTo: aProtocol];
|
||||
else
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL) conformsTo: (Protocol*)aProtocol
|
||||
{
|
||||
return [[self class] conformsTo:aProtocol];
|
||||
}
|
||||
|
||||
- (IMP)methodFor:(SEL)aSel
|
||||
{
|
||||
return (method_get_imp(object_is_instance(self)
|
||||
?class_get_instance_method(self->isa, aSel)
|
||||
:class_get_class_method(self->isa, aSel)));
|
||||
}
|
||||
|
||||
+ (struct objc_method_description *)descriptionForInstanceMethod:(SEL)aSel
|
||||
{
|
||||
return ((struct objc_method_description *)
|
||||
class_get_instance_method(self, aSel));
|
||||
}
|
||||
|
||||
- (struct objc_method_description *)descriptionForMethod:(SEL)aSel
|
||||
{
|
||||
return ((struct objc_method_description *)
|
||||
(object_is_instance(self)
|
||||
?class_get_instance_method(self->isa, aSel)
|
||||
:class_get_class_method(self->isa, aSel)));
|
||||
}
|
||||
|
||||
- perform:(SEL)aSel
|
||||
{
|
||||
IMP msg = objc_msg_lookup(self, aSel);
|
||||
if (!msg)
|
||||
return [self error:"invalid selector passed to %s", sel_get_name(_cmd)];
|
||||
return (*msg)(self, aSel);
|
||||
}
|
||||
|
||||
- perform:(SEL)aSel with:anObject
|
||||
{
|
||||
IMP msg = objc_msg_lookup(self, aSel);
|
||||
if (!msg)
|
||||
return [self error:"invalid selector passed to %s", sel_get_name(_cmd)];
|
||||
return (*msg)(self, aSel, anObject);
|
||||
}
|
||||
|
||||
- perform:(SEL)aSel with:anObject1 with:anObject2
|
||||
{
|
||||
IMP msg = objc_msg_lookup(self, aSel);
|
||||
if (!msg)
|
||||
return [self error:"invalid selector passed to %s", sel_get_name(_cmd)];
|
||||
return (*msg)(self, aSel, anObject1, anObject2);
|
||||
}
|
||||
|
||||
- (retval_t)forward:(SEL)aSel :(arglist_t)argFrame
|
||||
{
|
||||
return (retval_t)[self doesNotRecognize: aSel];
|
||||
}
|
||||
|
||||
- (retval_t)performv:(SEL)aSel :(arglist_t)argFrame
|
||||
{
|
||||
return objc_msg_sendv(self, aSel, argFrame);
|
||||
}
|
||||
|
||||
+ poseAs:(Class)aClassObject
|
||||
{
|
||||
return class_pose_as(self, aClassObject);
|
||||
}
|
||||
|
||||
- (Class)transmuteClassTo:(Class)aClassObject
|
||||
{
|
||||
if (object_is_instance(self))
|
||||
if (class_is_class(aClassObject))
|
||||
if (class_get_instance_size(aClassObject)==class_get_instance_size(isa))
|
||||
if ([self isKindOf:aClassObject])
|
||||
{
|
||||
Class old_isa = isa;
|
||||
isa = aClassObject;
|
||||
return old_isa;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- subclassResponsibility:(SEL)aSel
|
||||
{
|
||||
return [self error:"subclass should override %s", sel_get_name(aSel)];
|
||||
}
|
||||
|
||||
- notImplemented:(SEL)aSel
|
||||
{
|
||||
return [self error:"method %s not implemented", sel_get_name(aSel)];
|
||||
}
|
||||
|
||||
- shouldNotImplement:(SEL)aSel
|
||||
{
|
||||
return [self error:"%s should not implement %s",
|
||||
object_get_class_name(self), sel_get_name(aSel)];
|
||||
}
|
||||
|
||||
- doesNotRecognize:(SEL)aSel
|
||||
{
|
||||
return [self error:"%s does not recognize %s",
|
||||
object_get_class_name(self), sel_get_name(aSel)];
|
||||
}
|
||||
|
||||
#ifdef __alpha__
|
||||
extern size_t strlen(const char*);
|
||||
#endif
|
||||
|
||||
- error:(const char *)aString, ...
|
||||
{
|
||||
#define FMT "error: %s (%s)\n%s\n"
|
||||
char fmt[(strlen((char*)FMT)+strlen((char*)object_get_class_name(self))
|
||||
+((aString!=NULL)?strlen((char*)aString):0)+8)];
|
||||
va_list ap;
|
||||
|
||||
sprintf(fmt, FMT, object_get_class_name(self),
|
||||
object_is_instance(self)?"instance":"class",
|
||||
(aString!=NULL)?aString:"");
|
||||
va_start(ap, aString);
|
||||
objc_verror(self, OBJC_ERR_UNKNOWN, fmt, ap);
|
||||
va_end(ap);
|
||||
return nil;
|
||||
#undef FMT
|
||||
}
|
||||
|
||||
+ (int)version
|
||||
{
|
||||
return class_get_version(self);
|
||||
}
|
||||
|
||||
+ setVersion:(int)aVersion
|
||||
{
|
||||
class_set_version(self, aVersion);
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (int)streamVersion: (TypedStream*)aStream
|
||||
{
|
||||
if (aStream->mode == OBJC_READONLY)
|
||||
return objc_get_stream_class_version (aStream, self);
|
||||
else
|
||||
return class_get_version (self);
|
||||
}
|
||||
|
||||
// These are used to write or read the instance variables
|
||||
// declared in this particular part of the object. Subclasses
|
||||
// should extend these, by calling [super read/write: aStream]
|
||||
// before doing their own archiving. These methods are private, in
|
||||
// the sense that they should only be called from subclasses.
|
||||
|
||||
- read: (TypedStream*)aStream
|
||||
{
|
||||
// [super read: aStream];
|
||||
return self;
|
||||
}
|
||||
|
||||
- write: (TypedStream*)aStream
|
||||
{
|
||||
// [super write: aStream];
|
||||
return self;
|
||||
}
|
||||
|
||||
- awake
|
||||
{
|
||||
// [super awake];
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
58
libobjc/Protocol.h
Normal file
58
libobjc/Protocol.h
Normal file
@ -0,0 +1,58 @@
|
||||
/* Declare the class Protocol for Objective C programs.
|
||||
Copyright (C) 1993 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files
|
||||
compiled with GCC to produce an executable, this does not cause
|
||||
the resulting executable to be covered by the GNU General Public License.
|
||||
This exception does not however invalidate any other reasons why
|
||||
the executable file might be covered by the GNU General Public License. */
|
||||
|
||||
#ifndef __Protocol_INCLUDE_GNU
|
||||
#define __Protocol_INCLUDE_GNU
|
||||
|
||||
#include "objc/Object.h"
|
||||
|
||||
@interface Protocol : Object
|
||||
{
|
||||
@private
|
||||
char *protocol_name;
|
||||
struct objc_protocol_list *protocol_list;
|
||||
struct objc_method_description_list *instance_methods, *class_methods;
|
||||
}
|
||||
|
||||
/* Obtaining attributes intrinsic to the protocol */
|
||||
|
||||
- (const char *)name;
|
||||
|
||||
/* Testing protocol conformance */
|
||||
|
||||
- (BOOL) conformsTo: (Protocol *)aProtocolObject;
|
||||
|
||||
/* Looking up information specific to a protocol */
|
||||
|
||||
- (struct objc_method_description *) descriptionForInstanceMethod:(SEL)aSel;
|
||||
- (struct objc_method_description *) descriptionForClassMethod:(SEL)aSel;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
||||
#endif __Protocol_INCLUDE_GNU
|
128
libobjc/Protocol.m
Normal file
128
libobjc/Protocol.m
Normal file
@ -0,0 +1,128 @@
|
||||
/* This file contains the implementation of class Protocol.
|
||||
Copyright (C) 1993 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files
|
||||
compiled with GCC to produce an executable, this does not cause
|
||||
the resulting executable to be covered by the GNU General Public License.
|
||||
This exception does not however invalidate any other reasons why
|
||||
the executable file might be covered by the GNU General Public License. */
|
||||
|
||||
#include "objc/Protocol.h"
|
||||
#include "objc/objc-api.h"
|
||||
|
||||
/* Method description list */
|
||||
struct objc_method_description_list {
|
||||
int count;
|
||||
struct objc_method_description list[1];
|
||||
};
|
||||
|
||||
|
||||
@implementation Protocol
|
||||
{
|
||||
@private
|
||||
char *protocol_name;
|
||||
struct objc_protocol_list *protocol_list;
|
||||
struct objc_method_description_list *instance_methods, *class_methods;
|
||||
}
|
||||
|
||||
/* Obtaining attributes intrinsic to the protocol */
|
||||
|
||||
- (const char *)name
|
||||
{
|
||||
return protocol_name;
|
||||
}
|
||||
|
||||
/* Testing protocol conformance */
|
||||
|
||||
- (BOOL) conformsTo: (Protocol *)aProtocolObject
|
||||
{
|
||||
int i;
|
||||
struct objc_protocol_list* proto_list;
|
||||
|
||||
if (!strcmp(aProtocolObject->protocol_name, self->protocol_name))
|
||||
return YES;
|
||||
|
||||
for (proto_list = protocol_list; proto_list; proto_list = proto_list->next)
|
||||
{
|
||||
for (i=0; i < proto_list->count; i++)
|
||||
{
|
||||
if ([proto_list->list[i] conformsTo: aProtocolObject])
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
/* Looking up information specific to a protocol */
|
||||
|
||||
- (struct objc_method_description *) descriptionForInstanceMethod:(SEL)aSel
|
||||
{
|
||||
int i;
|
||||
struct objc_protocol_list* proto_list;
|
||||
const char* name = sel_get_name (aSel);
|
||||
struct objc_method_description *result;
|
||||
|
||||
for (i = 0; i < instance_methods->count; i++)
|
||||
{
|
||||
if (!strcmp ((char*)instance_methods->list[i].name, name))
|
||||
return &(instance_methods->list[i]);
|
||||
}
|
||||
|
||||
for (proto_list = protocol_list; proto_list; proto_list = proto_list->next)
|
||||
{
|
||||
for (i=0; i < proto_list->count; i++)
|
||||
{
|
||||
if ((result = [proto_list->list[i]
|
||||
descriptionForInstanceMethod: aSel]))
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
- (struct objc_method_description *) descriptionForClassMethod:(SEL)aSel;
|
||||
{
|
||||
int i;
|
||||
struct objc_protocol_list* proto_list;
|
||||
const char* name = sel_get_name (aSel);
|
||||
struct objc_method_description *result;
|
||||
|
||||
for (i = 0; i < class_methods->count; i++)
|
||||
{
|
||||
if (!strcmp ((char*)class_methods->list[i].name, name))
|
||||
return &(class_methods->list[i]);
|
||||
}
|
||||
|
||||
for (proto_list = protocol_list; proto_list; proto_list = proto_list->next)
|
||||
{
|
||||
for (i=0; i < proto_list->count; i++)
|
||||
{
|
||||
if ((result = [proto_list->list[i]
|
||||
descriptionForClassMethod: aSel]))
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@end
|
1651
libobjc/archive.c
Normal file
1651
libobjc/archive.c
Normal file
File diff suppressed because it is too large
Load Diff
358
libobjc/class.c
Normal file
358
libobjc/class.c
Normal file
@ -0,0 +1,358 @@
|
||||
/* GNU Objective C Runtime class related functions
|
||||
Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Kresten Krab Thorup and Dennis Glatting.
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include "runtime.h" /* the kitchen sink */
|
||||
#include "sarray.h"
|
||||
|
||||
/* The table of classname->class. Used for objc_lookup_class and friends */
|
||||
static cache_ptr __objc_class_hash = 0; /* !T:MUTEX */
|
||||
|
||||
/* This is a hook which is called by objc_get_class and
|
||||
objc_lookup_class if the runtime is not able to find the class.
|
||||
This may e.g. try to load in the class using dynamic loading */
|
||||
Class (*_objc_lookup_class)(const char* name) = 0; /* !T:SAFE */
|
||||
|
||||
|
||||
/* True when class links has been resolved */
|
||||
BOOL __objc_class_links_resolved = NO; /* !T:UNUSED */
|
||||
|
||||
|
||||
/* Initial number of buckets size of class hash table. */
|
||||
#define CLASS_HASH_SIZE 32
|
||||
|
||||
void __objc_init_class_tables()
|
||||
{
|
||||
/* Allocate the class hash table */
|
||||
|
||||
if(__objc_class_hash)
|
||||
return;
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
__objc_class_hash
|
||||
= hash_new (CLASS_HASH_SIZE,
|
||||
(hash_func_type) hash_string,
|
||||
(compare_func_type) compare_strings);
|
||||
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
}
|
||||
|
||||
/* This function adds a class to the class hash table, and assigns the
|
||||
class a number, unless it's already known */
|
||||
void
|
||||
__objc_add_class_to_hash(Class class)
|
||||
{
|
||||
Class h_class;
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
/* make sure the table is there */
|
||||
assert(__objc_class_hash);
|
||||
|
||||
/* make sure it's not a meta class */
|
||||
assert(CLS_ISCLASS(class));
|
||||
|
||||
/* Check to see if the class is already in the hash table. */
|
||||
h_class = hash_value_for_key (__objc_class_hash, class->name);
|
||||
if (!h_class)
|
||||
{
|
||||
/* The class isn't in the hash table. Add the class and assign a class
|
||||
number. */
|
||||
static unsigned int class_number = 1;
|
||||
|
||||
CLS_SETNUMBER(class, class_number);
|
||||
CLS_SETNUMBER(class->class_pointer, class_number);
|
||||
|
||||
++class_number;
|
||||
hash_add (&__objc_class_hash, class->name, class);
|
||||
}
|
||||
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
}
|
||||
|
||||
/* Get the class object for the class named NAME. If NAME does not
|
||||
identify a known class, the hook _objc_lookup_class is called. If
|
||||
this fails, nil is returned */
|
||||
Class objc_lookup_class (const char* name)
|
||||
{
|
||||
Class class;
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
/* Make sure the class hash table exists. */
|
||||
assert (__objc_class_hash);
|
||||
|
||||
class = hash_value_for_key (__objc_class_hash, name);
|
||||
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
|
||||
if (class)
|
||||
return class;
|
||||
|
||||
if (_objc_lookup_class)
|
||||
return (*_objc_lookup_class)(name);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the class object for the class named NAME. If NAME does not
|
||||
identify a known class, the hook _objc_lookup_class is called. If
|
||||
this fails, an error message is issued and the system aborts */
|
||||
Class
|
||||
objc_get_class (const char *name)
|
||||
{
|
||||
Class class;
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
/* Make sure the class hash table exists. */
|
||||
assert (__objc_class_hash);
|
||||
|
||||
class = hash_value_for_key (__objc_class_hash, name);
|
||||
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
|
||||
if (class)
|
||||
return class;
|
||||
|
||||
if (_objc_lookup_class)
|
||||
class = (*_objc_lookup_class)(name);
|
||||
|
||||
if(class)
|
||||
return class;
|
||||
|
||||
objc_error(nil, OBJC_ERR_BAD_CLASS,
|
||||
"objc runtime: cannot find class %s\n", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
MetaClass
|
||||
objc_get_meta_class(const char *name)
|
||||
{
|
||||
return objc_get_class(name)->class_pointer;
|
||||
}
|
||||
|
||||
/* This function provides a way to enumerate all the classes in the
|
||||
executable. Pass *ENUM_STATE == NULL to start the enumeration. The
|
||||
function will return 0 when there are no more classes.
|
||||
For example:
|
||||
id class;
|
||||
void *es = NULL;
|
||||
while ((class = objc_next_class(&es)))
|
||||
... do something with class;
|
||||
*/
|
||||
Class
|
||||
objc_next_class(void **enum_state)
|
||||
{
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
/* make sure the table is there */
|
||||
assert(__objc_class_hash);
|
||||
|
||||
*(node_ptr*)enum_state =
|
||||
hash_next(__objc_class_hash, *(node_ptr*)enum_state);
|
||||
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
|
||||
if (*(node_ptr*)enum_state)
|
||||
return (*(node_ptr*)enum_state)->value;
|
||||
return (Class)0;
|
||||
}
|
||||
|
||||
/* Resolve super/subclass links for all classes. The only thing we
|
||||
can be sure of is that the class_pointer for class objects point
|
||||
to the right meta class objects */
|
||||
void __objc_resolve_class_links()
|
||||
{
|
||||
node_ptr node;
|
||||
Class object_class = objc_get_class ("Object");
|
||||
|
||||
assert(object_class);
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
/* Assign subclass links */
|
||||
for (node = hash_next (__objc_class_hash, NULL); node;
|
||||
node = hash_next (__objc_class_hash, node))
|
||||
{
|
||||
Class class1 = node->value;
|
||||
|
||||
/* Make sure we have what we think we have. */
|
||||
assert (CLS_ISCLASS(class1));
|
||||
assert (CLS_ISMETA(class1->class_pointer));
|
||||
|
||||
/* The class_pointer of all meta classes point to Object's meta class. */
|
||||
class1->class_pointer->class_pointer = object_class->class_pointer;
|
||||
|
||||
if (!(CLS_ISRESOLV(class1)))
|
||||
{
|
||||
CLS_SETRESOLV(class1);
|
||||
CLS_SETRESOLV(class1->class_pointer);
|
||||
|
||||
if(class1->super_class)
|
||||
{
|
||||
Class a_super_class
|
||||
= objc_get_class ((char *) class1->super_class);
|
||||
|
||||
assert (a_super_class);
|
||||
|
||||
DEBUG_PRINTF ("making class connections for: %s\n",
|
||||
class1->name);
|
||||
|
||||
/* assign subclass links for superclass */
|
||||
class1->sibling_class = a_super_class->subclass_list;
|
||||
a_super_class->subclass_list = class1;
|
||||
|
||||
/* Assign subclass links for meta class of superclass */
|
||||
if (a_super_class->class_pointer)
|
||||
{
|
||||
class1->class_pointer->sibling_class
|
||||
= a_super_class->class_pointer->subclass_list;
|
||||
a_super_class->class_pointer->subclass_list
|
||||
= class1->class_pointer;
|
||||
}
|
||||
}
|
||||
else /* a root class, make its meta object */
|
||||
/* be a subclass of Object */
|
||||
{
|
||||
class1->class_pointer->sibling_class
|
||||
= object_class->subclass_list;
|
||||
object_class->subclass_list = class1->class_pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Assign superclass links */
|
||||
for (node = hash_next (__objc_class_hash, NULL); node;
|
||||
node = hash_next (__objc_class_hash, node))
|
||||
{
|
||||
Class class1 = node->value;
|
||||
Class sub_class;
|
||||
for (sub_class = class1->subclass_list; sub_class;
|
||||
sub_class = sub_class->sibling_class)
|
||||
{
|
||||
sub_class->super_class = class1;
|
||||
if(CLS_ISCLASS(sub_class))
|
||||
sub_class->class_pointer->super_class = class1->class_pointer;
|
||||
}
|
||||
}
|
||||
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define CLASSOF(c) ((c)->class_pointer)
|
||||
|
||||
Class
|
||||
class_pose_as (Class impostor, Class super_class)
|
||||
{
|
||||
node_ptr node;
|
||||
Class class1;
|
||||
|
||||
if (!CLS_ISRESOLV (impostor))
|
||||
__objc_resolve_class_links ();
|
||||
|
||||
/* preconditions */
|
||||
assert (impostor);
|
||||
assert (super_class);
|
||||
assert (impostor->super_class == super_class);
|
||||
assert (CLS_ISCLASS (impostor));
|
||||
assert (CLS_ISCLASS (super_class));
|
||||
assert (impostor->instance_size == super_class->instance_size);
|
||||
|
||||
{
|
||||
Class *subclass = &(super_class->subclass_list);
|
||||
|
||||
/* move subclasses of super_class to impostor */
|
||||
while (*subclass)
|
||||
{
|
||||
Class nextSub = (*subclass)->sibling_class;
|
||||
|
||||
if (*subclass != impostor)
|
||||
{
|
||||
Class sub = *subclass;
|
||||
|
||||
/* classes */
|
||||
sub->sibling_class = impostor->subclass_list;
|
||||
sub->super_class = impostor;
|
||||
impostor->subclass_list = sub;
|
||||
|
||||
/* It will happen that SUB is not a class object if it is
|
||||
the top of the meta class hierarchy chain. (root
|
||||
meta-class objects inherit their class object) If that is
|
||||
the case... don't mess with the meta-meta class. */
|
||||
if (CLS_ISCLASS (sub))
|
||||
{
|
||||
/* meta classes */
|
||||
CLASSOF (sub)->sibling_class =
|
||||
CLASSOF (impostor)->subclass_list;
|
||||
CLASSOF (sub)->super_class = CLASSOF (impostor);
|
||||
CLASSOF (impostor)->subclass_list = CLASSOF (sub);
|
||||
}
|
||||
}
|
||||
|
||||
*subclass = nextSub;
|
||||
}
|
||||
|
||||
/* set subclasses of superclass to be impostor only */
|
||||
super_class->subclass_list = impostor;
|
||||
CLASSOF (super_class)->subclass_list = CLASSOF (impostor);
|
||||
|
||||
/* set impostor to have no sibling classes */
|
||||
impostor->sibling_class = 0;
|
||||
CLASSOF (impostor)->sibling_class = 0;
|
||||
}
|
||||
|
||||
/* check relationship of impostor and super_class is kept. */
|
||||
assert (impostor->super_class == super_class);
|
||||
assert (CLASSOF (impostor)->super_class == CLASSOF (super_class));
|
||||
|
||||
/* This is how to update the lookup table. Regardless of
|
||||
what the keys of the hashtable is, change all values that are
|
||||
superclass into impostor. */
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
for (node = hash_next (__objc_class_hash, NULL); node;
|
||||
node = hash_next (__objc_class_hash, node))
|
||||
{
|
||||
class1 = (Class)node->value;
|
||||
if (class1 == super_class)
|
||||
{
|
||||
node->value = impostor; /* change hash table value */
|
||||
}
|
||||
}
|
||||
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
|
||||
/* next, we update the dispatch tables... */
|
||||
__objc_update_dispatch_table_for_class (CLASSOF (impostor));
|
||||
__objc_update_dispatch_table_for_class (impostor);
|
||||
|
||||
return impostor;
|
||||
}
|
||||
|
||||
|
912
libobjc/encoding.c
Normal file
912
libobjc/encoding.c
Normal file
@ -0,0 +1,912 @@
|
||||
/* Encoding of types for Objective C.
|
||||
Copyright (C) 1993, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
|
||||
Contributed by Kresten Krab Thorup
|
||||
Bitfield support by Ovidiu Predescu
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files
|
||||
compiled with GCC to produce an executable, this does not cause
|
||||
the resulting executable to be covered by the GNU General Public License.
|
||||
This exception does not however invalidate any other reasons why
|
||||
the executable file might be covered by the GNU General Public License. */
|
||||
|
||||
#include "tconfig.h"
|
||||
#include "objc-api.h"
|
||||
#include "encoding.h"
|
||||
|
||||
#define MAX(X, Y) \
|
||||
({ typeof(X) __x = (X), __y = (Y); \
|
||||
(__x > __y ? __x : __y); })
|
||||
|
||||
#define MIN(X, Y) \
|
||||
({ typeof(X) __x = (X), __y = (Y); \
|
||||
(__x < __y ? __x : __y); })
|
||||
|
||||
#define ROUND(V, A) \
|
||||
({ typeof(V) __v=(V); typeof(A) __a=(A); \
|
||||
__a*((__v+__a-1)/__a); })
|
||||
|
||||
|
||||
/* Various hacks for objc_layout_record. These are used by the target
|
||||
macros. */
|
||||
|
||||
#define TREE_CODE(TYPE) *TYPE
|
||||
#define RECORD_TYPE _C_STRUCT_B
|
||||
#define UNION_TYPE _C_UNION_B
|
||||
#define QUAL_UNION_TYPE _C_UNION_B
|
||||
|
||||
#define TYPE_FIELDS(TYPE) objc_skip_typespec (TYPE)
|
||||
|
||||
#define DECL_MODE(TYPE) *(TYPE)
|
||||
|
||||
#define DFmode _C_DBL
|
||||
|
||||
|
||||
|
||||
static inline int
|
||||
atoi (const char* str)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
while (isdigit (*str))
|
||||
res *= 10, res += (*str++ - '0');
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
return the size of an object specified by type
|
||||
*/
|
||||
|
||||
int
|
||||
objc_sizeof_type (const char* type)
|
||||
{
|
||||
/* Skip the variable name if any */
|
||||
if (*type == '"')
|
||||
{
|
||||
for (type++; *type++ != '"';)
|
||||
/* do nothing */;
|
||||
}
|
||||
|
||||
switch(*type) {
|
||||
case _C_ID:
|
||||
return sizeof(id);
|
||||
break;
|
||||
|
||||
case _C_CLASS:
|
||||
return sizeof(Class);
|
||||
break;
|
||||
|
||||
case _C_SEL:
|
||||
return sizeof(SEL);
|
||||
break;
|
||||
|
||||
case _C_CHR:
|
||||
return sizeof(char);
|
||||
break;
|
||||
|
||||
case _C_UCHR:
|
||||
return sizeof(unsigned char);
|
||||
break;
|
||||
|
||||
case _C_SHT:
|
||||
return sizeof(short);
|
||||
break;
|
||||
|
||||
case _C_USHT:
|
||||
return sizeof(unsigned short);
|
||||
break;
|
||||
|
||||
case _C_INT:
|
||||
return sizeof(int);
|
||||
break;
|
||||
|
||||
case _C_UINT:
|
||||
return sizeof(unsigned int);
|
||||
break;
|
||||
|
||||
case _C_LNG:
|
||||
return sizeof(long);
|
||||
break;
|
||||
|
||||
case _C_ULNG:
|
||||
return sizeof(unsigned long);
|
||||
break;
|
||||
|
||||
case _C_LNG_LNG:
|
||||
return sizeof(long long);
|
||||
break;
|
||||
|
||||
case _C_ULNG_LNG:
|
||||
return sizeof(unsigned long long);
|
||||
break;
|
||||
|
||||
case _C_FLT:
|
||||
return sizeof(float);
|
||||
break;
|
||||
|
||||
case _C_DBL:
|
||||
return sizeof(double);
|
||||
break;
|
||||
|
||||
case _C_VOID:
|
||||
return sizeof(void);
|
||||
break;
|
||||
case _C_PTR:
|
||||
case _C_ATOM:
|
||||
case _C_CHARPTR:
|
||||
return sizeof(char*);
|
||||
break;
|
||||
|
||||
case _C_ARY_B:
|
||||
{
|
||||
int len = atoi(type+1);
|
||||
while (isdigit(*++type));
|
||||
return len*objc_aligned_size (type);
|
||||
}
|
||||
break;
|
||||
|
||||
case _C_BFLD:
|
||||
{
|
||||
/* The new encoding of bitfields is: b 'position' 'type' 'size' */
|
||||
int position, size;
|
||||
int startByte, endByte;
|
||||
|
||||
position = atoi (type + 1);
|
||||
while (isdigit (*++type));
|
||||
size = atoi (type + 1);
|
||||
|
||||
startByte = position / BITS_PER_UNIT;
|
||||
endByte = (position + size) / BITS_PER_UNIT;
|
||||
return endByte - startByte;
|
||||
}
|
||||
|
||||
case _C_STRUCT_B:
|
||||
{
|
||||
struct objc_struct_layout layout;
|
||||
unsigned int size;
|
||||
|
||||
objc_layout_structure (type, &layout);
|
||||
while (objc_layout_structure_next_member (&layout))
|
||||
/* do nothing */ ;
|
||||
objc_layout_finish_structure (&layout, &size, NULL);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
case _C_UNION_B:
|
||||
{
|
||||
int max_size = 0;
|
||||
while (*type != _C_UNION_E && *type++ != '=') /* do nothing */;
|
||||
while (*type != _C_UNION_E)
|
||||
{
|
||||
/* Skip the variable name if any */
|
||||
if (*type == '"')
|
||||
{
|
||||
for (type++; *type++ != '"';)
|
||||
/* do nothing */;
|
||||
}
|
||||
max_size = MAX (max_size, objc_sizeof_type (type));
|
||||
type = objc_skip_typespec (type);
|
||||
}
|
||||
return max_size;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
objc_error(nil, OBJC_ERR_BAD_TYPE, "unknown type %s\n", type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Return the alignment of an object specified by type
|
||||
*/
|
||||
|
||||
int
|
||||
objc_alignof_type(const char* type)
|
||||
{
|
||||
/* Skip the variable name if any */
|
||||
if (*type == '"')
|
||||
{
|
||||
for (type++; *type++ != '"';)
|
||||
/* do nothing */;
|
||||
}
|
||||
switch(*type) {
|
||||
case _C_ID:
|
||||
return __alignof__(id);
|
||||
break;
|
||||
|
||||
case _C_CLASS:
|
||||
return __alignof__(Class);
|
||||
break;
|
||||
|
||||
case _C_SEL:
|
||||
return __alignof__(SEL);
|
||||
break;
|
||||
|
||||
case _C_CHR:
|
||||
return __alignof__(char);
|
||||
break;
|
||||
|
||||
case _C_UCHR:
|
||||
return __alignof__(unsigned char);
|
||||
break;
|
||||
|
||||
case _C_SHT:
|
||||
return __alignof__(short);
|
||||
break;
|
||||
|
||||
case _C_USHT:
|
||||
return __alignof__(unsigned short);
|
||||
break;
|
||||
|
||||
case _C_INT:
|
||||
return __alignof__(int);
|
||||
break;
|
||||
|
||||
case _C_UINT:
|
||||
return __alignof__(unsigned int);
|
||||
break;
|
||||
|
||||
case _C_LNG:
|
||||
return __alignof__(long);
|
||||
break;
|
||||
|
||||
case _C_ULNG:
|
||||
return __alignof__(unsigned long);
|
||||
break;
|
||||
|
||||
case _C_LNG_LNG:
|
||||
return __alignof__(long long);
|
||||
break;
|
||||
|
||||
case _C_ULNG_LNG:
|
||||
return __alignof__(unsigned long long);
|
||||
break;
|
||||
|
||||
case _C_FLT:
|
||||
return __alignof__(float);
|
||||
break;
|
||||
|
||||
case _C_DBL:
|
||||
return __alignof__(double);
|
||||
break;
|
||||
|
||||
case _C_PTR:
|
||||
case _C_ATOM:
|
||||
case _C_CHARPTR:
|
||||
return __alignof__(char*);
|
||||
break;
|
||||
|
||||
case _C_ARY_B:
|
||||
while (isdigit(*++type)) /* do nothing */;
|
||||
return objc_alignof_type (type);
|
||||
|
||||
case _C_STRUCT_B:
|
||||
{
|
||||
struct objc_struct_layout layout;
|
||||
unsigned int align;
|
||||
|
||||
objc_layout_structure (type, &layout);
|
||||
while (objc_layout_structure_next_member (&layout))
|
||||
/* do nothing */;
|
||||
objc_layout_finish_structure (&layout, NULL, &align);
|
||||
|
||||
return align;
|
||||
}
|
||||
|
||||
case _C_UNION_B:
|
||||
{
|
||||
int maxalign = 0;
|
||||
while (*type != _C_UNION_E && *type++ != '=') /* do nothing */;
|
||||
while (*type != _C_UNION_E)
|
||||
{
|
||||
/* Skip the variable name if any */
|
||||
if (*type == '"')
|
||||
{
|
||||
for (type++; *type++ != '"';)
|
||||
/* do nothing */;
|
||||
}
|
||||
maxalign = MAX (maxalign, objc_alignof_type (type));
|
||||
type = objc_skip_typespec (type);
|
||||
}
|
||||
return maxalign;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
objc_error(nil, OBJC_ERR_BAD_TYPE, "unknown type %s\n", type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
The aligned size if the size rounded up to the nearest alignment.
|
||||
*/
|
||||
|
||||
int
|
||||
objc_aligned_size (const char* type)
|
||||
{
|
||||
int size, align;
|
||||
|
||||
/* Skip the variable name */
|
||||
if (*type == '"')
|
||||
{
|
||||
for (type++; *type++ != '"';)
|
||||
/* do nothing */;
|
||||
}
|
||||
|
||||
size = objc_sizeof_type (type);
|
||||
align = objc_alignof_type (type);
|
||||
|
||||
return ROUND (size, align);
|
||||
}
|
||||
|
||||
/*
|
||||
The size rounded up to the nearest integral of the wordsize, taken
|
||||
to be the size of a void*.
|
||||
*/
|
||||
|
||||
int
|
||||
objc_promoted_size (const char* type)
|
||||
{
|
||||
int size, wordsize;
|
||||
|
||||
/* Skip the variable name */
|
||||
if (*type == '"')
|
||||
{
|
||||
for (type++; *type++ != '"';)
|
||||
/* do nothing */;
|
||||
}
|
||||
|
||||
size = objc_sizeof_type (type);
|
||||
wordsize = sizeof (void*);
|
||||
|
||||
return ROUND (size, wordsize);
|
||||
}
|
||||
|
||||
/*
|
||||
Skip type qualifiers. These may eventually precede typespecs
|
||||
occurring in method prototype encodings.
|
||||
*/
|
||||
|
||||
inline const char*
|
||||
objc_skip_type_qualifiers (const char* type)
|
||||
{
|
||||
while (*type == _C_CONST
|
||||
|| *type == _C_IN
|
||||
|| *type == _C_INOUT
|
||||
|| *type == _C_OUT
|
||||
|| *type == _C_BYCOPY
|
||||
|| *type == _C_ONEWAY
|
||||
|| *type == _C_GCINVISIBLE)
|
||||
{
|
||||
type += 1;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Skip one typespec element. If the typespec is prepended by type
|
||||
qualifiers, these are skipped as well.
|
||||
*/
|
||||
|
||||
const char*
|
||||
objc_skip_typespec (const char* type)
|
||||
{
|
||||
/* Skip the variable name if any */
|
||||
if (*type == '"')
|
||||
{
|
||||
for (type++; *type++ != '"';)
|
||||
/* do nothing */;
|
||||
}
|
||||
|
||||
type = objc_skip_type_qualifiers (type);
|
||||
|
||||
switch (*type) {
|
||||
|
||||
case _C_ID:
|
||||
/* An id may be annotated by the actual type if it is known
|
||||
with the @"ClassName" syntax */
|
||||
|
||||
if (*++type != '"')
|
||||
return type;
|
||||
else
|
||||
{
|
||||
while (*++type != '"') /* do nothing */;
|
||||
return type + 1;
|
||||
}
|
||||
|
||||
/* The following are one character type codes */
|
||||
case _C_CLASS:
|
||||
case _C_SEL:
|
||||
case _C_CHR:
|
||||
case _C_UCHR:
|
||||
case _C_CHARPTR:
|
||||
case _C_ATOM:
|
||||
case _C_SHT:
|
||||
case _C_USHT:
|
||||
case _C_INT:
|
||||
case _C_UINT:
|
||||
case _C_LNG:
|
||||
case _C_ULNG:
|
||||
case _C_LNG_LNG:
|
||||
case _C_ULNG_LNG:
|
||||
case _C_FLT:
|
||||
case _C_DBL:
|
||||
case _C_VOID:
|
||||
case _C_UNDEF:
|
||||
return ++type;
|
||||
break;
|
||||
|
||||
case _C_ARY_B:
|
||||
/* skip digits, typespec and closing ']' */
|
||||
|
||||
while(isdigit(*++type));
|
||||
type = objc_skip_typespec(type);
|
||||
if (*type == _C_ARY_E)
|
||||
return ++type;
|
||||
else
|
||||
{
|
||||
objc_error(nil, OBJC_ERR_BAD_TYPE, "bad array type %s\n", type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case _C_BFLD:
|
||||
/* The new encoding of bitfields is: b 'position' 'type' 'size' */
|
||||
while (isdigit (*++type)); /* skip position */
|
||||
while (isdigit (*++type)); /* skip type and size */
|
||||
return type;
|
||||
|
||||
case _C_STRUCT_B:
|
||||
/* skip name, and elements until closing '}' */
|
||||
|
||||
while (*type != _C_STRUCT_E && *type++ != '=');
|
||||
while (*type != _C_STRUCT_E) { type = objc_skip_typespec (type); }
|
||||
return ++type;
|
||||
|
||||
case _C_UNION_B:
|
||||
/* skip name, and elements until closing ')' */
|
||||
|
||||
while (*type != _C_UNION_E && *type++ != '=');
|
||||
while (*type != _C_UNION_E) { type = objc_skip_typespec (type); }
|
||||
return ++type;
|
||||
|
||||
case _C_PTR:
|
||||
/* Just skip the following typespec */
|
||||
|
||||
return objc_skip_typespec (++type);
|
||||
|
||||
default:
|
||||
{
|
||||
objc_error(nil, OBJC_ERR_BAD_TYPE, "unknown type %s\n", type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Skip an offset as part of a method encoding. This is prepended by a
|
||||
'+' if the argument is passed in registers.
|
||||
*/
|
||||
inline const char*
|
||||
objc_skip_offset (const char* type)
|
||||
{
|
||||
if (*type == '+') type++;
|
||||
while(isdigit(*++type));
|
||||
return type;
|
||||
}
|
||||
|
||||
/*
|
||||
Skip an argument specification of a method encoding.
|
||||
*/
|
||||
const char*
|
||||
objc_skip_argspec (const char* type)
|
||||
{
|
||||
type = objc_skip_typespec (type);
|
||||
type = objc_skip_offset (type);
|
||||
return type;
|
||||
}
|
||||
|
||||
/*
|
||||
Return the number of arguments that the method MTH expects.
|
||||
Note that all methods need two implicit arguments `self' and
|
||||
`_cmd'.
|
||||
*/
|
||||
int
|
||||
method_get_number_of_arguments (struct objc_method* mth)
|
||||
{
|
||||
int i = 0;
|
||||
const char* type = mth->method_types;
|
||||
while (*type)
|
||||
{
|
||||
type = objc_skip_argspec (type);
|
||||
i += 1;
|
||||
}
|
||||
return i - 1;
|
||||
}
|
||||
|
||||
/*
|
||||
Return the size of the argument block needed on the stack to invoke
|
||||
the method MTH. This may be zero, if all arguments are passed in
|
||||
registers.
|
||||
*/
|
||||
|
||||
int
|
||||
method_get_sizeof_arguments (struct objc_method* mth)
|
||||
{
|
||||
const char* type = objc_skip_typespec (mth->method_types);
|
||||
return atoi (type);
|
||||
}
|
||||
|
||||
/*
|
||||
Return a pointer to the next argument of ARGFRAME. type points to
|
||||
the last argument. Typical use of this look like:
|
||||
|
||||
{
|
||||
char *datum, *type;
|
||||
for (datum = method_get_first_argument (method, argframe, &type);
|
||||
datum; datum = method_get_next_argument (argframe, &type))
|
||||
{
|
||||
unsigned flags = objc_get_type_qualifiers (type);
|
||||
type = objc_skip_type_qualifiers (type);
|
||||
if (*type != _C_PTR)
|
||||
[portal encodeData: datum ofType: type];
|
||||
else
|
||||
{
|
||||
if ((flags & _F_IN) == _F_IN)
|
||||
[portal encodeData: *(char**)datum ofType: ++type];
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
char*
|
||||
method_get_next_argument (arglist_t argframe,
|
||||
const char **type)
|
||||
{
|
||||
const char *t = objc_skip_argspec (*type);
|
||||
|
||||
if (*t == '\0')
|
||||
return 0;
|
||||
|
||||
*type = t;
|
||||
t = objc_skip_typespec (t);
|
||||
|
||||
if (*t == '+')
|
||||
return argframe->arg_regs + atoi (++t);
|
||||
else
|
||||
return argframe->arg_ptr + atoi (t);
|
||||
}
|
||||
|
||||
/*
|
||||
Return a pointer to the value of the first argument of the method
|
||||
described in M with the given argumentframe ARGFRAME. The type
|
||||
is returned in TYPE. type must be passed to successive calls of
|
||||
method_get_next_argument.
|
||||
*/
|
||||
char*
|
||||
method_get_first_argument (struct objc_method* m,
|
||||
arglist_t argframe,
|
||||
const char** type)
|
||||
{
|
||||
*type = m->method_types;
|
||||
return method_get_next_argument (argframe, type);
|
||||
}
|
||||
|
||||
/*
|
||||
Return a pointer to the ARGth argument of the method
|
||||
M from the frame ARGFRAME. The type of the argument
|
||||
is returned in the value-result argument TYPE
|
||||
*/
|
||||
|
||||
char*
|
||||
method_get_nth_argument (struct objc_method* m,
|
||||
arglist_t argframe, int arg,
|
||||
const char **type)
|
||||
{
|
||||
const char* t = objc_skip_argspec (m->method_types);
|
||||
|
||||
if (arg > method_get_number_of_arguments (m))
|
||||
return 0;
|
||||
|
||||
while (arg--)
|
||||
t = objc_skip_argspec (t);
|
||||
|
||||
*type = t;
|
||||
t = objc_skip_typespec (t);
|
||||
|
||||
if (*t == '+')
|
||||
return argframe->arg_regs + atoi (++t);
|
||||
else
|
||||
return argframe->arg_ptr + atoi (t);
|
||||
}
|
||||
|
||||
unsigned
|
||||
objc_get_type_qualifiers (const char* type)
|
||||
{
|
||||
unsigned res = 0;
|
||||
BOOL flag = YES;
|
||||
|
||||
while (flag)
|
||||
switch (*type++)
|
||||
{
|
||||
case _C_CONST: res |= _F_CONST; break;
|
||||
case _C_IN: res |= _F_IN; break;
|
||||
case _C_INOUT: res |= _F_INOUT; break;
|
||||
case _C_OUT: res |= _F_OUT; break;
|
||||
case _C_BYCOPY: res |= _F_BYCOPY; break;
|
||||
case _C_ONEWAY: res |= _F_ONEWAY; break;
|
||||
case _C_GCINVISIBLE: res |= _F_GCINVISIBLE; break;
|
||||
default: flag = NO;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* The following three functions can be used to determine how a
|
||||
structure is laid out by the compiler. For example:
|
||||
|
||||
struct objc_struct_layout layout;
|
||||
int i;
|
||||
|
||||
objc_layout_structure (type, &layout);
|
||||
while (objc_layout_structure_next_member (&layout))
|
||||
{
|
||||
int position, align;
|
||||
const char *type;
|
||||
|
||||
objc_layout_structure_get_info (&layout, &position, &align, &type);
|
||||
printf ("element %d has offset %d, alignment %d\n",
|
||||
i++, position, align);
|
||||
}
|
||||
|
||||
These functions are used by objc_sizeof_type and objc_alignof_type
|
||||
functions to compute the size and alignment of structures. The
|
||||
previous method of computing the size and alignment of a structure
|
||||
was not working on some architectures, particulary on AIX, and in
|
||||
the presence of bitfields inside the structure. */
|
||||
void
|
||||
objc_layout_structure (const char *type,
|
||||
struct objc_struct_layout *layout)
|
||||
{
|
||||
const char *ntype;
|
||||
|
||||
if (*type++ != _C_STRUCT_B)
|
||||
{
|
||||
objc_error(nil, OBJC_ERR_BAD_TYPE,
|
||||
"record type expected in objc_layout_structure, got %s\n",
|
||||
type);
|
||||
}
|
||||
|
||||
layout->original_type = type;
|
||||
|
||||
/* Skip "<name>=" if any. Avoid embedded structures and unions. */
|
||||
ntype = type;
|
||||
while (*ntype != _C_STRUCT_E && *ntype != _C_STRUCT_B && *ntype != _C_UNION_B
|
||||
&& *ntype++ != '=')
|
||||
/* do nothing */;
|
||||
|
||||
/* If there's a "<name>=", ntype - 1 points to '='; skip the the name */
|
||||
if (*(ntype - 1) == '=')
|
||||
type = ntype;
|
||||
|
||||
layout->type = type;
|
||||
layout->prev_type = NULL;
|
||||
layout->record_size = 0;
|
||||
layout->record_align = BITS_PER_UNIT;
|
||||
|
||||
#ifdef STRUCTURE_SIZE_BOUNDARY
|
||||
layout->record_align = MAX (layout->record_align, STRUCTURE_SIZE_BOUNDARY);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
BOOL
|
||||
objc_layout_structure_next_member (struct objc_struct_layout *layout)
|
||||
{
|
||||
register int known_align = layout->record_size;
|
||||
register int desired_align = 0;
|
||||
|
||||
/* The following are used only if the field is a bitfield */
|
||||
register const char *bfld_type;
|
||||
register int bfld_type_size, bfld_type_align, bfld_field_size;
|
||||
|
||||
/* The current type without the type qualifiers */
|
||||
const char *type;
|
||||
|
||||
#if 1
|
||||
if (layout->prev_type == NULL)
|
||||
{
|
||||
layout->prev_type = layout->type;
|
||||
layout->type = objc_skip_typespec (layout->prev_type);
|
||||
return YES;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Add the size of the previous field to the size of the record. */
|
||||
if (layout->prev_type)
|
||||
{
|
||||
type = objc_skip_type_qualifiers (layout->prev_type);
|
||||
|
||||
if (*type != _C_BFLD)
|
||||
layout->record_size += objc_sizeof_type (type) * BITS_PER_UNIT;
|
||||
else {
|
||||
desired_align = 1;
|
||||
/* Get the bitfield's type */
|
||||
for (bfld_type = type + 1;
|
||||
isdigit(*bfld_type);
|
||||
bfld_type++)
|
||||
/* do nothing */;
|
||||
|
||||
bfld_type_size = objc_sizeof_type (bfld_type) * BITS_PER_UNIT;
|
||||
bfld_type_align = objc_alignof_type (bfld_type) * BITS_PER_UNIT;
|
||||
bfld_field_size = atoi (objc_skip_typespec (bfld_type));
|
||||
layout->record_size += bfld_field_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (*layout->type == _C_STRUCT_E)
|
||||
return NO;
|
||||
|
||||
/* Skip the variable name if any */
|
||||
if (*layout->type == '"')
|
||||
{
|
||||
for (layout->type++; *layout->type++ != '"';)
|
||||
/* do nothing */;
|
||||
}
|
||||
|
||||
type = objc_skip_type_qualifiers (layout->type);
|
||||
|
||||
if (*type != _C_BFLD)
|
||||
desired_align = objc_alignof_type(type) * BITS_PER_UNIT;
|
||||
else
|
||||
{
|
||||
desired_align = 1;
|
||||
/* Skip the bitfield's offset */
|
||||
for (bfld_type = type + 1; isdigit(*bfld_type); bfld_type++)
|
||||
/* do nothing */;
|
||||
|
||||
bfld_type_size = objc_sizeof_type (bfld_type) * BITS_PER_UNIT;
|
||||
bfld_type_align = objc_alignof_type (bfld_type) * BITS_PER_UNIT;
|
||||
bfld_field_size = atoi (objc_skip_typespec (bfld_type));
|
||||
}
|
||||
|
||||
#ifdef BIGGEST_FIELD_ALIGNMENT
|
||||
desired_align = MIN (desired_align, BIGGEST_FIELD_ALIGNMENT);
|
||||
#endif
|
||||
#ifdef ADJUST_FIELD_ALIGN
|
||||
desired_align = ADJUST_FIELD_ALIGN (type, desired_align);
|
||||
#endif
|
||||
|
||||
/* Record must have at least as much alignment as any field.
|
||||
Otherwise, the alignment of the field within the record
|
||||
is meaningless. */
|
||||
#ifndef PCC_BITFIELD_TYPE_MATTERS
|
||||
layout->record_align = MAX (layout->record_align, desired_align);
|
||||
#else
|
||||
if (*type == _C_BFLD)
|
||||
{
|
||||
/* For these machines, a zero-length field does not
|
||||
affect the alignment of the structure as a whole.
|
||||
It does, however, affect the alignment of the next field
|
||||
within the structure. */
|
||||
if (bfld_field_size)
|
||||
layout->record_align = MAX (layout->record_align, desired_align);
|
||||
else
|
||||
desired_align = objc_alignof_type (bfld_type) * BITS_PER_UNIT;
|
||||
|
||||
/* A named bit field of declared type `int'
|
||||
forces the entire structure to have `int' alignment.
|
||||
Q1: How is encoded this thing and how to check for it?
|
||||
Q2: How to determine maximum_field_alignment at runtime? */
|
||||
|
||||
/* if (DECL_NAME (field) != 0) */
|
||||
{
|
||||
int type_align = bfld_type_align;
|
||||
#if 0
|
||||
if (maximum_field_alignment != 0)
|
||||
type_align = MIN (type_align, maximum_field_alignment);
|
||||
else if (DECL_PACKED (field))
|
||||
type_align = MIN (type_align, BITS_PER_UNIT);
|
||||
#endif
|
||||
|
||||
layout->record_align = MAX (layout->record_align, type_align);
|
||||
}
|
||||
}
|
||||
else
|
||||
layout->record_align = MAX (layout->record_align, desired_align);
|
||||
#endif
|
||||
|
||||
/* Does this field automatically have alignment it needs
|
||||
by virtue of the fields that precede it and the record's
|
||||
own alignment? */
|
||||
|
||||
if (*type == _C_BFLD)
|
||||
layout->record_size = atoi (type + 1);
|
||||
else if (layout->record_size % desired_align != 0)
|
||||
{
|
||||
/* No, we need to skip space before this field.
|
||||
Bump the cumulative size to multiple of field alignment. */
|
||||
layout->record_size = ROUND (layout->record_size, desired_align);
|
||||
}
|
||||
|
||||
/* Jump to the next field in record. */
|
||||
|
||||
layout->prev_type = layout->type;
|
||||
layout->type = objc_skip_typespec (layout->type); /* skip component */
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
void objc_layout_finish_structure (struct objc_struct_layout *layout,
|
||||
unsigned int *size,
|
||||
unsigned int *align)
|
||||
{
|
||||
if (layout->type && *layout->type == _C_STRUCT_E)
|
||||
{
|
||||
/* Work out the alignment of the record as one expression and store
|
||||
in the record type. Round it up to a multiple of the record's
|
||||
alignment. */
|
||||
|
||||
#ifdef ROUND_TYPE_ALIGN
|
||||
layout->record_align = ROUND_TYPE_ALIGN (layout->original_type,
|
||||
1,
|
||||
layout->record_align);
|
||||
#else
|
||||
layout->record_align = MAX (1, layout->record_align);
|
||||
#endif
|
||||
|
||||
#ifdef ROUND_TYPE_SIZE
|
||||
layout->record_size = ROUND_TYPE_SIZE (layout->original_type,
|
||||
layout->record_size,
|
||||
layout->record_align);
|
||||
#else
|
||||
/* Round the size up to be a multiple of the required alignment */
|
||||
layout->record_size = ROUND (layout->record_size, layout->record_align);
|
||||
#endif
|
||||
|
||||
layout->type = NULL;
|
||||
}
|
||||
if (size)
|
||||
*size = layout->record_size / BITS_PER_UNIT;
|
||||
if (align)
|
||||
*align = layout->record_align / BITS_PER_UNIT;
|
||||
}
|
||||
|
||||
|
||||
void objc_layout_structure_get_info (struct objc_struct_layout *layout,
|
||||
unsigned int *offset,
|
||||
unsigned int *align,
|
||||
const char **type)
|
||||
{
|
||||
if (offset)
|
||||
*offset = layout->record_size / BITS_PER_UNIT;
|
||||
if (align)
|
||||
*align = layout->record_align / BITS_PER_UNIT;
|
||||
if (type)
|
||||
*type = layout->prev_type;
|
||||
}
|
97
libobjc/encoding.h
Normal file
97
libobjc/encoding.h
Normal file
@ -0,0 +1,97 @@
|
||||
/* Encoding of types for Objective C.
|
||||
Copyright (C) 1993, 1997 Free Software Foundation, Inc.
|
||||
|
||||
Author: Kresten Krab Thorup
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files
|
||||
compiled with GCC to produce an executable, this does not cause
|
||||
the resulting executable to be covered by the GNU General Public License.
|
||||
This exception does not however invalidate any other reasons why
|
||||
the executable file might be covered by the GNU General Public License. */
|
||||
|
||||
#ifndef __encoding_INCLUDE_GNU
|
||||
#define __encoding_INCLUDE_GNU
|
||||
|
||||
#include <ctype.h>
|
||||
#include "objc/objc-api.h"
|
||||
|
||||
#define _C_CONST 'r'
|
||||
#define _C_IN 'n'
|
||||
#define _C_INOUT 'N'
|
||||
#define _C_OUT 'o'
|
||||
#define _C_BYCOPY 'O'
|
||||
#define _C_ONEWAY 'V'
|
||||
#define _C_GCINVISIBLE '!'
|
||||
|
||||
#define _F_CONST 0x01
|
||||
#define _F_IN 0x01
|
||||
#define _F_OUT 0x02
|
||||
#define _F_INOUT 0x03
|
||||
#define _F_BYCOPY 0x04
|
||||
#define _F_ONEWAY 0x08
|
||||
#define _F_GCINVISIBLE 0x10
|
||||
|
||||
int objc_aligned_size (const char* type);
|
||||
int objc_sizeof_type (const char* type);
|
||||
int objc_alignof_type (const char* type);
|
||||
int objc_aligned_size (const char* type);
|
||||
int objc_promoted_size (const char* type);
|
||||
|
||||
const char* objc_skip_type_qualifiers (const char* type);
|
||||
const char* objc_skip_typespec (const char* type);
|
||||
const char* objc_skip_offset (const char* type);
|
||||
const char* objc_skip_argspec (const char* type);
|
||||
int method_get_number_of_arguments (struct objc_method*);
|
||||
int method_get_sizeof_arguments (struct objc_method*);
|
||||
|
||||
char* method_get_first_argument (struct objc_method*,
|
||||
arglist_t argframe,
|
||||
const char** type);
|
||||
char* method_get_next_argument (arglist_t argframe,
|
||||
const char **type);
|
||||
char* method_get_nth_argument (struct objc_method* m,
|
||||
arglist_t argframe,
|
||||
int arg,
|
||||
const char **type);
|
||||
|
||||
unsigned objc_get_type_qualifiers (const char* type);
|
||||
|
||||
|
||||
struct objc_struct_layout
|
||||
{
|
||||
const char *original_type;
|
||||
const char *type;
|
||||
const char *prev_type;
|
||||
unsigned int record_size;
|
||||
unsigned int record_align;
|
||||
};
|
||||
|
||||
void objc_layout_structure (const char *type,
|
||||
struct objc_struct_layout *layout);
|
||||
BOOL objc_layout_structure_next_member (struct objc_struct_layout *layout);
|
||||
void objc_layout_finish_structure (struct objc_struct_layout *layout,
|
||||
unsigned int *size,
|
||||
unsigned int *align);
|
||||
void objc_layout_structure_get_info (struct objc_struct_layout *layout,
|
||||
unsigned int *offset,
|
||||
unsigned int *align,
|
||||
const char **type);
|
||||
|
||||
#endif /* __encoding_INCLUDE_GNU */
|
458
libobjc/gc.c
Normal file
458
libobjc/gc.c
Normal file
@ -0,0 +1,458 @@
|
||||
/* Basic data types for Objective C.
|
||||
Copyright (C) 1998 Free Software Foundation, Inc.
|
||||
Contributed by Ovidiu Predescu.
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files
|
||||
compiled with GCC to produce an executable, this does not cause
|
||||
the resulting executable to be covered by the GNU General Public License.
|
||||
This exception does not however invalidate any other reasons why
|
||||
the executable file might be covered by the GNU General Public License. */
|
||||
|
||||
#include "../tconfig.h"
|
||||
#include "objc.h"
|
||||
#include "encoding.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#if OBJC_WITH_GC
|
||||
|
||||
#include <gc.h>
|
||||
|
||||
/* gc_typed.h uses the following but doesn't declare them */
|
||||
typedef GC_word word;
|
||||
typedef GC_signed_word signed_word;
|
||||
|
||||
#if BITS_PER_WORD == 32
|
||||
# define LOGWL 5
|
||||
# define modWORDSZ(n) ((n) & 0x1f) /* n mod size of word */
|
||||
#endif
|
||||
|
||||
#if BITS_PER_WORD == 64
|
||||
# define LOGWL 6
|
||||
# define modWORDSZ(n) ((n) & 0x3f) /* n mod size of word */
|
||||
#endif
|
||||
|
||||
#define divWORDSZ(n) ((n) >> LOGWL) /* divide n by size of word */
|
||||
|
||||
#include <gc_typed.h>
|
||||
|
||||
/* The following functions set up in `mask` the corresponding pointers.
|
||||
The offset is incremented with the size of the type. */
|
||||
|
||||
#define ROUND(V, A) \
|
||||
({ typeof(V) __v=(V); typeof(A) __a=(A); \
|
||||
__a*((__v+__a-1)/__a); })
|
||||
|
||||
#define SET_BIT_FOR_OFFSET(mask, offset) \
|
||||
GC_set_bit(mask, offset / sizeof (void*))
|
||||
|
||||
/* Some prototypes */
|
||||
static void
|
||||
__objc_gc_setup_struct (GC_bitmap mask, const char *type, int offset);
|
||||
static void
|
||||
__objc_gc_setup_union (GC_bitmap mask, const char *type, int offset);
|
||||
|
||||
|
||||
static void
|
||||
__objc_gc_setup_array (GC_bitmap mask, const char *type, int offset)
|
||||
{
|
||||
int i, len = atoi(type + 1);
|
||||
|
||||
while (isdigit(*++type))
|
||||
/* do nothing */; /* skip the size of the array */
|
||||
|
||||
switch (*type) {
|
||||
case _C_ARY_B:
|
||||
for (i = 0; i < len; i++)
|
||||
__objc_gc_setup_array (mask, type, offset);
|
||||
break;
|
||||
|
||||
case _C_STRUCT_B:
|
||||
for (i = 0; i < len; i++)
|
||||
__objc_gc_setup_struct (mask, type, offset);
|
||||
break;
|
||||
|
||||
case _C_UNION_B:
|
||||
for (i = 0; i < len; i++)
|
||||
__objc_gc_setup_union (mask, type, offset);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
__objc_gc_setup_struct (GC_bitmap mask, const char *type, int offset)
|
||||
{
|
||||
struct objc_struct_layout layout;
|
||||
unsigned int position;
|
||||
const char *mtype;
|
||||
|
||||
objc_layout_structure (type, &layout);
|
||||
|
||||
while (objc_layout_structure_next_member (&layout))
|
||||
{
|
||||
BOOL gc_invisible = NO;
|
||||
|
||||
objc_layout_structure_get_info (&layout, &position, NULL, &mtype);
|
||||
|
||||
/* Skip the variable name */
|
||||
if (*mtype == '"')
|
||||
{
|
||||
for (mtype++; *mtype++ != '"';)
|
||||
/* do nothing */;
|
||||
}
|
||||
|
||||
if (*mtype == _C_GCINVISIBLE)
|
||||
{
|
||||
gc_invisible = YES;
|
||||
mtype++;
|
||||
}
|
||||
|
||||
/* Add to position the offset of this structure */
|
||||
position += offset;
|
||||
|
||||
switch (*mtype) {
|
||||
case _C_ID:
|
||||
case _C_CLASS:
|
||||
case _C_SEL:
|
||||
case _C_PTR:
|
||||
case _C_CHARPTR:
|
||||
case _C_ATOM:
|
||||
if (!gc_invisible)
|
||||
SET_BIT_FOR_OFFSET(mask, position);
|
||||
break;
|
||||
|
||||
case _C_ARY_B:
|
||||
__objc_gc_setup_array (mask, mtype, position);
|
||||
break;
|
||||
|
||||
case _C_STRUCT_B:
|
||||
__objc_gc_setup_struct (mask, mtype, position);
|
||||
break;
|
||||
|
||||
case _C_UNION_B:
|
||||
__objc_gc_setup_union (mask, mtype, position);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
__objc_gc_setup_union (GC_bitmap mask, const char *type, int offset)
|
||||
{
|
||||
/* Sub-optimal, quick implementation: assume the union is made of
|
||||
pointers, set up the mask accordingly. */
|
||||
|
||||
int i, size, align;
|
||||
|
||||
/* Skip the variable name */
|
||||
if (*type == '"')
|
||||
{
|
||||
for (type++; *type++ != '"';)
|
||||
/* do nothing */;
|
||||
}
|
||||
|
||||
size = objc_sizeof_type (type);
|
||||
align = objc_alignof_type (type);
|
||||
|
||||
offset = ROUND(offset, align);
|
||||
for (i = 0; i < size; i += sizeof (void*))
|
||||
{
|
||||
SET_BIT_FOR_OFFSET(mask, offset);
|
||||
offset += sizeof (void*);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Iterates over the types in the structure that represents the class
|
||||
encoding and sets the bits in mask according to each ivar type. */
|
||||
static void
|
||||
__objc_gc_type_description_from_type (GC_bitmap mask, const char *type)
|
||||
{
|
||||
struct objc_struct_layout layout;
|
||||
unsigned int offset, align;
|
||||
const char *ivar_type;
|
||||
|
||||
objc_layout_structure (type, &layout);
|
||||
|
||||
while (objc_layout_structure_next_member (&layout))
|
||||
{
|
||||
BOOL gc_invisible = NO;
|
||||
|
||||
objc_layout_structure_get_info (&layout, &offset, &align, &ivar_type);
|
||||
|
||||
/* Skip the variable name */
|
||||
if (*ivar_type == '"')
|
||||
{
|
||||
for (ivar_type++; *ivar_type++ != '"';)
|
||||
/* do nothing */;
|
||||
}
|
||||
|
||||
if (*ivar_type == _C_GCINVISIBLE)
|
||||
{
|
||||
gc_invisible = YES;
|
||||
ivar_type++;
|
||||
}
|
||||
|
||||
switch (*ivar_type) {
|
||||
case _C_ID:
|
||||
case _C_CLASS:
|
||||
case _C_SEL:
|
||||
case _C_PTR:
|
||||
case _C_CHARPTR:
|
||||
if (!gc_invisible)
|
||||
SET_BIT_FOR_OFFSET(mask, offset);
|
||||
break;
|
||||
|
||||
case _C_ARY_B:
|
||||
__objc_gc_setup_array (mask, ivar_type, offset);
|
||||
break;
|
||||
|
||||
case _C_STRUCT_B:
|
||||
__objc_gc_setup_struct (mask, ivar_type, offset);
|
||||
break;
|
||||
|
||||
case _C_UNION_B:
|
||||
__objc_gc_setup_union (mask, ivar_type, offset);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Computes in *type the full type encoding of this class including
|
||||
its super classes. '*size' gives the total number of bytes allocated
|
||||
into *type, '*current' the number of bytes used so far by the
|
||||
encoding. */
|
||||
static void
|
||||
__objc_class_structure_encoding (Class class, char **type, int *size,
|
||||
int *current)
|
||||
{
|
||||
int i, ivar_count;
|
||||
struct objc_ivar_list* ivars;
|
||||
|
||||
if (!class)
|
||||
{
|
||||
strcat (*type, "{");
|
||||
*current++;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Add the type encodings of the super classes */
|
||||
__objc_class_structure_encoding (class->super_class, type, size, current);
|
||||
|
||||
ivars = class->ivars;
|
||||
if (!ivars)
|
||||
return;
|
||||
|
||||
ivar_count = ivars->ivar_count;
|
||||
|
||||
for (i = 0; i < ivar_count; i++)
|
||||
{
|
||||
struct objc_ivar *ivar = &(ivars->ivar_list[i]);
|
||||
const char *ivar_type = ivar->ivar_type;
|
||||
int len = strlen (ivar_type);
|
||||
|
||||
if (*current + len + 1 >= *size)
|
||||
{
|
||||
/* Increase the size of the encoding string so that it
|
||||
contains this ivar's type. */
|
||||
*size = ROUND(*current + len + 1, 10);
|
||||
*type = objc_realloc (*type, *size);
|
||||
}
|
||||
strcat (*type + *current, ivar_type);
|
||||
*current += len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Allocates the memory that will hold the type description for class
|
||||
and calls the __objc_class_structure_encoding that generates this
|
||||
value. */
|
||||
void
|
||||
__objc_generate_gc_type_description (Class class)
|
||||
{
|
||||
GC_bitmap mask;
|
||||
int bits_no, size;
|
||||
int type_size = 10, current;
|
||||
char *class_structure_type;
|
||||
|
||||
if (!CLS_ISCLASS(class))
|
||||
return;
|
||||
|
||||
/* We have to create a mask in which each bit counts for a pointer member.
|
||||
We take into consideration all the non-pointer instance variables and we
|
||||
round them up to the alignment. */
|
||||
|
||||
/* The number of bits in the mask is the size of an instance in bytes divided
|
||||
by the size of a pointer. */
|
||||
bits_no = (ROUND(class_get_instance_size (class), sizeof(void*))
|
||||
/ sizeof (void*));
|
||||
size = ROUND(bits_no, BITS_PER_WORD) / BITS_PER_WORD;
|
||||
mask = objc_atomic_malloc (size * sizeof (int));
|
||||
memset (mask, 0, size * sizeof (int));
|
||||
|
||||
class_structure_type = objc_atomic_malloc (type_size);
|
||||
*class_structure_type = current = 0;
|
||||
__objc_class_structure_encoding (class, &class_structure_type,
|
||||
&type_size, ¤t);
|
||||
if (current + 1 == type_size)
|
||||
class_structure_type = objc_realloc (class_structure_type, ++type_size);
|
||||
strcat (class_structure_type + current, "}");
|
||||
// printf ("type description for '%s' is %s\n", class->name, class_structure_type);
|
||||
|
||||
__objc_gc_type_description_from_type (mask, class_structure_type);
|
||||
objc_free (class_structure_type);
|
||||
|
||||
#define DEBUG 1
|
||||
#ifdef DEBUG
|
||||
printf (" mask for '%s', type '%s' (bits %d, mask size %d) is:",
|
||||
class_structure_type, class->name, bits_no, size);
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < size; i++)
|
||||
printf (" %lx", mask[i]);
|
||||
}
|
||||
puts ("");
|
||||
#endif
|
||||
|
||||
class->gc_object_type = (void*)GC_make_descriptor (mask, bits_no);
|
||||
}
|
||||
|
||||
|
||||
/* Returns YES if type denotes a pointer type, NO otherwise */
|
||||
static inline BOOL
|
||||
__objc_ivar_pointer (const char *type)
|
||||
{
|
||||
type = objc_skip_type_qualifiers (type);
|
||||
|
||||
return (*type == _C_ID
|
||||
|| *type == _C_CLASS
|
||||
|| *type == _C_SEL
|
||||
|| *type == _C_PTR
|
||||
|| *type == _C_CHARPTR
|
||||
|| *type == _C_ATOM);
|
||||
}
|
||||
|
||||
|
||||
/* Mark the instance variable whose name is given by ivarname as a
|
||||
weak pointer (a pointer hidden to the garbage collector) if
|
||||
gc_invisible is true. If gc_invisible is false it unmarks the
|
||||
instance variable and makes it a normal pointer, visible to the
|
||||
garbage collector.
|
||||
|
||||
This operation only makes sense on instance variables that are
|
||||
pointers. */
|
||||
void
|
||||
class_ivar_set_gcinvisible (Class class, const char* ivarname,
|
||||
BOOL gc_invisible)
|
||||
{
|
||||
int i, ivar_count;
|
||||
struct objc_ivar_list* ivars;
|
||||
|
||||
if (!class || !ivarname)
|
||||
return;
|
||||
|
||||
ivars = class->ivars;
|
||||
if (!ivars)
|
||||
return;
|
||||
|
||||
ivar_count = ivars->ivar_count;
|
||||
|
||||
for (i = 0; i < ivar_count; i++)
|
||||
{
|
||||
struct objc_ivar *ivar = &(ivars->ivar_list[i]);
|
||||
const char *type;
|
||||
|
||||
if (!ivar->ivar_name || strcmp (ivar->ivar_name, ivarname))
|
||||
continue;
|
||||
|
||||
assert (ivar->ivar_type);
|
||||
type = ivar->ivar_type;
|
||||
|
||||
/* Skip the variable name */
|
||||
if (*type == '"')
|
||||
{
|
||||
for (type++; *type++ != '"';)
|
||||
/* do nothing */;
|
||||
}
|
||||
|
||||
if (*type == _C_GCINVISIBLE)
|
||||
{
|
||||
char *new_type;
|
||||
|
||||
if (gc_invisible || !__objc_ivar_pointer (type))
|
||||
return; /* The type of the variable already matches the
|
||||
requested gc_invisible type */
|
||||
|
||||
/* The variable is gc_invisible and we have to reverse it */
|
||||
new_type = objc_atomic_malloc (strlen (ivar->ivar_type));
|
||||
strncpy (new_type, ivar->ivar_type,
|
||||
(size_t)(type - ivar->ivar_type));
|
||||
strcat (new_type, type + 1);
|
||||
ivar->ivar_type = new_type;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *new_type;
|
||||
|
||||
if (!gc_invisible || !__objc_ivar_pointer (type))
|
||||
return; /* The type of the variable already matches the
|
||||
requested gc_invisible type */
|
||||
|
||||
/* The variable is gc visible and we have to make it gc_invisible */
|
||||
new_type = objc_malloc (strlen (ivar->ivar_type) + 2);
|
||||
strncpy (new_type, ivar->ivar_type,
|
||||
(size_t)(type - ivar->ivar_type));
|
||||
strcat (new_type, "!");
|
||||
strcat (new_type, type);
|
||||
ivar->ivar_type = new_type;
|
||||
}
|
||||
|
||||
__objc_generate_gc_type_description (class);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Search the instance variable in the superclasses */
|
||||
class_ivar_set_gcinvisible (class->super_class, ivarname, gc_invisible);
|
||||
}
|
||||
|
||||
#else /* !OBJC_WITH_GC */
|
||||
|
||||
void
|
||||
__objc_generate_gc_type_description (Class class)
|
||||
{
|
||||
}
|
||||
|
||||
void class_ivar_set_gcinvisible (Class class,
|
||||
const char* ivarname,
|
||||
BOOL gc_invisible)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* OBJC_WITH_GC */
|
283
libobjc/hash.c
Normal file
283
libobjc/hash.c
Normal file
@ -0,0 +1,283 @@
|
||||
/* Hash tables for Objective C internal structures
|
||||
Copyright (C) 1993, 1996, 1997 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files
|
||||
compiled with GCC to produce an executable, this does not cause
|
||||
the resulting executable to be covered by the GNU General Public License.
|
||||
This exception does not however invalidate any other reasons why
|
||||
the executable file might be covered by the GNU General Public License. */
|
||||
|
||||
#include "assert.h"
|
||||
|
||||
#include "objc/hash.h"
|
||||
|
||||
#include "runtime.h" /* for DEBUG_PRINTF */
|
||||
|
||||
/* These two macros determine when a hash table is full and
|
||||
by how much it should be expanded respectively.
|
||||
|
||||
These equations are percentages. */
|
||||
#define FULLNESS(cache) \
|
||||
((((cache)->size * 75) / 100) <= (cache)->used)
|
||||
#define EXPANSION(cache) \
|
||||
((cache)->size * 2)
|
||||
|
||||
cache_ptr
|
||||
hash_new (unsigned int size, hash_func_type hash_func,
|
||||
compare_func_type compare_func)
|
||||
{
|
||||
cache_ptr cache;
|
||||
|
||||
/* Pass me a value greater than 0 and a power of 2. */
|
||||
assert (size);
|
||||
assert (!(size & (size - 1)));
|
||||
|
||||
/* Allocate the cache structure. calloc insures
|
||||
its initialization for default values. */
|
||||
cache = (cache_ptr) objc_calloc (1, sizeof (struct cache));
|
||||
assert (cache);
|
||||
|
||||
/* Allocate the array of buckets for the cache.
|
||||
calloc initializes all of the pointers to NULL. */
|
||||
cache->node_table
|
||||
= (node_ptr *) objc_calloc (size, sizeof (node_ptr));
|
||||
assert (cache->node_table);
|
||||
|
||||
cache->size = size;
|
||||
|
||||
/* This should work for all processor architectures? */
|
||||
cache->mask = (size - 1);
|
||||
|
||||
/* Store the hashing function so that codes can be computed. */
|
||||
cache->hash_func = hash_func;
|
||||
|
||||
/* Store the function that compares hash keys to
|
||||
determine if they are equal. */
|
||||
cache->compare_func = compare_func;
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
hash_delete (cache_ptr cache)
|
||||
{
|
||||
node_ptr node;
|
||||
node_ptr next_node;
|
||||
unsigned int i;
|
||||
|
||||
/* Purge all key/value pairs from the table. */
|
||||
/* Step through the nodes one by one and remove every node WITHOUT
|
||||
using hash_next. this makes hash_delete much more efficient. */
|
||||
for (i = 0;i < cache->size;i++) {
|
||||
if ((node = cache->node_table[i])) {
|
||||
/* an entry in the hash table has been found, now step through the
|
||||
nodes next in the list and free them. */
|
||||
while ((next_node = node->next)) {
|
||||
hash_remove (cache,node->key);
|
||||
node = next_node;
|
||||
}
|
||||
|
||||
hash_remove (cache,node->key);
|
||||
}
|
||||
}
|
||||
|
||||
/* Release the array of nodes and the cache itself. */
|
||||
objc_free(cache->node_table);
|
||||
objc_free(cache);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
hash_add (cache_ptr *cachep, const void *key, void *value)
|
||||
{
|
||||
size_t indx = (*(*cachep)->hash_func)(*cachep, key);
|
||||
node_ptr node = (node_ptr) objc_calloc (1, sizeof (struct cache_node));
|
||||
|
||||
|
||||
assert (node);
|
||||
|
||||
/* Initialize the new node. */
|
||||
node->key = key;
|
||||
node->value = value;
|
||||
node->next = (*cachep)->node_table[indx];
|
||||
|
||||
/* Debugging.
|
||||
Check the list for another key. */
|
||||
#ifdef DEBUG
|
||||
{ node_ptr node1 = (*cachep)->node_table[indx];
|
||||
|
||||
while (node1) {
|
||||
|
||||
assert (node1->key != key);
|
||||
node1 = node1->next;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Install the node as the first element on the list. */
|
||||
(*cachep)->node_table[indx] = node;
|
||||
|
||||
/* Bump the number of entries in the cache. */
|
||||
++(*cachep)->used;
|
||||
|
||||
/* Check the hash table's fullness. We're going
|
||||
to expand if it is above the fullness level. */
|
||||
if (FULLNESS (*cachep)) {
|
||||
|
||||
/* The hash table has reached its fullness level. Time to
|
||||
expand it.
|
||||
|
||||
I'm using a slow method here but is built on other
|
||||
primitive functions thereby increasing its
|
||||
correctness. */
|
||||
node_ptr node1 = NULL;
|
||||
cache_ptr new = hash_new (EXPANSION (*cachep),
|
||||
(*cachep)->hash_func,
|
||||
(*cachep)->compare_func);
|
||||
|
||||
DEBUG_PRINTF ("Expanding cache %#x from %d to %d\n",
|
||||
*cachep, (*cachep)->size, new->size);
|
||||
|
||||
/* Copy the nodes from the first hash table to the new one. */
|
||||
while ((node1 = hash_next (*cachep, node1)))
|
||||
hash_add (&new, node1->key, node1->value);
|
||||
|
||||
/* Trash the old cache. */
|
||||
hash_delete (*cachep);
|
||||
|
||||
/* Return a pointer to the new hash table. */
|
||||
*cachep = new;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
hash_remove (cache_ptr cache, const void *key)
|
||||
{
|
||||
size_t indx = (*cache->hash_func)(cache, key);
|
||||
node_ptr node = cache->node_table[indx];
|
||||
|
||||
|
||||
/* We assume there is an entry in the table. Error if it is not. */
|
||||
assert (node);
|
||||
|
||||
/* Special case. First element is the key/value pair to be removed. */
|
||||
if ((*cache->compare_func)(node->key, key)) {
|
||||
cache->node_table[indx] = node->next;
|
||||
objc_free(node);
|
||||
} else {
|
||||
|
||||
/* Otherwise, find the hash entry. */
|
||||
node_ptr prev = node;
|
||||
BOOL removed = NO;
|
||||
|
||||
do {
|
||||
|
||||
if ((*cache->compare_func)(node->key, key)) {
|
||||
prev->next = node->next, removed = YES;
|
||||
objc_free(node);
|
||||
} else
|
||||
prev = node, node = node->next;
|
||||
} while (!removed && node);
|
||||
assert (removed);
|
||||
}
|
||||
|
||||
/* Decrement the number of entries in the hash table. */
|
||||
--cache->used;
|
||||
}
|
||||
|
||||
|
||||
node_ptr
|
||||
hash_next (cache_ptr cache, node_ptr node)
|
||||
{
|
||||
/* If the scan is being started then reset the last node
|
||||
visitied pointer and bucket index. */
|
||||
if (!node)
|
||||
cache->last_bucket = 0;
|
||||
|
||||
/* If there is a node visited last then check for another
|
||||
entry in the same bucket; Otherwise step to the next bucket. */
|
||||
if (node) {
|
||||
if (node->next)
|
||||
/* There is a node which follows the last node
|
||||
returned. Step to that node and retun it. */
|
||||
return node->next;
|
||||
else
|
||||
++cache->last_bucket;
|
||||
}
|
||||
|
||||
/* If the list isn't exhausted then search the buckets for
|
||||
other nodes. */
|
||||
if (cache->last_bucket < cache->size) {
|
||||
/* Scan the remainder of the buckets looking for an entry
|
||||
at the head of the list. Return the first item found. */
|
||||
while (cache->last_bucket < cache->size)
|
||||
if (cache->node_table[cache->last_bucket])
|
||||
return cache->node_table[cache->last_bucket];
|
||||
else
|
||||
++cache->last_bucket;
|
||||
|
||||
/* No further nodes were found in the hash table. */
|
||||
return NULL;
|
||||
} else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Given KEY, return corresponding value for it in CACHE.
|
||||
Return NULL if the KEY is not recorded. */
|
||||
|
||||
void *
|
||||
hash_value_for_key (cache_ptr cache, const void *key)
|
||||
{
|
||||
node_ptr node = cache->node_table[(*cache->hash_func)(cache, key)];
|
||||
void *retval = NULL;
|
||||
|
||||
if (node)
|
||||
do {
|
||||
if ((*cache->compare_func)(node->key, key)) {
|
||||
retval = node->value;
|
||||
break;
|
||||
} else
|
||||
node = node->next;
|
||||
} while (!retval && node);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Given KEY, return YES if it exists in the CACHE.
|
||||
Return NO if it does not */
|
||||
|
||||
BOOL
|
||||
hash_is_key_in_hash (cache_ptr cache, const void *key)
|
||||
{
|
||||
node_ptr node = cache->node_table[(*cache->hash_func)(cache, key)];
|
||||
|
||||
if (node)
|
||||
do {
|
||||
if ((*cache->compare_func)(node->key, key))
|
||||
return YES;
|
||||
else
|
||||
node = node->next;
|
||||
} while (node);
|
||||
|
||||
return NO;
|
||||
}
|
206
libobjc/hash.h
Normal file
206
libobjc/hash.h
Normal file
@ -0,0 +1,206 @@
|
||||
/* Hash tables for Objective C method dispatch.
|
||||
Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files
|
||||
compiled with GCC to produce an executable, this does not cause
|
||||
the resulting executable to be covered by the GNU General Public License.
|
||||
This exception does not however invalidate any other reasons why
|
||||
the executable file might be covered by the GNU General Public License. */
|
||||
|
||||
|
||||
#ifndef __hash_INCLUDE_GNU
|
||||
#define __hash_INCLUDE_GNU
|
||||
|
||||
#include <stddef.h>
|
||||
#include <objc/objc.h>
|
||||
|
||||
/*
|
||||
* This data structure is used to hold items
|
||||
* stored in a hash table. Each node holds
|
||||
* a key/value pair.
|
||||
*
|
||||
* Items in the cache are really of type void *.
|
||||
*/
|
||||
typedef struct cache_node
|
||||
{
|
||||
struct cache_node *next; /* Pointer to next entry on the list.
|
||||
NULL indicates end of list. */
|
||||
const void *key; /* Key used to locate the value. Used
|
||||
to locate value when more than one
|
||||
key computes the same hash
|
||||
value. */
|
||||
void *value; /* Value stored for the key. */
|
||||
} *node_ptr;
|
||||
|
||||
|
||||
/*
|
||||
* This data type is the function that computes a hash code given a key.
|
||||
* Therefore, the key can be a pointer to anything and the function specific
|
||||
* to the key type.
|
||||
*
|
||||
* Unfortunately there is a mutual data structure reference problem with this
|
||||
* typedef. Therefore, to remove compiler warnings the functions passed to
|
||||
* hash_new will have to be casted to this type.
|
||||
*/
|
||||
typedef unsigned int (*hash_func_type)(void *, const void *);
|
||||
|
||||
/*
|
||||
* This data type is the function that compares two hash keys and returns an
|
||||
* integer greater than, equal to, or less than 0, according as the first
|
||||
* parameter is lexicographically greater than, equal to, or less than the
|
||||
* second.
|
||||
*/
|
||||
|
||||
typedef int (*compare_func_type)(const void *, const void *);
|
||||
|
||||
|
||||
/*
|
||||
* This data structure is the cache.
|
||||
*
|
||||
* It must be passed to all of the hashing routines
|
||||
* (except for new).
|
||||
*/
|
||||
typedef struct cache
|
||||
{
|
||||
/* Variables used to implement the hash itself. */
|
||||
node_ptr *node_table; /* Pointer to an array of hash nodes. */
|
||||
/* Variables used to track the size of the hash table so to determine
|
||||
when to resize it. */
|
||||
unsigned int size; /* Number of buckets allocated for the hash table
|
||||
(number of array entries allocated for
|
||||
"node_table"). Must be a power of two. */
|
||||
unsigned int used; /* Current number of entries in the hash table. */
|
||||
unsigned int mask; /* Precomputed mask. */
|
||||
|
||||
/* Variables used to implement indexing through the hash table. */
|
||||
|
||||
unsigned int last_bucket; /* Tracks which entry in the array where
|
||||
the last value was returned. */
|
||||
/* Function used to compute a hash code given a key.
|
||||
This function is specified when the hash table is created. */
|
||||
hash_func_type hash_func;
|
||||
/* Function used to compare two hash keys to see if they are equal. */
|
||||
compare_func_type compare_func;
|
||||
} *cache_ptr;
|
||||
|
||||
|
||||
/* Two important hash tables. */
|
||||
extern cache_ptr module_hash_table, class_hash_table;
|
||||
|
||||
/* Allocate and initialize a hash table. */
|
||||
|
||||
cache_ptr hash_new (unsigned int size,
|
||||
hash_func_type hash_func,
|
||||
compare_func_type compare_func);
|
||||
|
||||
/* Deallocate all of the hash nodes and the cache itself. */
|
||||
|
||||
void hash_delete (cache_ptr cache);
|
||||
|
||||
/* Add the key/value pair to the hash table. If the
|
||||
hash table reaches a level of fullness then it will be resized.
|
||||
|
||||
assert if the key is already in the hash. */
|
||||
|
||||
void hash_add (cache_ptr *cachep, const void *key, void *value);
|
||||
|
||||
/* Remove the key/value pair from the hash table.
|
||||
assert if the key isn't in the table. */
|
||||
|
||||
void hash_remove (cache_ptr cache, const void *key);
|
||||
|
||||
/* Used to index through the hash table. Start with NULL
|
||||
to get the first entry.
|
||||
|
||||
Successive calls pass the value returned previously.
|
||||
** Don't modify the hash during this operation ***
|
||||
|
||||
Cache nodes are returned such that key or value can
|
||||
be extracted. */
|
||||
|
||||
node_ptr hash_next (cache_ptr cache, node_ptr node);
|
||||
|
||||
/* Used to return a value from a hash table using a given key. */
|
||||
|
||||
void *hash_value_for_key (cache_ptr cache, const void *key);
|
||||
|
||||
/* Used to determine if the given key exists in the hash table */
|
||||
|
||||
BOOL hash_is_key_in_hash (cache_ptr cache, const void *key);
|
||||
|
||||
/************************************************
|
||||
|
||||
Useful hashing functions.
|
||||
|
||||
Declared inline for your pleasure.
|
||||
|
||||
************************************************/
|
||||
|
||||
/* Calculate a hash code by performing some
|
||||
manipulation of the key pointer. (Use the lowest bits
|
||||
except for those likely to be 0 due to alignment.) */
|
||||
|
||||
static inline unsigned int
|
||||
hash_ptr (cache_ptr cache, const void *key)
|
||||
{
|
||||
return ((size_t)key / sizeof (void *)) & cache->mask;
|
||||
}
|
||||
|
||||
|
||||
/* Calculate a hash code by iterating over a NULL
|
||||
terminate string. */
|
||||
static inline unsigned int
|
||||
hash_string (cache_ptr cache, const void *key)
|
||||
{
|
||||
unsigned int ret = 0;
|
||||
unsigned int ctr = 0;
|
||||
|
||||
|
||||
while (*(char*)key) {
|
||||
ret ^= *(char*)key++ << ctr;
|
||||
ctr = (ctr + 1) % sizeof (void *);
|
||||
}
|
||||
|
||||
return ret & cache->mask;
|
||||
}
|
||||
|
||||
|
||||
/* Compare two pointers for equality. */
|
||||
static inline int
|
||||
compare_ptrs (const void *k1, const void *k2)
|
||||
{
|
||||
return !(k1 - k2);
|
||||
}
|
||||
|
||||
|
||||
/* Compare two strings. */
|
||||
static inline int
|
||||
compare_strings (const void *k1, const void *k2)
|
||||
{
|
||||
if (k1 == k2)
|
||||
return 1;
|
||||
else if (k1 == 0 || k2 == 0)
|
||||
return 0;
|
||||
else
|
||||
return !strcmp (k1, k2);
|
||||
}
|
||||
|
||||
|
||||
#endif /* not __hash_INCLUDE_GNU */
|
834
libobjc/init.c
Normal file
834
libobjc/init.c
Normal file
@ -0,0 +1,834 @@
|
||||
/* GNU Objective C Runtime initialization
|
||||
Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Kresten Krab Thorup
|
||||
+load support contributed by Ovidiu Predescu <ovidiu@net-community.com>
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include "runtime.h"
|
||||
|
||||
/* The version number of this runtime. This must match the number
|
||||
defined in gcc (objc-act.c) */
|
||||
#define OBJC_VERSION 8
|
||||
#define PROTOCOL_VERSION 2
|
||||
|
||||
/* This list contains all modules currently loaded into the runtime */
|
||||
static struct objc_list* __objc_module_list = 0; /* !T:MUTEX */
|
||||
|
||||
/* This list contains all proto_list's not yet assigned class links */
|
||||
static struct objc_list* unclaimed_proto_list = 0; /* !T:MUTEX */
|
||||
|
||||
/* List of unresolved static instances. */
|
||||
static struct objc_list *uninitialized_statics = 0; /* !T:MUTEX */
|
||||
|
||||
/* Global runtime "write" mutex. */
|
||||
objc_mutex_t __objc_runtime_mutex = 0;
|
||||
|
||||
/* Number of threads that are alive. */
|
||||
int __objc_runtime_threads_alive = 1; /* !T:MUTEX */
|
||||
|
||||
/* Check compiler vs runtime version */
|
||||
static void init_check_module_version (Module_t);
|
||||
|
||||
/* Assign isa links to protos */
|
||||
static void __objc_init_protocols (struct objc_protocol_list* protos);
|
||||
|
||||
/* Add protocol to class */
|
||||
static void __objc_class_add_protocols (Class, struct objc_protocol_list*);
|
||||
|
||||
/* This is a hook which is called by __objc_exec_class every time a class
|
||||
or a category is loaded into the runtime. This may e.g. help a
|
||||
dynamic loader determine the classes that have been loaded when
|
||||
an object file is dynamically linked in */
|
||||
void (*_objc_load_callback)(Class class, Category* category); /* !T:SAFE */
|
||||
|
||||
/* Is all categories/classes resolved? */
|
||||
BOOL __objc_dangling_categories = NO; /* !T:UNUSED */
|
||||
|
||||
extern SEL
|
||||
__sel_register_typed_name (const char *name, const char *types,
|
||||
struct objc_selector *orig, BOOL is_const);
|
||||
|
||||
/* Sends +load to all classes and categories in certain situations. */
|
||||
static void objc_send_load (void);
|
||||
|
||||
/* Inserts all the classes defined in module in a tree of classes that
|
||||
resembles the class hierarchy. This tree is traversed in preorder and the
|
||||
classes in its nodes receive the +load message if these methods were not
|
||||
executed before. The algorithm ensures that when the +load method of a class
|
||||
is executed all the superclasses have been already received the +load
|
||||
message. */
|
||||
static void __objc_create_classes_tree (Module_t module);
|
||||
|
||||
static void __objc_call_callback (Module_t module);
|
||||
|
||||
/* A special version that works only before the classes are completely
|
||||
installed in the runtime. */
|
||||
static BOOL class_is_subclass_of_class (Class class, Class superclass);
|
||||
|
||||
typedef struct objc_class_tree {
|
||||
Class class;
|
||||
struct objc_list *subclasses; /* `head' is pointer to an objc_class_tree */
|
||||
} objc_class_tree;
|
||||
|
||||
/* This is a linked list of objc_class_tree trees. The head of these trees
|
||||
are root classes (their super class is Nil). These different trees
|
||||
represent different class hierarchies. */
|
||||
static struct objc_list *__objc_class_tree_list = NULL;
|
||||
|
||||
/* Keeps the +load methods who have been already executed. This hash should
|
||||
not be destroyed during the execution of the program. */
|
||||
static cache_ptr __objc_load_methods = NULL;
|
||||
|
||||
/* Creates a tree of classes whose topmost class is directly inherited from
|
||||
`upper' and the bottom class in this tree is `bottom_class'. The classes
|
||||
in this tree are super classes of `bottom_class'. `subclasses' member
|
||||
of each tree node point to the next subclass tree node. */
|
||||
static objc_class_tree *
|
||||
create_tree_of_subclasses_inherited_from (Class bottom_class, Class upper)
|
||||
{
|
||||
Class superclass = bottom_class->super_class ?
|
||||
objc_lookup_class ((char*)bottom_class->super_class)
|
||||
: Nil;
|
||||
|
||||
objc_class_tree *tree, *prev;
|
||||
|
||||
DEBUG_PRINTF ("create_tree_of_subclasses_inherited_from:");
|
||||
DEBUG_PRINTF ("bottom_class = %s, upper = %s\n",
|
||||
(bottom_class ? bottom_class->name : NULL),
|
||||
(upper ? upper->name : NULL));
|
||||
|
||||
tree = prev = objc_calloc (1, sizeof (objc_class_tree));
|
||||
prev->class = bottom_class;
|
||||
|
||||
while (superclass != upper)
|
||||
{
|
||||
tree = objc_calloc (1, sizeof (objc_class_tree));
|
||||
tree->class = superclass;
|
||||
tree->subclasses = list_cons (prev, tree->subclasses);
|
||||
superclass = (superclass->super_class ?
|
||||
objc_lookup_class ((char*)superclass->super_class)
|
||||
: Nil);
|
||||
prev = tree;
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
/* Insert the `class' into the proper place in the `tree' class hierarchy. This
|
||||
function returns a new tree if the class has been successfully inserted into
|
||||
the tree or NULL if the class is not part of the classes hierarchy described
|
||||
by `tree'. This function is private to objc_tree_insert_class(), you should
|
||||
not call it directly. */
|
||||
static objc_class_tree *
|
||||
__objc_tree_insert_class (objc_class_tree *tree, Class class)
|
||||
{
|
||||
DEBUG_PRINTF ("__objc_tree_insert_class: tree = %x, class = %s\n",
|
||||
tree, class->name);
|
||||
|
||||
if (tree == NULL)
|
||||
return create_tree_of_subclasses_inherited_from (class, NULL);
|
||||
else if (class == tree->class)
|
||||
{
|
||||
/* `class' has been already inserted */
|
||||
DEBUG_PRINTF ("1. class %s was previously inserted\n", class->name);
|
||||
return tree;
|
||||
}
|
||||
else if ((class->super_class ?
|
||||
objc_lookup_class ((char*)class->super_class)
|
||||
: Nil)
|
||||
== tree->class)
|
||||
{
|
||||
/* If class is a direct subclass of tree->class then add class to the
|
||||
list of subclasses. First check to see if it wasn't already
|
||||
inserted. */
|
||||
struct objc_list *list = tree->subclasses;
|
||||
objc_class_tree *node;
|
||||
|
||||
while (list)
|
||||
{
|
||||
/* Class has been already inserted; do nothing just return
|
||||
the tree. */
|
||||
if (((objc_class_tree*)list->head)->class == class)
|
||||
{
|
||||
DEBUG_PRINTF ("2. class %s was previously inserted\n",
|
||||
class->name);
|
||||
return tree;
|
||||
}
|
||||
list = list->tail;
|
||||
}
|
||||
|
||||
/* Create a new node class and insert it into the list of subclasses */
|
||||
node = objc_calloc (1, sizeof (objc_class_tree));
|
||||
node->class = class;
|
||||
tree->subclasses = list_cons (node, tree->subclasses);
|
||||
DEBUG_PRINTF ("3. class %s inserted\n", class->name);
|
||||
return tree;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The class is not a direct subclass of tree->class. Search for class's
|
||||
superclasses in the list of subclasses. */
|
||||
struct objc_list *subclasses = tree->subclasses;
|
||||
|
||||
/* Precondition: the class must be a subclass of tree->class; otherwise
|
||||
return NULL to indicate our caller that it must take the next tree. */
|
||||
if (!class_is_subclass_of_class (class, tree->class))
|
||||
return NULL;
|
||||
|
||||
for (; subclasses != NULL; subclasses = subclasses->tail)
|
||||
{
|
||||
Class aClass = ((objc_class_tree*)(subclasses->head))->class;
|
||||
|
||||
if (class_is_subclass_of_class (class, aClass))
|
||||
{
|
||||
/* If we found one of class's superclasses we insert the class
|
||||
into its subtree and return the original tree since nothing
|
||||
has been changed. */
|
||||
subclasses->head
|
||||
= __objc_tree_insert_class (subclasses->head, class);
|
||||
DEBUG_PRINTF ("4. class %s inserted\n", class->name);
|
||||
return tree;
|
||||
}
|
||||
}
|
||||
|
||||
/* We haven't found a subclass of `class' in the `subclasses' list.
|
||||
Create a new tree of classes whose topmost class is a direct subclass
|
||||
of tree->class. */
|
||||
{
|
||||
objc_class_tree *new_tree
|
||||
= create_tree_of_subclasses_inherited_from (class, tree->class);
|
||||
tree->subclasses = list_cons (new_tree, tree->subclasses);
|
||||
DEBUG_PRINTF ("5. class %s inserted\n", class->name);
|
||||
return tree;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This function inserts `class' in the right tree hierarchy classes. */
|
||||
static void
|
||||
objc_tree_insert_class (Class class)
|
||||
{
|
||||
struct objc_list *list_node;
|
||||
objc_class_tree *tree;
|
||||
|
||||
list_node = __objc_class_tree_list;
|
||||
while (list_node)
|
||||
{
|
||||
tree = __objc_tree_insert_class (list_node->head, class);
|
||||
if (tree)
|
||||
{
|
||||
list_node->head = tree;
|
||||
break;
|
||||
}
|
||||
else
|
||||
list_node = list_node->tail;
|
||||
}
|
||||
|
||||
/* If the list was finished but the class hasn't been inserted, insert it
|
||||
here. */
|
||||
if (!list_node)
|
||||
{
|
||||
__objc_class_tree_list = list_cons (NULL, __objc_class_tree_list);
|
||||
__objc_class_tree_list->head = __objc_tree_insert_class (NULL, class);
|
||||
}
|
||||
}
|
||||
|
||||
/* Traverse tree in preorder. Used to send +load. */
|
||||
static void
|
||||
objc_preorder_traverse (objc_class_tree *tree,
|
||||
int level,
|
||||
void (*function)(objc_class_tree*, int))
|
||||
{
|
||||
struct objc_list *node;
|
||||
|
||||
(*function) (tree, level);
|
||||
for (node = tree->subclasses; node; node = node->tail)
|
||||
objc_preorder_traverse (node->head, level + 1, function);
|
||||
}
|
||||
|
||||
/* Traverse tree in postorder. Used to destroy a tree. */
|
||||
static void
|
||||
objc_postorder_traverse (objc_class_tree *tree,
|
||||
int level,
|
||||
void (*function)(objc_class_tree*, int))
|
||||
{
|
||||
struct objc_list *node;
|
||||
|
||||
for (node = tree->subclasses; node; node = node->tail)
|
||||
objc_postorder_traverse (node->head, level + 1, function);
|
||||
(*function) (tree, level);
|
||||
}
|
||||
|
||||
/* Used to print a tree class hierarchy. */
|
||||
#ifdef DEBUG
|
||||
static void
|
||||
__objc_tree_print (objc_class_tree *tree, int level)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < level; i++)
|
||||
printf (" ");
|
||||
printf ("%s\n", tree->class->name);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Walks on a linked list of methods in the reverse order and executes all
|
||||
the methods corresponding to `op' selector. Walking in the reverse order
|
||||
assures the +load of class is executed first and then +load of categories
|
||||
because of the way in which categories are added to the class methods. */
|
||||
static void
|
||||
__objc_send_message_in_list (MethodList_t method_list, Class class, SEL op)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!method_list)
|
||||
return;
|
||||
|
||||
/* First execute the `op' message in the following method lists */
|
||||
__objc_send_message_in_list (method_list->method_next, class, op);
|
||||
|
||||
/* Search the method list. */
|
||||
for (i = 0; i < method_list->method_count; i++)
|
||||
{
|
||||
Method_t mth = &method_list->method_list[i];
|
||||
|
||||
if (mth->method_name && sel_eq (mth->method_name, op)
|
||||
&& !hash_is_key_in_hash (__objc_load_methods, mth->method_name))
|
||||
{
|
||||
/* The method was found and wasn't previously executed. */
|
||||
(*mth->method_imp) ((id)class, mth->method_name);
|
||||
|
||||
/* Add this method into the +load hash table */
|
||||
hash_add (&__objc_load_methods, mth->method_imp, mth->method_imp);
|
||||
|
||||
DEBUG_PRINTF ("sending +load in class: %s\n", class->name);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
__objc_send_load (objc_class_tree *tree, int level)
|
||||
{
|
||||
static SEL load_sel = 0;
|
||||
Class class = tree->class;
|
||||
MethodList_t method_list = class->class_pointer->methods;
|
||||
|
||||
if (!load_sel)
|
||||
load_sel = sel_register_name ("load");
|
||||
|
||||
__objc_send_message_in_list (method_list, class, load_sel);
|
||||
}
|
||||
|
||||
static void
|
||||
__objc_destroy_class_tree_node (objc_class_tree *tree, int level)
|
||||
{
|
||||
objc_free (tree);
|
||||
}
|
||||
|
||||
/* This is used to check if the relationship between two classes before the
|
||||
runtime completely installs the classes. */
|
||||
static BOOL
|
||||
class_is_subclass_of_class (Class class, Class superclass)
|
||||
{
|
||||
for (; class != Nil;)
|
||||
{
|
||||
if (class == superclass)
|
||||
return YES;
|
||||
class = (class->super_class ?
|
||||
objc_lookup_class ((char*)class->super_class)
|
||||
: Nil);
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
/* This list contains all the classes in the runtime system for whom their
|
||||
superclasses are not yet know to the runtime. */
|
||||
static struct objc_list* unresolved_classes = 0;
|
||||
|
||||
/* Static function used to reference the Object and NXConstantString classes.
|
||||
*/
|
||||
static void
|
||||
__objc_force_linking (void)
|
||||
{
|
||||
extern void __objc_linking (void);
|
||||
__objc_linking ();
|
||||
|
||||
/* Call the function to avoid compiler warning */
|
||||
__objc_force_linking ();
|
||||
}
|
||||
|
||||
/* Run through the statics list, removing modules as soon as all its statics
|
||||
have been initialized. */
|
||||
static void
|
||||
objc_init_statics (void)
|
||||
{
|
||||
struct objc_list **cell = &uninitialized_statics;
|
||||
struct objc_static_instances **statics_in_module;
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
while (*cell)
|
||||
{
|
||||
int module_initialized = 1;
|
||||
|
||||
for (statics_in_module = (*cell)->head;
|
||||
*statics_in_module; statics_in_module++)
|
||||
{
|
||||
struct objc_static_instances *statics = *statics_in_module;
|
||||
Class class = objc_lookup_class (statics->class_name);
|
||||
|
||||
if (!class)
|
||||
module_initialized = 0;
|
||||
/* Actually, the static's class_pointer will be NULL when we
|
||||
haven't been here before. However, the comparison is to be
|
||||
reminded of taking into account class posing and to think about
|
||||
possible semantics... */
|
||||
else if (class != statics->instances[0]->class_pointer)
|
||||
{
|
||||
id *inst;
|
||||
|
||||
for (inst = &statics->instances[0]; *inst; inst++)
|
||||
{
|
||||
(*inst)->class_pointer = class;
|
||||
|
||||
/* ??? Make sure the object will not be freed. With
|
||||
refcounting, invoke `-retain'. Without refcounting, do
|
||||
nothing and hope that `-free' will never be invoked. */
|
||||
|
||||
/* ??? Send the object an `-initStatic' or something to
|
||||
that effect now or later on? What are the semantics of
|
||||
statically allocated instances, besides the trivial
|
||||
NXConstantString, anyway? */
|
||||
}
|
||||
}
|
||||
}
|
||||
if (module_initialized)
|
||||
{
|
||||
/* Remove this module from the uninitialized list. */
|
||||
struct objc_list *this = *cell;
|
||||
*cell = this->tail;
|
||||
objc_free(this);
|
||||
}
|
||||
else
|
||||
cell = &(*cell)->tail;
|
||||
}
|
||||
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
} /* objc_init_statics */
|
||||
|
||||
/* This function is called by constructor functions generated for each
|
||||
module compiled. (_GLOBAL_$I$...) The purpose of this function is to
|
||||
gather the module pointers so that they may be processed by the
|
||||
initialization routines as soon as possible */
|
||||
|
||||
void
|
||||
__objc_exec_class (Module_t module)
|
||||
{
|
||||
/* Have we processed any constructors previously? This flag is used to
|
||||
indicate that some global data structures need to be built. */
|
||||
static BOOL previous_constructors = 0;
|
||||
|
||||
static struct objc_list* unclaimed_categories = 0;
|
||||
|
||||
/* The symbol table (defined in objc-api.h) generated by gcc */
|
||||
Symtab_t symtab = module->symtab;
|
||||
|
||||
/* The statics in this module */
|
||||
struct objc_static_instances **statics
|
||||
= symtab->defs[symtab->cls_def_cnt + symtab->cat_def_cnt];
|
||||
|
||||
/* Entry used to traverse hash lists */
|
||||
struct objc_list** cell;
|
||||
|
||||
/* The table of selector references for this module */
|
||||
SEL selectors = symtab->refs;
|
||||
|
||||
/* dummy counter */
|
||||
int i;
|
||||
|
||||
DEBUG_PRINTF ("received module: %s\n", module->name);
|
||||
|
||||
/* check gcc version */
|
||||
init_check_module_version(module);
|
||||
|
||||
/* On the first call of this routine, initialize some data structures. */
|
||||
if (!previous_constructors)
|
||||
{
|
||||
/* Initialize thread-safe system */
|
||||
__objc_init_thread_system();
|
||||
__objc_runtime_threads_alive = 1;
|
||||
__objc_runtime_mutex = objc_mutex_allocate();
|
||||
|
||||
__objc_init_selector_tables();
|
||||
__objc_init_class_tables();
|
||||
__objc_init_dispatch_tables();
|
||||
__objc_class_tree_list = list_cons (NULL, __objc_class_tree_list);
|
||||
__objc_load_methods
|
||||
= hash_new (128, (hash_func_type)hash_ptr, compare_ptrs);
|
||||
previous_constructors = 1;
|
||||
}
|
||||
|
||||
/* Save the module pointer for later processing. (not currently used) */
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
__objc_module_list = list_cons(module, __objc_module_list);
|
||||
|
||||
/* Replace referenced selectors from names to SEL's. */
|
||||
if (selectors)
|
||||
{
|
||||
for (i = 0; selectors[i].sel_id; ++i)
|
||||
{
|
||||
const char *name, *type;
|
||||
name = (char*)selectors[i].sel_id;
|
||||
type = (char*)selectors[i].sel_types;
|
||||
/* Constructors are constant static data so we can safely store
|
||||
pointers to them in the runtime structures. is_const == YES */
|
||||
__sel_register_typed_name (name, type,
|
||||
(struct objc_selector*)&(selectors[i]),
|
||||
YES);
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse the classes in the load module and gather selector information. */
|
||||
DEBUG_PRINTF ("gathering selectors from module: %s\n", module->name);
|
||||
for (i = 0; i < symtab->cls_def_cnt; ++i)
|
||||
{
|
||||
Class class = (Class) symtab->defs[i];
|
||||
const char* superclass = (char*)class->super_class;
|
||||
|
||||
/* Make sure we have what we think. */
|
||||
assert (CLS_ISCLASS(class));
|
||||
assert (CLS_ISMETA(class->class_pointer));
|
||||
DEBUG_PRINTF ("phase 1, processing class: %s\n", class->name);
|
||||
|
||||
/* Initialize the subclass list to be NULL.
|
||||
In some cases it isn't and this crashes the program. */
|
||||
class->subclass_list = NULL;
|
||||
|
||||
/* Store the class in the class table and assign class numbers. */
|
||||
__objc_add_class_to_hash (class);
|
||||
|
||||
/* Register all of the selectors in the class and meta class. */
|
||||
__objc_register_selectors_from_class (class);
|
||||
__objc_register_selectors_from_class ((Class) class->class_pointer);
|
||||
|
||||
/* Install the fake dispatch tables */
|
||||
__objc_install_premature_dtable(class);
|
||||
__objc_install_premature_dtable(class->class_pointer);
|
||||
|
||||
/* Register the instance methods as class methods, this is
|
||||
only done for root classes. */
|
||||
__objc_register_instance_methods_to_class(class);
|
||||
|
||||
if (class->protocols)
|
||||
__objc_init_protocols (class->protocols);
|
||||
|
||||
/* Check to see if the superclass is known in this point. If it's not
|
||||
add the class to the unresolved_classes list. */
|
||||
if (superclass && !objc_lookup_class (superclass))
|
||||
unresolved_classes = list_cons (class, unresolved_classes);
|
||||
}
|
||||
|
||||
/* Process category information from the module. */
|
||||
for (i = 0; i < symtab->cat_def_cnt; ++i)
|
||||
{
|
||||
Category_t category = symtab->defs[i + symtab->cls_def_cnt];
|
||||
Class class = objc_lookup_class (category->class_name);
|
||||
|
||||
/* If the class for the category exists then append its methods. */
|
||||
if (class)
|
||||
{
|
||||
|
||||
DEBUG_PRINTF ("processing categories from (module,object): %s, %s\n",
|
||||
module->name,
|
||||
class->name);
|
||||
|
||||
/* Do instance methods. */
|
||||
if (category->instance_methods)
|
||||
class_add_method_list (class, category->instance_methods);
|
||||
|
||||
/* Do class methods. */
|
||||
if (category->class_methods)
|
||||
class_add_method_list ((Class) class->class_pointer,
|
||||
category->class_methods);
|
||||
|
||||
if (category->protocols)
|
||||
{
|
||||
__objc_init_protocols (category->protocols);
|
||||
__objc_class_add_protocols (class, category->protocols);
|
||||
}
|
||||
|
||||
/* Register the instance methods as class methods, this is
|
||||
only done for root classes. */
|
||||
__objc_register_instance_methods_to_class(class);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The object to which the category methods belong can't be found.
|
||||
Save the information. */
|
||||
unclaimed_categories = list_cons(category, unclaimed_categories);
|
||||
}
|
||||
}
|
||||
|
||||
if (statics)
|
||||
uninitialized_statics = list_cons (statics, uninitialized_statics);
|
||||
if (uninitialized_statics)
|
||||
objc_init_statics ();
|
||||
|
||||
/* Scan the unclaimed category hash. Attempt to attach any unclaimed
|
||||
categories to objects. */
|
||||
for (cell = &unclaimed_categories;
|
||||
*cell;
|
||||
({ if (*cell) cell = &(*cell)->tail; }))
|
||||
{
|
||||
Category_t category = (*cell)->head;
|
||||
Class class = objc_lookup_class (category->class_name);
|
||||
|
||||
if (class)
|
||||
{
|
||||
DEBUG_PRINTF ("attaching stored categories to object: %s\n",
|
||||
class->name);
|
||||
|
||||
list_remove_head (cell);
|
||||
|
||||
if (category->instance_methods)
|
||||
class_add_method_list (class, category->instance_methods);
|
||||
|
||||
if (category->class_methods)
|
||||
class_add_method_list ((Class) class->class_pointer,
|
||||
category->class_methods);
|
||||
|
||||
if (category->protocols)
|
||||
{
|
||||
__objc_init_protocols (category->protocols);
|
||||
__objc_class_add_protocols (class, category->protocols);
|
||||
}
|
||||
|
||||
/* Register the instance methods as class methods, this is
|
||||
only done for root classes. */
|
||||
__objc_register_instance_methods_to_class(class);
|
||||
}
|
||||
}
|
||||
|
||||
if (unclaimed_proto_list && objc_lookup_class ("Protocol"))
|
||||
{
|
||||
list_mapcar (unclaimed_proto_list,(void(*)(void*))__objc_init_protocols);
|
||||
list_free (unclaimed_proto_list);
|
||||
unclaimed_proto_list = 0;
|
||||
}
|
||||
|
||||
objc_send_load ();
|
||||
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
}
|
||||
|
||||
static void objc_send_load (void)
|
||||
{
|
||||
if (!__objc_module_list)
|
||||
return;
|
||||
|
||||
/* Try to find out if all the classes loaded so far also have their
|
||||
superclasses known to the runtime. We suppose that the objects that are
|
||||
allocated in the +load method are in general of a class declared in the
|
||||
same module. */
|
||||
if (unresolved_classes)
|
||||
{
|
||||
Class class = unresolved_classes->head;
|
||||
|
||||
while (objc_lookup_class ((char*)class->super_class))
|
||||
{
|
||||
list_remove_head (&unresolved_classes);
|
||||
if (unresolved_classes)
|
||||
class = unresolved_classes->head;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we still have classes for whom we don't have yet their super
|
||||
* classes known to the runtime we don't send the +load messages.
|
||||
*/
|
||||
if (unresolved_classes)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Special check to allow creating and sending messages to constant strings
|
||||
in +load methods. If these classes are not yet known, even if all the
|
||||
other classes are known, delay sending of +load. */
|
||||
if (!objc_lookup_class ("NXConstantString") ||
|
||||
!objc_lookup_class ("Object"))
|
||||
return;
|
||||
|
||||
/* Iterate over all modules in the __objc_module_list and call on them the
|
||||
__objc_create_classes_tree function. This function creates a tree of
|
||||
classes that resembles the class hierarchy. */
|
||||
list_mapcar (__objc_module_list, (void(*)(void*))__objc_create_classes_tree);
|
||||
|
||||
while (__objc_class_tree_list)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
objc_preorder_traverse (__objc_class_tree_list->head,
|
||||
0, __objc_tree_print);
|
||||
#endif
|
||||
objc_preorder_traverse (__objc_class_tree_list->head,
|
||||
0, __objc_send_load);
|
||||
objc_postorder_traverse (__objc_class_tree_list->head,
|
||||
0, __objc_destroy_class_tree_node);
|
||||
list_remove_head (&__objc_class_tree_list);
|
||||
}
|
||||
|
||||
list_mapcar (__objc_module_list, (void(*)(void*))__objc_call_callback);
|
||||
list_free (__objc_module_list);
|
||||
__objc_module_list = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
__objc_create_classes_tree (Module_t module)
|
||||
{
|
||||
/* The runtime mutex is locked in this point */
|
||||
|
||||
Symtab_t symtab = module->symtab;
|
||||
int i;
|
||||
|
||||
/* Iterate thru classes defined in this module and insert them in the classes
|
||||
tree hierarchy. */
|
||||
for (i = 0; i < symtab->cls_def_cnt; i++)
|
||||
{
|
||||
Class class = (Class) symtab->defs[i];
|
||||
|
||||
objc_tree_insert_class (class);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
__objc_call_callback (Module_t module)
|
||||
{
|
||||
/* The runtime mutex is locked in this point */
|
||||
|
||||
Symtab_t symtab = module->symtab;
|
||||
int i;
|
||||
|
||||
/* Iterate thru classes defined in this module and call the callback for
|
||||
each one. */
|
||||
for (i = 0; i < symtab->cls_def_cnt; i++)
|
||||
{
|
||||
Class class = (Class) symtab->defs[i];
|
||||
|
||||
/* Call the _objc_load_callback for this class. */
|
||||
if (_objc_load_callback)
|
||||
_objc_load_callback(class, 0);
|
||||
}
|
||||
|
||||
/* Call the _objc_load_callback for categories. Don't register the instance
|
||||
methods as class methods for categories to root classes since they were
|
||||
already added in the class. */
|
||||
for (i = 0; i < symtab->cat_def_cnt; i++)
|
||||
{
|
||||
Category_t category = symtab->defs[i + symtab->cls_def_cnt];
|
||||
Class class = objc_lookup_class (category->class_name);
|
||||
|
||||
if (_objc_load_callback)
|
||||
_objc_load_callback(class, category);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sanity check the version of gcc used to compile `module'*/
|
||||
static void init_check_module_version(Module_t module)
|
||||
{
|
||||
if ((module->version != OBJC_VERSION) || (module->size != sizeof (Module)))
|
||||
{
|
||||
int code;
|
||||
|
||||
if(module->version > OBJC_VERSION)
|
||||
code = OBJC_ERR_OBJC_VERSION;
|
||||
else if (module->version < OBJC_VERSION)
|
||||
code = OBJC_ERR_GCC_VERSION;
|
||||
else
|
||||
code = OBJC_ERR_MODULE_SIZE;
|
||||
|
||||
objc_error(nil, code, "Module %s version %d doesn't match runtime %d\n",
|
||||
module->name, (int)module->version, OBJC_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
__objc_init_protocols (struct objc_protocol_list* protos)
|
||||
{
|
||||
int i;
|
||||
static Class proto_class = 0;
|
||||
|
||||
if (! protos)
|
||||
return;
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
if (!proto_class)
|
||||
proto_class = objc_lookup_class("Protocol");
|
||||
|
||||
if (!proto_class)
|
||||
{
|
||||
unclaimed_proto_list = list_cons (protos, unclaimed_proto_list);
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
assert (protos->next == 0); /* only single ones allowed */
|
||||
#endif
|
||||
|
||||
for(i = 0; i < protos->count; i++)
|
||||
{
|
||||
struct objc_protocol* aProto = protos->list[i];
|
||||
if (((size_t)aProto->class_pointer) == PROTOCOL_VERSION)
|
||||
{
|
||||
/* assign class pointer */
|
||||
aProto->class_pointer = proto_class;
|
||||
|
||||
/* init super protocols */
|
||||
__objc_init_protocols (aProto->protocol_list);
|
||||
}
|
||||
else if (protos->list[i]->class_pointer != proto_class)
|
||||
{
|
||||
objc_error(nil, OBJC_ERR_PROTOCOL_VERSION,
|
||||
"Version %d doesn't match runtime protocol version %d\n",
|
||||
(int)((char*)protos->list[i]->class_pointer-(char*)0),
|
||||
PROTOCOL_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
}
|
||||
|
||||
static void __objc_class_add_protocols (Class class,
|
||||
struct objc_protocol_list* protos)
|
||||
{
|
||||
/* Well... */
|
||||
if (! protos)
|
||||
return;
|
||||
|
||||
/* Add it... */
|
||||
protos->next = class->protocols;
|
||||
class->protocols = protos;
|
||||
}
|
161
libobjc/libobjc.def
Normal file
161
libobjc/libobjc.def
Normal file
@ -0,0 +1,161 @@
|
||||
; GNU Objective C Runtime DLL Export Definitions
|
||||
; Copyright (C) 1997 Free Software Foundation, Inc.
|
||||
; Contributed by Scott Christley <scottc@net-community.com>
|
||||
;
|
||||
; This file is part of GNU CC.
|
||||
;
|
||||
; GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
; terms of the GNU General Public License as published by the Free Software
|
||||
; Foundation; either version 2, or (at your option) any later version.
|
||||
;
|
||||
; GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
; details.
|
||||
;
|
||||
; You should have received a copy of the GNU General Public License along with
|
||||
; GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
; Foundation, 59 Temple Place - Suite 330,
|
||||
; Boston, MA 02111-1307, USA.
|
||||
|
||||
LIBRARY libobjc
|
||||
EXPORTS
|
||||
search_for_method_in_list
|
||||
objc_get_uninstalled_dtable
|
||||
hash_is_key_in_hash
|
||||
objc_verror
|
||||
_objc_load_callback
|
||||
objc_malloc
|
||||
objc_atomic_malloc
|
||||
objc_valloc
|
||||
objc_realloc
|
||||
objc_calloc
|
||||
objc_free
|
||||
__objc_init_thread_system
|
||||
objc_mutex_allocate
|
||||
objc_mutex_deallocate
|
||||
objc_mutex_lock
|
||||
objc_mutex_trylock
|
||||
objc_mutex_unlock
|
||||
objc_thread_detach
|
||||
objc_thread_exit
|
||||
objc_thread_get_data
|
||||
objc_thread_get_priority
|
||||
objc_thread_id
|
||||
objc_thread_set_data
|
||||
objc_thread_set_priority
|
||||
objc_thread_yield
|
||||
__objc_class_name_Object
|
||||
__objc_class_name_Protocol
|
||||
__objc_class_name_NXConstantString
|
||||
objc_error
|
||||
__objc_object_alloc
|
||||
__objc_object_copy
|
||||
__objc_object_dispose
|
||||
class_create_instance
|
||||
object_copy
|
||||
object_dispose
|
||||
__objc_init_selector_tables
|
||||
__objc_register_selectors_from_class
|
||||
__sel_register_typed_name
|
||||
sel_get_any_typed_uid
|
||||
sel_get_any_uid
|
||||
sel_get_name
|
||||
sel_get_type
|
||||
sel_get_typed_uid
|
||||
sel_get_uid
|
||||
sel_is_mapped
|
||||
sel_register_name
|
||||
sel_register_typed_name
|
||||
sel_types_match
|
||||
method_get_first_argument
|
||||
method_get_next_argument
|
||||
method_get_nth_argument
|
||||
method_get_number_of_arguments
|
||||
method_get_sizeof_arguments
|
||||
objc_aligned_size
|
||||
objc_alignof_type
|
||||
objc_get_type_qualifiers
|
||||
objc_promoted_size
|
||||
objc_sizeof_type
|
||||
objc_skip_argspec
|
||||
objc_skip_offset
|
||||
objc_skip_type_qualifiers
|
||||
objc_skip_typespec
|
||||
__objc_read_nbyte_uint
|
||||
__objc_read_nbyte_ulong
|
||||
__objc_write_class
|
||||
__objc_write_object
|
||||
__objc_write_selector
|
||||
objc_close_typed_stream
|
||||
objc_end_of_typed_stream
|
||||
objc_flush_typed_stream
|
||||
objc_get_stream_class_version
|
||||
objc_open_typed_stream
|
||||
objc_open_typed_stream_for_file
|
||||
objc_read_array
|
||||
objc_read_char
|
||||
objc_read_int
|
||||
objc_read_long
|
||||
objc_read_object
|
||||
objc_read_selector
|
||||
objc_read_short
|
||||
objc_read_string
|
||||
objc_read_type
|
||||
objc_read_types
|
||||
objc_read_unsigned_char
|
||||
objc_read_unsigned_int
|
||||
objc_read_unsigned_long
|
||||
objc_read_unsigned_short
|
||||
objc_write_array
|
||||
objc_write_char
|
||||
objc_write_int
|
||||
objc_write_long
|
||||
objc_write_object
|
||||
objc_write_object_reference
|
||||
objc_write_root_object
|
||||
objc_write_selector
|
||||
objc_write_short
|
||||
objc_write_string
|
||||
objc_write_string_atomic
|
||||
objc_write_type
|
||||
objc_write_types
|
||||
objc_write_unsigned_char
|
||||
objc_write_unsigned_int
|
||||
objc_write_unsigned_long
|
||||
objc_write_unsigned_short
|
||||
__objc_exec_class
|
||||
__objc_init_dispatch_tables
|
||||
__objc_install_premature_dtable
|
||||
__objc_print_dtable_stats
|
||||
__objc_responds_to
|
||||
__objc_update_dispatch_table_for_class
|
||||
class_add_method_list
|
||||
class_get_class_method
|
||||
class_get_instance_method
|
||||
get_imp
|
||||
nil_method
|
||||
objc_msg_lookup
|
||||
objc_msg_lookup_super
|
||||
objc_msg_sendv
|
||||
__objc_add_class_to_hash
|
||||
__objc_init_class_tables
|
||||
__objc_resolve_class_links
|
||||
class_pose_as
|
||||
objc_get_class
|
||||
objc_get_meta_class
|
||||
objc_lookup_class
|
||||
objc_next_class
|
||||
sarray_at_put
|
||||
sarray_at_put_safe
|
||||
sarray_free
|
||||
sarray_lazy_copy
|
||||
sarray_new
|
||||
sarray_realloc
|
||||
sarray_remove_garbage
|
||||
hash_add
|
||||
hash_delete
|
||||
hash_new
|
||||
hash_next
|
||||
hash_remove
|
||||
hash_value_for_key
|
55
libobjc/libobjc_entry.c
Normal file
55
libobjc/libobjc_entry.c
Normal file
@ -0,0 +1,55 @@
|
||||
/* GNU Objective C Runtime DLL Entry
|
||||
Copyright (C) 1997 Free Software Foundation, Inc.
|
||||
Contributed by Scott Christley <scottc@net-community.com>
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to the Free
|
||||
Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
/*
|
||||
DLL entry function for Objective-C Runtime library
|
||||
This function gets called everytime a process/thread attaches to DLL
|
||||
*/
|
||||
WINBOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call,
|
||||
LPVOID lpReserved)
|
||||
{
|
||||
switch(ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
case DLL_THREAD_ATTACH:
|
||||
break;
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
This section terminates the list of imports under GCC. If you do not
|
||||
include this then you will have problems when linking with DLLs.
|
||||
*/
|
||||
asm (".section .idata$3\n" ".long 0,0,0,0,0,0,0,0");
|
40
libobjc/linking.m
Normal file
40
libobjc/linking.m
Normal file
@ -0,0 +1,40 @@
|
||||
/* Force linking of classes required by Objective C runtime.
|
||||
Copyright (C) 1997 Free Software Foundation, Inc.
|
||||
Contributed by Ovidiu Predescu (ovidiu@net-community.com).
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include <objc/Object.h>
|
||||
#include <objc/NXConstStr.h>
|
||||
|
||||
/* Generate references to Object and NXConstanstString classes since they are
|
||||
needed by the runtime system to run correctly. */
|
||||
|
||||
|
||||
void __objc_linking (void)
|
||||
{
|
||||
[Object name];
|
||||
[NXConstantString name];
|
||||
}
|
||||
|
56
libobjc/makefile.dos
Normal file
56
libobjc/makefile.dos
Normal file
@ -0,0 +1,56 @@
|
||||
# GNU Objective C Runtime Makefile for compiling with djgpp
|
||||
# Copyright (C) 1993, 1994, 1996 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of GNU CC.
|
||||
#
|
||||
# GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2, or (at your option) any later version.
|
||||
#
|
||||
# GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
# Foundation, 59 Temple Place - Suite 330,
|
||||
# Boston, MA 02111-1307, USA.
|
||||
|
||||
# This Makefile is configured for GnuMAKE
|
||||
|
||||
GCC_FOR_TARGET=gcc
|
||||
|
||||
.SUFFIXES: .o .m
|
||||
|
||||
OPTIMIZE = -O2
|
||||
|
||||
# Always search these dirs when compiling.
|
||||
SUBDIR_INCLUDES = -I. -I.. -I../config
|
||||
|
||||
.c.o:
|
||||
$(GCC_FOR_TARGET) $(OPTIMIZE) \
|
||||
-c $(GCC_CFLAGS) $(SUBDIR_INCLUDES) $<
|
||||
|
||||
.m.o:
|
||||
$(GCC_FOR_TARGET) $(OPTIMIZE) -fgnu-runtime \
|
||||
-c $(GCC_CFLAGS) $(SUBDIR_INCLUDES) $<
|
||||
|
||||
OBJC_O = hash.o sarray.o class.o sendmsg.o init.o archive.o \
|
||||
selector.o objects.o misc.o object.o protocol.o encoding.o thread.o
|
||||
|
||||
libobjc.a: $(OBJC_O)
|
||||
-rm -f libobjc.a
|
||||
ar rc libobjc.a $(OBJC_O)
|
||||
ranlib libobjc.a
|
||||
|
||||
OBJC_H = hash.h objc-list.h sarray.h objc.h \
|
||||
objc-api.h \
|
||||
object.h protocol.h mutex.h \
|
||||
typedstream.h thread.h
|
||||
|
||||
mostlyclean:
|
||||
-rm -f *.o libobjc.a xforward fflags
|
||||
clean: mostlyclean
|
||||
distclean: mostlyclean
|
||||
extraclean: mostlyclean
|
180
libobjc/misc.c
Normal file
180
libobjc/misc.c
Normal file
@ -0,0 +1,180 @@
|
||||
/* GNU Objective C Runtime Miscellaneous
|
||||
Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Kresten Krab Thorup
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to the Free
|
||||
Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#define __USE_FIXED_PROTOTYPES__
|
||||
#include <stdlib.h>
|
||||
#include "runtime.h"
|
||||
|
||||
/*
|
||||
** Error handler function
|
||||
** NULL so that default is to just print to stderr
|
||||
*/
|
||||
static objc_error_handler _objc_error_handler = NULL;
|
||||
|
||||
/* Trigger an objc error */
|
||||
void
|
||||
objc_error(id object, int code, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
objc_verror(object, code, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* Trigger an objc error */
|
||||
void
|
||||
objc_verror(id object, int code, const char* fmt, va_list ap)
|
||||
{
|
||||
BOOL result = NO;
|
||||
|
||||
/* Call the error handler if its there
|
||||
Otherwise print to stderr */
|
||||
if (_objc_error_handler)
|
||||
result = (*_objc_error_handler)(object, code, fmt, ap);
|
||||
else
|
||||
vfprintf (stderr, fmt, ap);
|
||||
|
||||
/* Continue if the error handler says its ok
|
||||
Otherwise abort the program */
|
||||
if (result)
|
||||
return;
|
||||
else
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Set the error handler */
|
||||
objc_error_handler
|
||||
objc_set_error_handler(objc_error_handler func)
|
||||
{
|
||||
objc_error_handler temp = _objc_error_handler;
|
||||
_objc_error_handler = func;
|
||||
return temp;
|
||||
}
|
||||
|
||||
/*
|
||||
** Standard functions for memory allocation and disposal.
|
||||
** Users should use these functions in their ObjC programs so
|
||||
** that they work properly with garbage collectors as well as
|
||||
** can take advantage of the exception/error handling available.
|
||||
*/
|
||||
|
||||
void *
|
||||
objc_malloc(size_t size)
|
||||
{
|
||||
void* res = (void*) (*_objc_malloc)(size);
|
||||
if(!res)
|
||||
objc_error(nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
void *
|
||||
objc_atomic_malloc(size_t size)
|
||||
{
|
||||
void* res = (void*) (*_objc_atomic_malloc)(size);
|
||||
if(!res)
|
||||
objc_error(nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
void *
|
||||
objc_valloc(size_t size)
|
||||
{
|
||||
void* res = (void*) (*_objc_valloc)(size);
|
||||
if(!res)
|
||||
objc_error(nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
void *
|
||||
objc_realloc(void *mem, size_t size)
|
||||
{
|
||||
void* res = (void*) (*_objc_realloc)(mem, size);
|
||||
if(!res)
|
||||
objc_error(nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
void *
|
||||
objc_calloc(size_t nelem, size_t size)
|
||||
{
|
||||
void* res = (void*) (*_objc_calloc)(nelem, size);
|
||||
if(!res)
|
||||
objc_error(nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
objc_free(void *mem)
|
||||
{
|
||||
(*_objc_free)(mem);
|
||||
}
|
||||
|
||||
/*
|
||||
** Hook functions for memory allocation and disposal.
|
||||
** This makes it easy to substitute garbage collection systems
|
||||
** such as Boehm's GC by assigning these function pointers
|
||||
** to the GC's allocation routines. By default these point
|
||||
** to the ANSI standard malloc, realloc, free, etc.
|
||||
**
|
||||
** Users should call the normal objc routines above for
|
||||
** memory allocation and disposal within their programs.
|
||||
*/
|
||||
|
||||
#if OBJC_WITH_GC
|
||||
#include <gc.h>
|
||||
|
||||
static void *GC_calloc (size_t nelem, size_t size)
|
||||
{
|
||||
void* p = GC_malloc (nelem * size);
|
||||
if (!p)
|
||||
objc_error (nil, OBJC_ERR_MEMORY, "Virtual memory exhausted!\n");
|
||||
|
||||
memset (p, 0, nelem * size);
|
||||
return p;
|
||||
}
|
||||
|
||||
static void noFree (void* p) {}
|
||||
|
||||
void *(*_objc_malloc)(size_t) = GC_malloc;
|
||||
void *(*_objc_atomic_malloc)(size_t) = GC_malloc_atomic;
|
||||
void *(*_objc_valloc)(size_t) = GC_malloc;
|
||||
void *(*_objc_realloc)(void *, size_t) = GC_realloc;
|
||||
void *(*_objc_calloc)(size_t, size_t) = GC_calloc;
|
||||
void (*_objc_free)(void *) = noFree;
|
||||
|
||||
#else
|
||||
|
||||
void *(*_objc_malloc)(size_t) = malloc;
|
||||
void *(*_objc_atomic_malloc)(size_t) = malloc;
|
||||
void *(*_objc_valloc)(size_t) = malloc;
|
||||
void *(*_objc_realloc)(void *, size_t) = realloc;
|
||||
void *(*_objc_calloc)(size_t, size_t) = calloc;
|
||||
void (*_objc_free)(void *) = free;
|
||||
|
||||
|
||||
#endif
|
40
libobjc/nil_method.c
Normal file
40
libobjc/nil_method.c
Normal file
@ -0,0 +1,40 @@
|
||||
/* GNU Objective C Runtime nil receiver function
|
||||
Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
|
||||
Contributed by Kresten Krab Thorup
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
/* This is the nil method, the function that is called when the receiver
|
||||
of a method is nil */
|
||||
|
||||
#include "runtime.h"
|
||||
|
||||
id
|
||||
nil_method(id receiver, SEL op, ...)
|
||||
{
|
||||
return receiver;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
597
libobjc/objc-api.h
Normal file
597
libobjc/objc-api.h
Normal file
@ -0,0 +1,597 @@
|
||||
/* GNU Objective-C Runtime API.
|
||||
Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled
|
||||
with GCC to produce an executable, this does not cause the resulting
|
||||
executable to be covered by the GNU General Public License. This
|
||||
exception does not however invalidate any other reasons why the
|
||||
executable file might be covered by the GNU General Public License. */
|
||||
|
||||
#ifndef __objc_api_INCLUDE_GNU
|
||||
#define __objc_api_INCLUDE_GNU
|
||||
|
||||
#include "objc/objc.h"
|
||||
#include "objc/hash.h"
|
||||
#include "objc/thr.h"
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* For functions which return Method_t */
|
||||
#define METHOD_NULL (Method_t)0
|
||||
/* Boolean typedefs */
|
||||
/*
|
||||
** Method descriptor returned by introspective Object methods.
|
||||
** This is really just the first part of the more complete objc_method
|
||||
** structure defined below and used internally by the runtime.
|
||||
*/
|
||||
struct objc_method_description
|
||||
{
|
||||
SEL name; /* this is a selector, not a string */
|
||||
char *types; /* type encoding */
|
||||
};
|
||||
|
||||
/* Filer types used to describe Ivars and Methods. */
|
||||
#define _C_ID '@'
|
||||
#define _C_CLASS '#'
|
||||
#define _C_SEL ':'
|
||||
#define _C_CHR 'c'
|
||||
#define _C_UCHR 'C'
|
||||
#define _C_SHT 's'
|
||||
#define _C_USHT 'S'
|
||||
#define _C_INT 'i'
|
||||
#define _C_UINT 'I'
|
||||
#define _C_LNG 'l'
|
||||
#define _C_ULNG 'L'
|
||||
#define _C_LNG_LNG 'q'
|
||||
#define _C_ULNG_LNG 'Q'
|
||||
#define _C_FLT 'f'
|
||||
#define _C_DBL 'd'
|
||||
#define _C_BFLD 'b'
|
||||
#define _C_VOID 'v'
|
||||
#define _C_UNDEF '?'
|
||||
#define _C_PTR '^'
|
||||
#define _C_CHARPTR '*'
|
||||
#define _C_ATOM '%'
|
||||
#define _C_ARY_B '['
|
||||
#define _C_ARY_E ']'
|
||||
#define _C_UNION_B '('
|
||||
#define _C_UNION_E ')'
|
||||
#define _C_STRUCT_B '{'
|
||||
#define _C_STRUCT_E '}'
|
||||
|
||||
|
||||
/*
|
||||
** Error handling
|
||||
**
|
||||
** Call objc_error() or objc_verror() to record an error; this error
|
||||
** routine will generally exit the program but not necessarily if the
|
||||
** user has installed his own error handler.
|
||||
**
|
||||
** Call objc_set_error_handler to assign your own function for
|
||||
** handling errors. The function should return YES if it is ok
|
||||
** to continue execution, or return NO or just abort if the
|
||||
** program should be stopped. The default error handler is just to
|
||||
** print a message on stderr.
|
||||
**
|
||||
** The error handler function should be of type objc_error_handler
|
||||
** The first parameter is an object instance of relevance.
|
||||
** The second parameter is an error code.
|
||||
** The third parameter is a format string in the printf style.
|
||||
** The fourth parameter is a variable list of arguments.
|
||||
*/
|
||||
extern void objc_error(id object, int code, const char* fmt, ...);
|
||||
extern void objc_verror(id object, int code, const char* fmt, va_list ap);
|
||||
typedef BOOL (*objc_error_handler)(id, int code, const char *fmt, va_list ap);
|
||||
objc_error_handler objc_set_error_handler(objc_error_handler func);
|
||||
|
||||
/*
|
||||
** Error codes
|
||||
** These are used by the runtime library, and your
|
||||
** error handling may use them to determine if the error is
|
||||
** hard or soft thus whether execution can continue or abort.
|
||||
*/
|
||||
#define OBJC_ERR_UNKNOWN 0 /* Generic error */
|
||||
|
||||
#define OBJC_ERR_OBJC_VERSION 1 /* Incorrect runtime version */
|
||||
#define OBJC_ERR_GCC_VERSION 2 /* Incorrect compiler version */
|
||||
#define OBJC_ERR_MODULE_SIZE 3 /* Bad module size */
|
||||
#define OBJC_ERR_PROTOCOL_VERSION 4 /* Incorrect protocol version */
|
||||
|
||||
#define OBJC_ERR_MEMORY 10 /* Out of memory */
|
||||
|
||||
#define OBJC_ERR_RECURSE_ROOT 20 /* Attempt to archive the root
|
||||
object more than once. */
|
||||
#define OBJC_ERR_BAD_DATA 21 /* Didn't read expected data */
|
||||
#define OBJC_ERR_BAD_KEY 22 /* Bad key for object */
|
||||
#define OBJC_ERR_BAD_CLASS 23 /* Unknown class */
|
||||
#define OBJC_ERR_BAD_TYPE 24 /* Bad type specification */
|
||||
#define OBJC_ERR_NO_READ 25 /* Cannot read stream */
|
||||
#define OBJC_ERR_NO_WRITE 26 /* Cannot write stream */
|
||||
#define OBJC_ERR_STREAM_VERSION 27 /* Incorrect stream version */
|
||||
#define OBJC_ERR_BAD_OPCODE 28 /* Bad opcode */
|
||||
|
||||
#define OBJC_ERR_UNIMPLEMENTED 30 /* Method is not implemented */
|
||||
|
||||
#define OBJC_ERR_BAD_STATE 40 /* Bad thread state */
|
||||
|
||||
/*
|
||||
** Set this variable nonzero to print a line describing each
|
||||
** message that is sent. (this is currently disabled)
|
||||
*/
|
||||
extern BOOL objc_trace;
|
||||
|
||||
|
||||
/* For every class which happens to have statically allocated instances in
|
||||
this module, one OBJC_STATIC_INSTANCES is allocated by the compiler.
|
||||
INSTANCES is NULL terminated and points to all statically allocated
|
||||
instances of this class. */
|
||||
struct objc_static_instances
|
||||
{
|
||||
char *class_name;
|
||||
id instances[0];
|
||||
};
|
||||
|
||||
/*
|
||||
** Whereas a Module (defined further down) is the root (typically) of a file,
|
||||
** a Symtab is the root of the class and category definitions within the
|
||||
** module.
|
||||
**
|
||||
** A Symtab contains a variable length array of pointers to classes and
|
||||
** categories defined in the module.
|
||||
*/
|
||||
typedef struct objc_symtab {
|
||||
unsigned long sel_ref_cnt; /* Unknown. */
|
||||
SEL refs; /* Unknown. */
|
||||
unsigned short cls_def_cnt; /* Number of classes compiled
|
||||
(defined) in the module. */
|
||||
unsigned short cat_def_cnt; /* Number of categories
|
||||
compiled (defined) in the
|
||||
module. */
|
||||
|
||||
void *defs[1]; /* Variable array of pointers.
|
||||
cls_def_cnt of type Class
|
||||
followed by cat_def_cnt of
|
||||
type Category_t, followed
|
||||
by a NULL terminated array
|
||||
of objc_static_instances. */
|
||||
} Symtab, *Symtab_t;
|
||||
|
||||
|
||||
/*
|
||||
** The compiler generates one of these structures for each module that
|
||||
** composes the executable (eg main.m).
|
||||
**
|
||||
** This data structure is the root of the definition tree for the module.
|
||||
**
|
||||
** A collect program runs between ld stages and creates a ObjC ctor array.
|
||||
** That array holds a pointer to each module structure of the executable.
|
||||
*/
|
||||
typedef struct objc_module {
|
||||
unsigned long version; /* Compiler revision. */
|
||||
unsigned long size; /* sizeof(Module). */
|
||||
const char* name; /* Name of the file where the
|
||||
module was generated. The
|
||||
name includes the path. */
|
||||
|
||||
Symtab_t symtab; /* Pointer to the Symtab of
|
||||
the module. The Symtab
|
||||
holds an array of
|
||||
pointers to
|
||||
the classes and categories
|
||||
defined in the module. */
|
||||
} Module, *Module_t;
|
||||
|
||||
|
||||
/*
|
||||
** The compiler generates one of these structures for a class that has
|
||||
** instance variables defined in its specification.
|
||||
*/
|
||||
typedef struct objc_ivar* Ivar_t;
|
||||
typedef struct objc_ivar_list {
|
||||
int ivar_count; /* Number of structures (Ivar)
|
||||
contained in the list. One
|
||||
structure per instance
|
||||
variable defined in the
|
||||
class. */
|
||||
struct objc_ivar {
|
||||
const char* ivar_name; /* Name of the instance
|
||||
variable as entered in the
|
||||
class definition. */
|
||||
const char* ivar_type; /* Description of the Ivar's
|
||||
type. Useful for
|
||||
debuggers. */
|
||||
int ivar_offset; /* Byte offset from the base
|
||||
address of the instance
|
||||
structure to the variable. */
|
||||
|
||||
} ivar_list[1]; /* Variable length
|
||||
structure. */
|
||||
} IvarList, *IvarList_t;
|
||||
|
||||
|
||||
/*
|
||||
** The compiler generates one (or more) of these structures for a class that
|
||||
** has methods defined in its specification.
|
||||
**
|
||||
** The implementation of a class can be broken into separate pieces in a file
|
||||
** and categories can break them across modules. To handle this problem is a
|
||||
** singly linked list of methods.
|
||||
*/
|
||||
typedef struct objc_method Method;
|
||||
typedef Method* Method_t;
|
||||
typedef struct objc_method_list {
|
||||
struct objc_method_list* method_next; /* This variable is used to link
|
||||
a method list to another. It
|
||||
is a singly linked list. */
|
||||
int method_count; /* Number of methods defined in
|
||||
this structure. */
|
||||
struct objc_method {
|
||||
SEL method_name; /* This variable is the method's
|
||||
name. It is a char*.
|
||||
The unique integer passed to
|
||||
objc_msg_send is a char* too.
|
||||
It is compared against
|
||||
method_name using strcmp. */
|
||||
const char* method_types; /* Description of the method's
|
||||
parameter list. Useful for
|
||||
debuggers. */
|
||||
IMP method_imp; /* Address of the method in the
|
||||
executable. */
|
||||
} method_list[1]; /* Variable length
|
||||
structure. */
|
||||
} MethodList, *MethodList_t;
|
||||
|
||||
struct objc_protocol_list {
|
||||
struct objc_protocol_list *next;
|
||||
int count;
|
||||
Protocol *list[1];
|
||||
};
|
||||
|
||||
/*
|
||||
** This is used to assure consistent access to the info field of
|
||||
** classes
|
||||
*/
|
||||
#ifndef HOST_BITS_PER_LONG
|
||||
#define HOST_BITS_PER_LONG (sizeof(long)*8)
|
||||
#endif
|
||||
|
||||
#define __CLS_INFO(cls) ((cls)->info)
|
||||
#define __CLS_ISINFO(cls, mask) ((__CLS_INFO(cls)&mask)==mask)
|
||||
#define __CLS_SETINFO(cls, mask) (__CLS_INFO(cls) |= mask)
|
||||
|
||||
/* The structure is of type MetaClass */
|
||||
#define _CLS_META 0x2L
|
||||
#define CLS_ISMETA(cls) ((cls)&&__CLS_ISINFO(cls, _CLS_META))
|
||||
|
||||
|
||||
/* The structure is of type Class */
|
||||
#define _CLS_CLASS 0x1L
|
||||
#define CLS_ISCLASS(cls) ((cls)&&__CLS_ISINFO(cls, _CLS_CLASS))
|
||||
|
||||
/*
|
||||
** The class is initialized within the runtime. This means that
|
||||
** it has had correct super and sublinks assigned
|
||||
*/
|
||||
#define _CLS_RESOLV 0x8L
|
||||
#define CLS_ISRESOLV(cls) __CLS_ISINFO(cls, _CLS_RESOLV)
|
||||
#define CLS_SETRESOLV(cls) __CLS_SETINFO(cls, _CLS_RESOLV)
|
||||
|
||||
/*
|
||||
** The class has been send a +initialize message or a such is not
|
||||
** defined for this class
|
||||
*/
|
||||
#define _CLS_INITIALIZED 0x04L
|
||||
#define CLS_ISINITIALIZED(cls) __CLS_ISINFO(cls, _CLS_INITIALIZED)
|
||||
#define CLS_SETINITIALIZED(cls) __CLS_SETINFO(cls, _CLS_INITIALIZED)
|
||||
|
||||
/*
|
||||
** The class number of this class. This must be the same for both the
|
||||
** class and its meta class object
|
||||
*/
|
||||
#define CLS_GETNUMBER(cls) (__CLS_INFO(cls) >> (HOST_BITS_PER_LONG/2))
|
||||
#define CLS_SETNUMBER(cls, num) \
|
||||
({ (cls)->info <<= (HOST_BITS_PER_LONG/2); \
|
||||
(cls)->info >>= (HOST_BITS_PER_LONG/2); \
|
||||
__CLS_SETINFO(cls, (((unsigned long)num) << (HOST_BITS_PER_LONG/2))); })
|
||||
|
||||
/*
|
||||
** The compiler generates one of these structures for each category. A class
|
||||
** may have many categories and contain both instance and factory methods.
|
||||
*/
|
||||
typedef struct objc_category {
|
||||
const char* category_name; /* Name of the category. Name
|
||||
contained in the () of the
|
||||
category definition. */
|
||||
const char* class_name; /* Name of the class to which
|
||||
the category belongs. */
|
||||
MethodList_t instance_methods; /* Linked list of instance
|
||||
methods defined in the
|
||||
category. NULL indicates no
|
||||
instance methods defined. */
|
||||
MethodList_t class_methods; /* Linked list of factory
|
||||
methods defined in the
|
||||
category. NULL indicates no
|
||||
class methods defined. */
|
||||
struct objc_protocol_list *protocols; /* List of Protocols
|
||||
conformed to */
|
||||
} Category, *Category_t;
|
||||
|
||||
/*
|
||||
** Structure used when a message is send to a class's super class. The
|
||||
** compiler generates one of these structures and passes it to
|
||||
** objc_msg_super.
|
||||
*/
|
||||
typedef struct objc_super {
|
||||
id self; /* Id of the object sending
|
||||
the message. */
|
||||
Class class; /* Object's super class. */
|
||||
} Super, *Super_t;
|
||||
|
||||
IMP objc_msg_lookup_super(Super_t super, SEL sel);
|
||||
|
||||
retval_t objc_msg_sendv(id, SEL, arglist_t);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** This is a hook which is called by objc_lookup_class and
|
||||
** objc_get_class if the runtime is not able to find the class.
|
||||
** This may e.g. try to load in the class using dynamic loading.
|
||||
** The function is guaranteed to be passed a non-NULL name string.
|
||||
*/
|
||||
extern Class (*_objc_lookup_class)(const char *name);
|
||||
|
||||
/*
|
||||
** This is a hook which is called by __objc_exec_class every time a class
|
||||
** or a category is loaded into the runtime. This may e.g. help a
|
||||
** dynamic loader determine the classes that have been loaded when
|
||||
** an object file is dynamically linked in.
|
||||
*/
|
||||
extern void (*_objc_load_callback)(Class class, Category* category);
|
||||
|
||||
/*
|
||||
** Hook functions for allocating, copying and disposing of instances
|
||||
*/
|
||||
extern id (*_objc_object_alloc)(Class class);
|
||||
extern id (*_objc_object_copy)(id object);
|
||||
extern id (*_objc_object_dispose)(id object);
|
||||
|
||||
/*
|
||||
** Standard functions for memory allocation and disposal.
|
||||
** Users should use these functions in their ObjC programs so
|
||||
** that they work properly with garbage collectors as well as
|
||||
** can take advantage of the exception/error handling available.
|
||||
*/
|
||||
void *
|
||||
objc_malloc(size_t size);
|
||||
|
||||
void *
|
||||
objc_atomic_malloc(size_t size);
|
||||
|
||||
void *
|
||||
objc_valloc(size_t size);
|
||||
|
||||
void *
|
||||
objc_realloc(void *mem, size_t size);
|
||||
|
||||
void *
|
||||
objc_calloc(size_t nelem, size_t size);
|
||||
|
||||
void
|
||||
objc_free(void *mem);
|
||||
|
||||
/*
|
||||
** Hook functions for memory allocation and disposal.
|
||||
** This makes it easy to substitute garbage collection systems
|
||||
** such as Boehm's GC by assigning these function pointers
|
||||
** to the GC's allocation routines. By default these point
|
||||
** to the ANSI standard malloc, realloc, free, etc.
|
||||
**
|
||||
** Users should call the normal objc routines above for
|
||||
** memory allocation and disposal within their programs.
|
||||
*/
|
||||
extern void *(*_objc_malloc)(size_t);
|
||||
extern void *(*_objc_atomic_malloc)(size_t);
|
||||
extern void *(*_objc_valloc)(size_t);
|
||||
extern void *(*_objc_realloc)(void *, size_t);
|
||||
extern void *(*_objc_calloc)(size_t, size_t);
|
||||
extern void (*_objc_free)(void *);
|
||||
|
||||
Method_t class_get_class_method(MetaClass class, SEL aSel);
|
||||
|
||||
Method_t class_get_instance_method(Class class, SEL aSel);
|
||||
|
||||
Class class_pose_as(Class impostor, Class superclass);
|
||||
|
||||
Class objc_get_class(const char *name);
|
||||
|
||||
Class objc_lookup_class(const char *name);
|
||||
|
||||
Class objc_next_class(void **enum_state);
|
||||
|
||||
const char *sel_get_name(SEL selector);
|
||||
|
||||
const char *sel_get_type(SEL selector);
|
||||
|
||||
SEL sel_get_uid(const char *name);
|
||||
|
||||
SEL sel_get_any_uid(const char *name);
|
||||
|
||||
SEL sel_get_any_typed_uid(const char *name);
|
||||
|
||||
SEL sel_get_typed_uid(const char *name, const char*);
|
||||
|
||||
SEL sel_register_name(const char *name);
|
||||
|
||||
SEL sel_register_typed_name(const char *name, const char*type);
|
||||
|
||||
|
||||
BOOL sel_is_mapped (SEL aSel);
|
||||
|
||||
extern id class_create_instance(Class class);
|
||||
|
||||
static inline const char *
|
||||
class_get_class_name(Class class)
|
||||
{
|
||||
return CLS_ISCLASS(class)?class->name:((class==Nil)?"Nil":0);
|
||||
}
|
||||
|
||||
static inline long
|
||||
class_get_instance_size(Class class)
|
||||
{
|
||||
return CLS_ISCLASS(class)?class->instance_size:0;
|
||||
}
|
||||
|
||||
static inline MetaClass
|
||||
class_get_meta_class(Class class)
|
||||
{
|
||||
return CLS_ISCLASS(class)?class->class_pointer:Nil;
|
||||
}
|
||||
|
||||
static inline Class
|
||||
class_get_super_class(Class class)
|
||||
{
|
||||
return CLS_ISCLASS(class)?class->super_class:Nil;
|
||||
}
|
||||
|
||||
static inline int
|
||||
class_get_version(Class class)
|
||||
{
|
||||
return CLS_ISCLASS(class)?class->version:-1;
|
||||
}
|
||||
|
||||
static inline BOOL
|
||||
class_is_class(Class class)
|
||||
{
|
||||
return CLS_ISCLASS(class);
|
||||
}
|
||||
|
||||
static inline BOOL
|
||||
class_is_meta_class(Class class)
|
||||
{
|
||||
return CLS_ISMETA(class);
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
class_set_version(Class class, long version)
|
||||
{
|
||||
if (CLS_ISCLASS(class))
|
||||
class->version = version;
|
||||
}
|
||||
|
||||
static inline void *
|
||||
class_get_gc_object_type (Class class)
|
||||
{
|
||||
return CLS_ISCLASS(class) ? class->gc_object_type : NULL;
|
||||
}
|
||||
|
||||
/* Mark the instance variable as innaccessible to the garbage collector */
|
||||
extern void class_ivar_set_gcinvisible (Class class,
|
||||
const char* ivarname,
|
||||
BOOL gcInvisible);
|
||||
|
||||
static inline IMP
|
||||
method_get_imp(Method_t method)
|
||||
{
|
||||
return (method!=METHOD_NULL)?method->method_imp:(IMP)0;
|
||||
}
|
||||
|
||||
IMP get_imp (Class class, SEL sel);
|
||||
|
||||
/* Redefine on NeXTSTEP so as not to conflict with system function */
|
||||
#ifdef __NeXT__
|
||||
#define object_copy gnu_object_copy
|
||||
#define object_dispose gnu_object_dispose
|
||||
#endif
|
||||
|
||||
id object_copy(id object);
|
||||
|
||||
id object_dispose(id object);
|
||||
|
||||
static inline Class
|
||||
object_get_class(id object)
|
||||
{
|
||||
return ((object!=nil)
|
||||
? (CLS_ISCLASS(object->class_pointer)
|
||||
? object->class_pointer
|
||||
: (CLS_ISMETA(object->class_pointer)
|
||||
? (Class)object
|
||||
: Nil))
|
||||
: Nil);
|
||||
}
|
||||
|
||||
static inline const char *
|
||||
object_get_class_name(id object)
|
||||
{
|
||||
return ((object!=nil)?(CLS_ISCLASS(object->class_pointer)
|
||||
?object->class_pointer->name
|
||||
:((Class)object)->name)
|
||||
:"Nil");
|
||||
}
|
||||
|
||||
static inline MetaClass
|
||||
object_get_meta_class(id object)
|
||||
{
|
||||
return ((object!=nil)?(CLS_ISCLASS(object->class_pointer)
|
||||
?object->class_pointer->class_pointer
|
||||
:(CLS_ISMETA(object->class_pointer)
|
||||
?object->class_pointer
|
||||
:Nil))
|
||||
:Nil);
|
||||
}
|
||||
|
||||
static inline Class
|
||||
object_get_super_class
|
||||
(id object)
|
||||
{
|
||||
return ((object!=nil)?(CLS_ISCLASS(object->class_pointer)
|
||||
?object->class_pointer->super_class
|
||||
:(CLS_ISMETA(object->class_pointer)
|
||||
?((Class)object)->super_class
|
||||
:Nil))
|
||||
:Nil);
|
||||
}
|
||||
|
||||
static inline BOOL
|
||||
object_is_class(id object)
|
||||
{
|
||||
return CLS_ISCLASS((Class)object);
|
||||
}
|
||||
|
||||
static inline BOOL
|
||||
object_is_instance(id object)
|
||||
{
|
||||
return (object!=nil)&&CLS_ISCLASS(object->class_pointer);
|
||||
}
|
||||
|
||||
static inline BOOL
|
||||
object_is_meta_class(id object)
|
||||
{
|
||||
return CLS_ISMETA((Class)object);
|
||||
}
|
||||
|
||||
struct sarray*
|
||||
objc_get_uninstalled_dtable(void);
|
||||
|
||||
#endif /* not __objc_api_INCLUDE_GNU */
|
||||
|
||||
|
||||
|
147
libobjc/objc-list.h
Normal file
147
libobjc/objc-list.h
Normal file
@ -0,0 +1,147 @@
|
||||
/* Generic single linked list to keep various information
|
||||
Copyright (C) 1993, 1994, 1996 Free Software Foundation, Inc.
|
||||
Contributed by Kresten Krab Thorup.
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#ifndef __GNU_OBJC_LIST_H
|
||||
#define __GNU_OBJC_LIST_H
|
||||
|
||||
struct objc_list {
|
||||
void *head;
|
||||
struct objc_list *tail;
|
||||
};
|
||||
|
||||
/* Return a cons cell produced from (head . tail) */
|
||||
|
||||
static inline struct objc_list*
|
||||
list_cons(void* head, struct objc_list* tail)
|
||||
{
|
||||
struct objc_list* cell;
|
||||
|
||||
cell = (struct objc_list*)objc_malloc(sizeof(struct objc_list));
|
||||
cell->head = head;
|
||||
cell->tail = tail;
|
||||
return cell;
|
||||
}
|
||||
|
||||
/* Return the length of a list, list_length(NULL) returns zero */
|
||||
|
||||
static inline int
|
||||
list_length(struct objc_list* list)
|
||||
{
|
||||
int i = 0;
|
||||
while(list)
|
||||
{
|
||||
i += 1;
|
||||
list = list->tail;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Return the Nth element of LIST, where N count from zero. If N
|
||||
larger than the list length, NULL is returned */
|
||||
|
||||
static inline void*
|
||||
list_nth(int index, struct objc_list* list)
|
||||
{
|
||||
while(index-- != 0)
|
||||
{
|
||||
if(list->tail)
|
||||
list = list->tail;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
return list->head;
|
||||
}
|
||||
|
||||
/* Remove the element at the head by replacing it by its successor */
|
||||
|
||||
static inline void
|
||||
list_remove_head(struct objc_list** list)
|
||||
{
|
||||
if ((*list)->tail)
|
||||
{
|
||||
struct objc_list* tail = (*list)->tail; /* fetch next */
|
||||
*(*list) = *tail; /* copy next to list head */
|
||||
objc_free(tail); /* free next */
|
||||
}
|
||||
else /* only one element in list */
|
||||
{
|
||||
objc_free(*list);
|
||||
(*list) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Remove the element with `car' set to ELEMENT */
|
||||
|
||||
static inline void
|
||||
list_remove_elem(struct objc_list** list, void* elem)
|
||||
{
|
||||
while (*list) {
|
||||
if ((*list)->head == elem)
|
||||
list_remove_head(list);
|
||||
list = &((*list)->tail);
|
||||
}
|
||||
}
|
||||
|
||||
/* Map FUNCTION over all elements in LIST */
|
||||
|
||||
static inline void
|
||||
list_mapcar(struct objc_list* list, void(*function)(void*))
|
||||
{
|
||||
while(list)
|
||||
{
|
||||
(*function)(list->head);
|
||||
list = list->tail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return element that has ELEM as car */
|
||||
|
||||
static inline struct objc_list**
|
||||
list_find(struct objc_list** list, void* elem)
|
||||
{
|
||||
while(*list)
|
||||
{
|
||||
if ((*list)->head == elem)
|
||||
return list;
|
||||
list = &((*list)->tail);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Free list (backwards recursive) */
|
||||
|
||||
static void
|
||||
list_free(struct objc_list* list)
|
||||
{
|
||||
if(list)
|
||||
{
|
||||
list_free(list->tail);
|
||||
objc_free(list);
|
||||
}
|
||||
}
|
||||
#endif __GNU_OBJC_LIST_H
|
158
libobjc/objc.h
Normal file
158
libobjc/objc.h
Normal file
@ -0,0 +1,158 @@
|
||||
/* Basic data types for Objective C.
|
||||
Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files
|
||||
compiled with GCC to produce an executable, this does not cause
|
||||
the resulting executable to be covered by the GNU General Public License.
|
||||
This exception does not however invalidate any other reasons why
|
||||
the executable file might be covered by the GNU General Public License. */
|
||||
|
||||
#ifndef __objc_INCLUDE_GNU
|
||||
#define __objc_INCLUDE_GNU
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/*
|
||||
** Definition of the boolean type.
|
||||
*/
|
||||
#ifdef __vxworks
|
||||
typedef int BOOL;
|
||||
#else
|
||||
typedef unsigned char BOOL;
|
||||
#endif
|
||||
#define YES (BOOL)1
|
||||
#define NO (BOOL)0
|
||||
|
||||
/*
|
||||
** Definition of a selector. Selectors themselves are not unique, but
|
||||
** the sel_id is a unique identifier.
|
||||
*/
|
||||
typedef const struct objc_selector
|
||||
{
|
||||
void *sel_id;
|
||||
const char *sel_types;
|
||||
} *SEL;
|
||||
|
||||
inline static BOOL
|
||||
sel_eq (SEL s1, SEL s2)
|
||||
{
|
||||
if (s1 == 0 || s2 == 0)
|
||||
return s1 == s2;
|
||||
else
|
||||
return s1->sel_id == s2->sel_id;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** ObjC uses this typedef for untyped instances.
|
||||
*/
|
||||
typedef struct objc_object {
|
||||
struct objc_class* class_pointer;
|
||||
} *id;
|
||||
|
||||
/*
|
||||
** Definition of method type. When retrieving the implementation of a
|
||||
** method, this is type of the pointer returned
|
||||
*/
|
||||
typedef id (*IMP)(id, SEL, ...);
|
||||
|
||||
/*
|
||||
** More simple types...
|
||||
*/
|
||||
#define nil (id)0 /* id of Nil instance */
|
||||
#define Nil (Class)0 /* id of Nil class */
|
||||
typedef char *STR; /* String alias */
|
||||
|
||||
/*
|
||||
** The compiler generates one of these structures for each class.
|
||||
**
|
||||
** This structure is the definition for classes.
|
||||
**
|
||||
** This structure is generated by the compiler in the executable and used by
|
||||
** the run-time during normal messaging operations. Therefore some members
|
||||
** change type. The compiler generates "char* const" and places a string in
|
||||
** the following member variables: super_class.
|
||||
*/
|
||||
typedef struct objc_class *MetaClass;
|
||||
typedef struct objc_class *Class;
|
||||
struct objc_class {
|
||||
MetaClass class_pointer; /* Pointer to the class's
|
||||
meta class. */
|
||||
struct objc_class* super_class; /* Pointer to the super
|
||||
class. NULL for class
|
||||
Object. */
|
||||
const char* name; /* Name of the class. */
|
||||
long version; /* Unknown. */
|
||||
unsigned long info; /* Bit mask. See class masks
|
||||
defined above. */
|
||||
long instance_size; /* Size in bytes of the class.
|
||||
The sum of the class
|
||||
definition and all super
|
||||
class definitions. */
|
||||
struct objc_ivar_list* ivars; /* Pointer to a structure that
|
||||
describes the instance
|
||||
variables in the class
|
||||
definition. NULL indicates
|
||||
no instance variables. Does
|
||||
not include super class
|
||||
variables. */
|
||||
struct objc_method_list* methods; /* Linked list of instance
|
||||
methods defined for the
|
||||
class. */
|
||||
struct sarray * dtable; /* Pointer to instance
|
||||
method dispatch table. */
|
||||
struct objc_class* subclass_list; /* Subclasses */
|
||||
struct objc_class* sibling_class;
|
||||
|
||||
struct objc_protocol_list *protocols; /* Protocols conformed to */
|
||||
void* gc_object_type;
|
||||
};
|
||||
|
||||
#ifndef __OBJC__
|
||||
typedef struct objc_protocol {
|
||||
struct objc_class* class_pointer;
|
||||
char *protocol_name;
|
||||
struct objc_protocol_list *protocol_list;
|
||||
struct objc_method_description_list *instance_methods, *class_methods;
|
||||
} Protocol;
|
||||
|
||||
#else /* __OBJC__ */
|
||||
@class Protocol;
|
||||
#endif
|
||||
|
||||
typedef void* retval_t; /* return value */
|
||||
typedef void(*apply_t)(void); /* function pointer */
|
||||
typedef union {
|
||||
char *arg_ptr;
|
||||
char arg_regs[sizeof (char*)];
|
||||
} *arglist_t; /* argument frame */
|
||||
|
||||
|
||||
IMP objc_msg_lookup(id receiver, SEL op);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* not __objc_INCLUDE_GNU */
|
105
libobjc/objects.c
Normal file
105
libobjc/objects.c
Normal file
@ -0,0 +1,105 @@
|
||||
/* GNU Objective C Runtime class related functions
|
||||
Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
|
||||
Contributed by Kresten Krab Thorup
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include "../tconfig.h" /* include defs of bzero for target */
|
||||
#include "objc.h"
|
||||
#include "runtime.h" /* the kitchen sink */
|
||||
|
||||
#if OBJC_WITH_GC
|
||||
# include <gc.h>
|
||||
#endif
|
||||
|
||||
id __objc_object_alloc(Class);
|
||||
id __objc_object_dispose(id);
|
||||
id __objc_object_copy(id);
|
||||
|
||||
id (*_objc_object_alloc)(Class) = __objc_object_alloc; /* !T:SINGLE */
|
||||
id (*_objc_object_dispose)(id) = __objc_object_dispose; /* !T:SINGLE */
|
||||
id (*_objc_object_copy)(id) = __objc_object_copy; /* !T:SINGLE */
|
||||
|
||||
id
|
||||
class_create_instance(Class class)
|
||||
{
|
||||
id new = nil;
|
||||
|
||||
#if OBJC_WITH_GC
|
||||
if (CLS_ISCLASS(class))
|
||||
new = (id)GC_malloc_explicitly_typed (class->instance_size,
|
||||
class->gc_object_type);
|
||||
#else
|
||||
if (CLS_ISCLASS(class))
|
||||
new = (*_objc_object_alloc)(class);
|
||||
#endif
|
||||
|
||||
if (new!=nil)
|
||||
{
|
||||
memset (new, 0, class->instance_size);
|
||||
new->class_pointer = class;
|
||||
}
|
||||
return new;
|
||||
}
|
||||
|
||||
id
|
||||
object_copy(id object)
|
||||
{
|
||||
if ((object!=nil)&&CLS_ISCLASS(object->class_pointer))
|
||||
return (*_objc_object_copy)(object);
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
id
|
||||
object_dispose(id object)
|
||||
{
|
||||
if ((object!=nil)&&CLS_ISCLASS(object->class_pointer))
|
||||
{
|
||||
if (_objc_object_dispose)
|
||||
(*_objc_object_dispose)(object);
|
||||
else
|
||||
objc_free(object);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
id __objc_object_alloc(Class class)
|
||||
{
|
||||
return (id)objc_malloc(class->instance_size);
|
||||
}
|
||||
|
||||
id __objc_object_dispose(id object)
|
||||
{
|
||||
objc_free(object);
|
||||
return 0;
|
||||
}
|
||||
|
||||
id __objc_object_copy(id object)
|
||||
{
|
||||
id copy = class_create_instance(object->class_pointer);
|
||||
memcpy(copy, object, object->class_pointer->instance_size);
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
88
libobjc/runtime.h
Normal file
88
libobjc/runtime.h
Normal file
@ -0,0 +1,88 @@
|
||||
/* GNU Objective C Runtime internal declarations
|
||||
Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Kresten Krab Thorup
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#ifndef __objc_runtime_INCLUDE_GNU
|
||||
#define __objc_runtime_INCLUDE_GNU
|
||||
|
||||
#include <stdarg.h> /* for varargs and va_list's */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <stddef.h> /* so noone else will get system versions */
|
||||
#include "assert.h"
|
||||
|
||||
#include "objc/objc.h" /* core data types */
|
||||
#include "objc/objc-api.h" /* runtime api functions */
|
||||
|
||||
#include "objc/thr.h" /* thread and mutex support */
|
||||
|
||||
#include "objc/hash.h" /* hash structures */
|
||||
#include "objc/objc-list.h" /* linear lists */
|
||||
|
||||
extern void __objc_add_class_to_hash(Class); /* (objc-class.c) */
|
||||
extern void __objc_init_selector_tables(void); /* (objc-sel.c) */
|
||||
extern void __objc_init_class_tables(void); /* (objc-class.c) */
|
||||
extern void __objc_init_dispatch_tables(void); /* (objc-dispatch.c) */
|
||||
extern void __objc_install_premature_dtable(Class); /* (objc-dispatch.c) */
|
||||
extern void __objc_resolve_class_links(void); /* (objc-class.c) */
|
||||
extern void __objc_register_selectors_from_class(Class); /* (objc-sel.c) */
|
||||
extern void __objc_update_dispatch_table_for_class (Class);/* (objc-msg.c) */
|
||||
|
||||
extern int __objc_init_thread_system(void); /* thread.c */
|
||||
extern int __objc_fini_thread_system(void); /* thread.c */
|
||||
extern void __objc_print_dtable_stats(void); /* sendmsg.c */
|
||||
|
||||
extern void class_add_method_list(Class, MethodList_t);
|
||||
|
||||
/* Registering instance methods as class methods for root classes */
|
||||
extern void __objc_register_instance_methods_to_class(Class);
|
||||
extern Method_t search_for_method_in_list(MethodList_t list, SEL op);
|
||||
|
||||
/* True when class links has been resolved */
|
||||
extern BOOL __objc_class_links_resolved;
|
||||
|
||||
/* Number of selectors stored in each of the selector tables */
|
||||
extern int __objc_selector_max_index;
|
||||
|
||||
/* Mutex locking __objc_selector_max_index and its arrays. */
|
||||
extern objc_mutex_t __objc_runtime_mutex;
|
||||
|
||||
/* Number of threads which are alive. */
|
||||
extern int __objc_runtime_threads_alive;
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DEBUG_PRINTF(format, args...) printf (format, ## args)
|
||||
#else
|
||||
#define DEBUG_PRINTF(format, args...)
|
||||
#endif
|
||||
|
||||
BOOL __objc_responds_to (id object, SEL sel); /* for internal use only! */
|
||||
SEL __sel_register_typed_name (const char*, const char*,
|
||||
struct objc_selector*, BOOL is_const);
|
||||
|
||||
#endif /* not __objc_runtime_INCLUDE_GNU */
|
||||
|
||||
|
522
libobjc/sarray.c
Normal file
522
libobjc/sarray.c
Normal file
@ -0,0 +1,522 @@
|
||||
/* Sparse Arrays for Objective C dispatch tables
|
||||
Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files
|
||||
compiled with GCC to produce an executable, this does not cause
|
||||
the resulting executable to be covered by the GNU General Public License.
|
||||
This exception does not however invalidate any other reasons why
|
||||
the executable file might be covered by the GNU General Public License. */
|
||||
|
||||
#include "objc/sarray.h"
|
||||
#include "objc/runtime.h"
|
||||
#include <stdio.h>
|
||||
#include "assert.h"
|
||||
|
||||
int nbuckets = 0; /* !T:MUTEX */
|
||||
int nindices = 0; /* !T:MUTEX */
|
||||
int narrays = 0; /* !T:MUTEX */
|
||||
int idxsize = 0; /* !T:MUTEX */
|
||||
|
||||
static void * first_free_data = NULL; /* !T:MUTEX */
|
||||
|
||||
#ifdef OBJC_SPARSE2
|
||||
const char* __objc_sparse2_id = "2 level sparse indices";
|
||||
#endif
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
const char* __objc_sparse3_id = "3 level sparse indices";
|
||||
#endif
|
||||
|
||||
#ifdef __alpha__
|
||||
const void *memcpy (void*, const void*, size_t);
|
||||
#endif
|
||||
|
||||
/* This function removes any structures left over from free operations
|
||||
that were not safe in a multi-threaded environment. */
|
||||
void
|
||||
sarray_remove_garbage(void)
|
||||
{
|
||||
void **vp;
|
||||
void *np;
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
vp = first_free_data;
|
||||
first_free_data = NULL;
|
||||
|
||||
while (vp) {
|
||||
np = *vp;
|
||||
objc_free(vp);
|
||||
vp = np;
|
||||
}
|
||||
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
}
|
||||
|
||||
/* Free a block of dynamically allocated memory. If we are in multi-threaded
|
||||
mode, it is ok to free it. If not, we add it to the garbage heap to be
|
||||
freed later. */
|
||||
|
||||
static void
|
||||
sarray_free_garbage(void *vp)
|
||||
{
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
if (__objc_runtime_threads_alive == 1) {
|
||||
objc_free(vp);
|
||||
if (first_free_data)
|
||||
sarray_remove_garbage();
|
||||
}
|
||||
else {
|
||||
*(void **)vp = first_free_data;
|
||||
first_free_data = vp;
|
||||
}
|
||||
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
}
|
||||
|
||||
/* sarray_at_put : copies data in such a way as to be thread reader safe. */
|
||||
void
|
||||
sarray_at_put(struct sarray* array, sidx index, void* element)
|
||||
{
|
||||
#ifdef OBJC_SPARSE3
|
||||
struct sindex** the_index;
|
||||
struct sindex* new_index;
|
||||
#endif
|
||||
struct sbucket** the_bucket;
|
||||
struct sbucket* new_bucket;
|
||||
#ifdef OBJC_SPARSE3
|
||||
size_t ioffset;
|
||||
#endif
|
||||
size_t boffset;
|
||||
size_t eoffset;
|
||||
#ifdef PRECOMPUTE_SELECTORS
|
||||
union sofftype xx;
|
||||
xx.idx = index;
|
||||
#ifdef OBJC_SPARSE3
|
||||
ioffset = xx.off.ioffset;
|
||||
#endif
|
||||
boffset = xx.off.boffset;
|
||||
eoffset = xx.off.eoffset;
|
||||
#else /* not PRECOMPUTE_SELECTORS */
|
||||
#ifdef OBJC_SPARSE3
|
||||
ioffset = index/INDEX_CAPACITY;
|
||||
boffset = (index/BUCKET_SIZE)%INDEX_SIZE;
|
||||
eoffset = index%BUCKET_SIZE;
|
||||
#else
|
||||
boffset = index/BUCKET_SIZE;
|
||||
eoffset = index%BUCKET_SIZE;
|
||||
#endif
|
||||
#endif /* not PRECOMPUTE_SELECTORS */
|
||||
|
||||
assert(soffset_decode(index) < array->capacity); /* Range check */
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
the_index = &(array->indices[ioffset]);
|
||||
the_bucket = &((*the_index)->buckets[boffset]);
|
||||
#else
|
||||
the_bucket = &(array->buckets[boffset]);
|
||||
#endif
|
||||
|
||||
if ((*the_bucket)->elems[eoffset] == element)
|
||||
return; /* great! we just avoided a lazy copy */
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
|
||||
/* First, perform lazy copy/allocation of index if needed */
|
||||
|
||||
if ((*the_index) == array->empty_index) {
|
||||
|
||||
/* The index was previously empty, allocate a new */
|
||||
new_index = (struct sindex*)objc_malloc(sizeof(struct sindex));
|
||||
memcpy(new_index, array->empty_index, sizeof(struct sindex));
|
||||
new_index->version.version = array->version.version;
|
||||
*the_index = new_index; /* Prepared for install. */
|
||||
the_bucket = &((*the_index)->buckets[boffset]);
|
||||
|
||||
nindices += 1;
|
||||
} else if ((*the_index)->version.version != array->version.version) {
|
||||
|
||||
/* This index must be lazy copied */
|
||||
struct sindex* old_index = *the_index;
|
||||
new_index = (struct sindex*)objc_malloc(sizeof(struct sindex));
|
||||
memcpy( new_index, old_index, sizeof(struct sindex));
|
||||
new_index->version.version = array->version.version;
|
||||
*the_index = new_index; /* Prepared for install. */
|
||||
the_bucket = &((*the_index)->buckets[boffset]);
|
||||
|
||||
nindices += 1;
|
||||
}
|
||||
|
||||
#endif /* OBJC_SPARSE3 */
|
||||
|
||||
/* next, perform lazy allocation/copy of the bucket if needed */
|
||||
|
||||
if ((*the_bucket) == array->empty_bucket) {
|
||||
|
||||
/* The bucket was previously empty (or something like that), */
|
||||
/* allocate a new. This is the effect of `lazy' allocation */
|
||||
new_bucket = (struct sbucket*)objc_malloc(sizeof(struct sbucket));
|
||||
memcpy((void *) new_bucket, (const void*)array->empty_bucket,
|
||||
sizeof(struct sbucket));
|
||||
new_bucket->version.version = array->version.version;
|
||||
*the_bucket = new_bucket; /* Prepared for install. */
|
||||
|
||||
nbuckets += 1;
|
||||
|
||||
} else if ((*the_bucket)->version.version != array->version.version) {
|
||||
|
||||
/* Perform lazy copy. */
|
||||
struct sbucket* old_bucket = *the_bucket;
|
||||
new_bucket = (struct sbucket*)objc_malloc(sizeof(struct sbucket));
|
||||
memcpy( new_bucket, old_bucket, sizeof(struct sbucket));
|
||||
new_bucket->version.version = array->version.version;
|
||||
*the_bucket = new_bucket; /* Prepared for install. */
|
||||
|
||||
nbuckets += 1;
|
||||
|
||||
}
|
||||
(*the_bucket)->elems[eoffset] = element;
|
||||
}
|
||||
|
||||
void
|
||||
sarray_at_put_safe(struct sarray* array, sidx index, void* element)
|
||||
{
|
||||
if(soffset_decode(index) >= array->capacity)
|
||||
sarray_realloc(array, soffset_decode(index)+1);
|
||||
sarray_at_put(array, index, element);
|
||||
}
|
||||
|
||||
struct sarray*
|
||||
sarray_new (int size, void* default_element)
|
||||
{
|
||||
struct sarray* arr;
|
||||
#ifdef OBJC_SPARSE3
|
||||
size_t num_indices = ((size-1)/(INDEX_CAPACITY))+1;
|
||||
struct sindex ** new_indices;
|
||||
#else /* OBJC_SPARSE2 */
|
||||
size_t num_indices = ((size-1)/BUCKET_SIZE)+1;
|
||||
struct sbucket ** new_buckets;
|
||||
#endif
|
||||
int counter;
|
||||
|
||||
assert(size > 0);
|
||||
|
||||
/* Allocate core array */
|
||||
arr = (struct sarray*) objc_malloc(sizeof(struct sarray));
|
||||
arr->version.version = 0;
|
||||
|
||||
/* Initialize members */
|
||||
#ifdef OBJC_SPARSE3
|
||||
arr->capacity = num_indices*INDEX_CAPACITY;
|
||||
new_indices = (struct sindex**)
|
||||
objc_malloc(sizeof(struct sindex*)*num_indices);
|
||||
|
||||
arr->empty_index = (struct sindex*) objc_malloc(sizeof(struct sindex));
|
||||
arr->empty_index->version.version = 0;
|
||||
|
||||
narrays += 1;
|
||||
idxsize += num_indices;
|
||||
nindices += 1;
|
||||
|
||||
#else /* OBJC_SPARSE2 */
|
||||
arr->capacity = num_indices*BUCKET_SIZE;
|
||||
new_buckets = (struct sbucket**)
|
||||
objc_malloc(sizeof(struct sbucket*)*num_indices);
|
||||
|
||||
narrays += 1;
|
||||
idxsize += num_indices;
|
||||
|
||||
#endif
|
||||
|
||||
arr->empty_bucket = (struct sbucket*) objc_malloc(sizeof(struct sbucket));
|
||||
arr->empty_bucket->version.version = 0;
|
||||
|
||||
nbuckets += 1;
|
||||
|
||||
arr->ref_count = 1;
|
||||
arr->is_copy_of = (struct sarray*)0;
|
||||
|
||||
for (counter=0; counter<BUCKET_SIZE; counter++)
|
||||
arr->empty_bucket->elems[counter] = default_element;
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
for (counter=0; counter<INDEX_SIZE; counter++)
|
||||
arr->empty_index->buckets[counter] = arr->empty_bucket;
|
||||
|
||||
for (counter=0; counter<num_indices; counter++)
|
||||
new_indices[counter] = arr->empty_index;
|
||||
|
||||
#else /* OBJC_SPARSE2 */
|
||||
|
||||
for (counter=0; counter<num_indices; counter++)
|
||||
new_buckets[counter] = arr->empty_bucket;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
arr->indices = new_indices;
|
||||
#else /* OBJC_SPARSE2 */
|
||||
arr->buckets = new_buckets;
|
||||
#endif
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
/* Reallocate the sparse array to hold `newsize' entries
|
||||
Note: We really allocate and then free. We have to do this to ensure that
|
||||
any concurrent readers notice the update. */
|
||||
|
||||
void
|
||||
sarray_realloc(struct sarray* array, int newsize)
|
||||
{
|
||||
#ifdef OBJC_SPARSE3
|
||||
size_t old_max_index = (array->capacity-1)/INDEX_CAPACITY;
|
||||
size_t new_max_index = ((newsize-1)/INDEX_CAPACITY);
|
||||
size_t rounded_size = (new_max_index+1)*INDEX_CAPACITY;
|
||||
|
||||
struct sindex ** new_indices;
|
||||
struct sindex ** old_indices;
|
||||
|
||||
#else /* OBJC_SPARSE2 */
|
||||
size_t old_max_index = (array->capacity-1)/BUCKET_SIZE;
|
||||
size_t new_max_index = ((newsize-1)/BUCKET_SIZE);
|
||||
size_t rounded_size = (new_max_index+1)*BUCKET_SIZE;
|
||||
|
||||
struct sbucket ** new_buckets;
|
||||
struct sbucket ** old_buckets;
|
||||
|
||||
#endif
|
||||
|
||||
int counter;
|
||||
|
||||
assert(newsize > 0);
|
||||
|
||||
/* The size is the same, just ignore the request */
|
||||
if(rounded_size <= array->capacity)
|
||||
return;
|
||||
|
||||
assert(array->ref_count == 1); /* stop if lazy copied... */
|
||||
|
||||
/* We are asked to extend the array -- allocate new bucket table, */
|
||||
/* and insert empty_bucket in newly allocated places. */
|
||||
if(rounded_size > array->capacity)
|
||||
{
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
new_max_index += 4;
|
||||
rounded_size = (new_max_index+1)*INDEX_CAPACITY;
|
||||
|
||||
#else /* OBJC_SPARSE2 */
|
||||
new_max_index += 4;
|
||||
rounded_size = (new_max_index+1)*BUCKET_SIZE;
|
||||
#endif
|
||||
|
||||
/* update capacity */
|
||||
array->capacity = rounded_size;
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
/* alloc to force re-read by any concurrent readers. */
|
||||
old_indices = array->indices;
|
||||
new_indices = (struct sindex**)
|
||||
objc_malloc((new_max_index+1)*sizeof(struct sindex*));
|
||||
#else /* OBJC_SPARSE2 */
|
||||
old_buckets = array->buckets;
|
||||
new_buckets = (struct sbucket**)
|
||||
objc_malloc((new_max_index+1)*sizeof(struct sbucket*));
|
||||
#endif
|
||||
|
||||
/* copy buckets below old_max_index (they are still valid) */
|
||||
for(counter = 0; counter <= old_max_index; counter++ ) {
|
||||
#ifdef OBJC_SPARSE3
|
||||
new_indices[counter] = old_indices[counter];
|
||||
#else /* OBJC_SPARSE2 */
|
||||
new_buckets[counter] = old_buckets[counter];
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
/* reset entries above old_max_index to empty_bucket */
|
||||
for(counter = old_max_index+1; counter <= new_max_index; counter++)
|
||||
new_indices[counter] = array->empty_index;
|
||||
#else /* OBJC_SPARSE2 */
|
||||
/* reset entries above old_max_index to empty_bucket */
|
||||
for(counter = old_max_index+1; counter <= new_max_index; counter++)
|
||||
new_buckets[counter] = array->empty_bucket;
|
||||
#endif
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
/* install the new indices */
|
||||
array->indices = new_indices;
|
||||
#else /* OBJC_SPARSE2 */
|
||||
array->buckets = new_buckets;
|
||||
#endif
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
/* free the old indices */
|
||||
sarray_free_garbage(old_indices);
|
||||
#else /* OBJC_SPARSE2 */
|
||||
sarray_free_garbage(old_buckets);
|
||||
#endif
|
||||
|
||||
idxsize += (new_max_index-old_max_index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Free a sparse array allocated with sarray_new */
|
||||
|
||||
void
|
||||
sarray_free(struct sarray* array) {
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
size_t old_max_index = (array->capacity-1)/INDEX_CAPACITY;
|
||||
struct sindex ** old_indices;
|
||||
#else
|
||||
size_t old_max_index = (array->capacity-1)/BUCKET_SIZE;
|
||||
struct sbucket ** old_buckets;
|
||||
#endif
|
||||
int counter = 0;
|
||||
|
||||
assert(array->ref_count != 0); /* Freed multiple times!!! */
|
||||
|
||||
if(--(array->ref_count) != 0) /* There exists copies of me */
|
||||
return;
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
old_indices = array->indices;
|
||||
#else
|
||||
old_buckets = array->buckets;
|
||||
#endif
|
||||
|
||||
if((array->is_copy_of) && ((array->is_copy_of->ref_count - 1) == 0))
|
||||
sarray_free(array->is_copy_of);
|
||||
|
||||
/* Free all entries that do not point to empty_bucket */
|
||||
for(counter = 0; counter <= old_max_index; counter++ ) {
|
||||
#ifdef OBJC_SPARSE3
|
||||
struct sindex* idx = old_indices[counter];
|
||||
if((idx != array->empty_index) &&
|
||||
(idx->version.version == array->version.version)) {
|
||||
int c2;
|
||||
for(c2=0; c2<INDEX_SIZE; c2++) {
|
||||
struct sbucket* bkt = idx->buckets[c2];
|
||||
if((bkt != array->empty_bucket) &&
|
||||
(bkt->version.version == array->version.version))
|
||||
{
|
||||
sarray_free_garbage(bkt);
|
||||
nbuckets -= 1;
|
||||
}
|
||||
}
|
||||
sarray_free_garbage(idx);
|
||||
nindices -= 1;
|
||||
}
|
||||
#else /* OBJC_SPARSE2 */
|
||||
struct sbucket* bkt = array->buckets[counter];
|
||||
if ((bkt != array->empty_bucket) &&
|
||||
(bkt->version.version == array->version.version))
|
||||
{
|
||||
sarray_free_garbage(bkt);
|
||||
nbuckets -= 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
/* free empty_index */
|
||||
if(array->empty_index->version.version == array->version.version) {
|
||||
sarray_free_garbage(array->empty_index);
|
||||
nindices -= 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* free empty_bucket */
|
||||
if(array->empty_bucket->version.version == array->version.version) {
|
||||
sarray_free_garbage(array->empty_bucket);
|
||||
nbuckets -= 1;
|
||||
}
|
||||
idxsize -= (old_max_index+1);
|
||||
narrays -= 1;
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
/* free bucket table */
|
||||
sarray_free_garbage(array->indices);
|
||||
|
||||
#else
|
||||
/* free bucket table */
|
||||
sarray_free_garbage(array->buckets);
|
||||
|
||||
#endif
|
||||
|
||||
/* free array */
|
||||
sarray_free_garbage(array);
|
||||
}
|
||||
|
||||
/* This is a lazy copy. Only the core of the structure is actually */
|
||||
/* copied. */
|
||||
|
||||
struct sarray*
|
||||
sarray_lazy_copy(struct sarray* oarr)
|
||||
{
|
||||
struct sarray* arr;
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
size_t num_indices = ((oarr->capacity-1)/INDEX_CAPACITY)+1;
|
||||
struct sindex ** new_indices;
|
||||
#else /* OBJC_SPARSE2 */
|
||||
size_t num_indices = ((oarr->capacity-1)/BUCKET_SIZE)+1;
|
||||
struct sbucket ** new_buckets;
|
||||
#endif
|
||||
|
||||
/* Allocate core array */
|
||||
arr = (struct sarray*) objc_malloc(sizeof(struct sarray)); /* !!! */
|
||||
arr->version.version = oarr->version.version + 1;
|
||||
#ifdef OBJC_SPARSE3
|
||||
arr->empty_index = oarr->empty_index;
|
||||
#endif
|
||||
arr->empty_bucket = oarr->empty_bucket;
|
||||
arr->ref_count = 1;
|
||||
oarr->ref_count += 1;
|
||||
arr->is_copy_of = oarr;
|
||||
arr->capacity = oarr->capacity;
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
/* Copy bucket table */
|
||||
new_indices = (struct sindex**)
|
||||
objc_malloc(sizeof(struct sindex*)*num_indices);
|
||||
memcpy( new_indices,oarr->indices,
|
||||
sizeof(struct sindex*)*num_indices);
|
||||
arr->indices = new_indices;
|
||||
#else
|
||||
/* Copy bucket table */
|
||||
new_buckets = (struct sbucket**)
|
||||
objc_malloc(sizeof(struct sbucket*)*num_indices);
|
||||
memcpy( new_buckets,oarr->buckets,
|
||||
sizeof(struct sbucket*)*num_indices);
|
||||
arr->buckets = new_buckets;
|
||||
#endif
|
||||
|
||||
idxsize += num_indices;
|
||||
narrays += 1;
|
||||
|
||||
return arr;
|
||||
}
|
237
libobjc/sarray.h
Normal file
237
libobjc/sarray.h
Normal file
@ -0,0 +1,237 @@
|
||||
/* Sparse Arrays for Objective C dispatch tables
|
||||
Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
|
||||
Contributed by Kresten Krab Thorup.
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files
|
||||
compiled with GCC to produce an executable, this does not cause
|
||||
the resulting executable to be covered by the GNU General Public License.
|
||||
This exception does not however invalidate any other reasons why
|
||||
the executable file might be covered by the GNU General Public License. */
|
||||
|
||||
#ifndef __sarray_INCLUDE_GNU
|
||||
#define __sarray_INCLUDE_GNU
|
||||
|
||||
#define OBJC_SPARSE2 /* 2-level sparse array */
|
||||
/* #define OBJC_SPARSE3 */ /* 3-level sparse array */
|
||||
|
||||
#ifdef OBJC_SPARSE2
|
||||
extern const char* __objc_sparse2_id;
|
||||
#endif
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
extern const char* __objc_sparse3_id;
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "objc/thr.h"
|
||||
|
||||
extern int nbuckets; /* for stats */
|
||||
extern int nindices;
|
||||
extern int narrays;
|
||||
extern int idxsize;
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/* An unsigned integer of same size as a pointer */
|
||||
#define SIZET_BITS (sizeof(size_t)*8)
|
||||
|
||||
#if defined(__sparc__) || defined(OBJC_SPARSE2)
|
||||
#define PRECOMPUTE_SELECTORS
|
||||
#endif
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
|
||||
/* Buckets are 8 words each */
|
||||
#define BUCKET_BITS 3
|
||||
#define BUCKET_SIZE (1<<BUCKET_BITS)
|
||||
#define BUCKET_MASK (BUCKET_SIZE-1)
|
||||
|
||||
/* Indices are 16 words each */
|
||||
#define INDEX_BITS 4
|
||||
#define INDEX_SIZE (1<<INDEX_BITS)
|
||||
#define INDEX_MASK (INDEX_SIZE-1)
|
||||
|
||||
#define INDEX_CAPACITY (BUCKET_SIZE*INDEX_SIZE)
|
||||
|
||||
#else /* OBJC_SPARSE2 */
|
||||
|
||||
/* Buckets are 32 words each */
|
||||
#define BUCKET_BITS 5
|
||||
#define BUCKET_SIZE (1<<BUCKET_BITS)
|
||||
#define BUCKET_MASK (BUCKET_SIZE-1)
|
||||
|
||||
#endif /* OBJC_SPARSE2 */
|
||||
|
||||
typedef size_t sidx;
|
||||
|
||||
#ifdef PRECOMPUTE_SELECTORS
|
||||
|
||||
struct soffset {
|
||||
#ifdef OBJC_SPARSE3
|
||||
unsigned int unused : SIZET_BITS/4;
|
||||
unsigned int eoffset : SIZET_BITS/4;
|
||||
unsigned int boffset : SIZET_BITS/4;
|
||||
unsigned int ioffset : SIZET_BITS/4;
|
||||
#else /* OBJC_SPARSE2 */
|
||||
#ifdef __sparc__
|
||||
unsigned int boffset : (SIZET_BITS - 2) - BUCKET_BITS;
|
||||
unsigned int eoffset : BUCKET_BITS;
|
||||
unsigned int unused : 2;
|
||||
#else
|
||||
unsigned int boffset : SIZET_BITS/2;
|
||||
unsigned int eoffset : SIZET_BITS/2;
|
||||
#endif
|
||||
#endif /* OBJC_SPARSE2 */
|
||||
};
|
||||
|
||||
union sofftype {
|
||||
struct soffset off;
|
||||
sidx idx;
|
||||
};
|
||||
|
||||
#endif /* not PRECOMPUTE_SELECTORS */
|
||||
|
||||
union sversion {
|
||||
int version;
|
||||
void *next_free;
|
||||
};
|
||||
|
||||
struct sbucket {
|
||||
void* elems[BUCKET_SIZE]; /* elements stored in array */
|
||||
union sversion version; /* used for copy-on-write */
|
||||
};
|
||||
|
||||
#ifdef OBJC_SPARSE3
|
||||
|
||||
struct sindex {
|
||||
struct sbucket* buckets[INDEX_SIZE];
|
||||
union sversion version; /* used for copy-on-write */
|
||||
};
|
||||
|
||||
#endif /* OBJC_SPARSE3 */
|
||||
|
||||
struct sarray {
|
||||
#ifdef OBJC_SPARSE3
|
||||
struct sindex** indices;
|
||||
struct sindex* empty_index;
|
||||
#else /* OBJC_SPARSE2 */
|
||||
struct sbucket** buckets;
|
||||
#endif /* OBJC_SPARSE2 */
|
||||
struct sbucket* empty_bucket;
|
||||
union sversion version; /* used for copy-on-write */
|
||||
short ref_count;
|
||||
struct sarray* is_copy_of;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
struct sarray* sarray_new(int, void* default_element);
|
||||
void sarray_free(struct sarray*);
|
||||
struct sarray* sarray_lazy_copy(struct sarray*);
|
||||
void sarray_realloc(struct sarray*, int new_size);
|
||||
void sarray_at_put(struct sarray*, sidx index, void* elem);
|
||||
void sarray_at_put_safe(struct sarray*, sidx index, void* elem);
|
||||
|
||||
struct sarray* sarray_hard_copy(struct sarray*); /* ... like the name? */
|
||||
void sarray_remove_garbage(void);
|
||||
|
||||
|
||||
#ifdef PRECOMPUTE_SELECTORS
|
||||
/* Transform soffset values to ints and vica verca */
|
||||
static inline unsigned int
|
||||
soffset_decode(sidx index)
|
||||
{
|
||||
union sofftype x;
|
||||
x.idx = index;
|
||||
#ifdef OBJC_SPARSE3
|
||||
return x.off.eoffset
|
||||
+ (x.off.boffset*BUCKET_SIZE)
|
||||
+ (x.off.ioffset*INDEX_CAPACITY);
|
||||
#else /* OBJC_SPARSE2 */
|
||||
return x.off.eoffset + (x.off.boffset*BUCKET_SIZE);
|
||||
#endif /* OBJC_SPARSE2 */
|
||||
}
|
||||
|
||||
static inline sidx
|
||||
soffset_encode(size_t offset)
|
||||
{
|
||||
union sofftype x;
|
||||
x.off.eoffset = offset%BUCKET_SIZE;
|
||||
#ifdef OBJC_SPARSE3
|
||||
x.off.boffset = (offset/BUCKET_SIZE)%INDEX_SIZE;
|
||||
x.off.ioffset = offset/INDEX_CAPACITY;
|
||||
#else /* OBJC_SPARSE2 */
|
||||
x.off.boffset = offset/BUCKET_SIZE;
|
||||
#endif
|
||||
return (sidx)x.idx;
|
||||
}
|
||||
|
||||
#else /* not PRECOMPUTE_SELECTORS */
|
||||
|
||||
static inline size_t
|
||||
soffset_decode(sidx index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
static inline sidx
|
||||
soffset_encode(size_t offset)
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
#endif /* not PRECOMPUTE_SELECTORS */
|
||||
|
||||
/* Get element from the Sparse array `array' at offset `index' */
|
||||
|
||||
static inline void* sarray_get(struct sarray* array, sidx index)
|
||||
{
|
||||
#ifdef PRECOMPUTE_SELECTORS
|
||||
union sofftype x;
|
||||
x.idx = index;
|
||||
#ifdef OBJC_SPARSE3
|
||||
return
|
||||
array->
|
||||
indices[x.off.ioffset]->
|
||||
buckets[x.off.boffset]->
|
||||
elems[x.off.eoffset];
|
||||
#else /* OBJC_SPARSE2 */
|
||||
return array->buckets[x.off.boffset]->elems[x.off.eoffset];
|
||||
#endif /* OBJC_SPARSE2 */
|
||||
#else /* not PRECOMPUTE_SELECTORS */
|
||||
#ifdef OBJC_SPARSE3
|
||||
return array->
|
||||
indices[index/INDEX_CAPACITY]->
|
||||
buckets[(index/BUCKET_SIZE)%INDEX_SIZE]->
|
||||
elems[index%BUCKET_SIZE];
|
||||
#else /* OBJC_SPARSE2 */
|
||||
return array->buckets[index/BUCKET_SIZE]->elems[index%BUCKET_SIZE];
|
||||
#endif /* not OBJC_SPARSE3 */
|
||||
#endif /* not PRECOMPUTE_SELECTORS */
|
||||
}
|
||||
|
||||
static inline void* sarray_get_safe(struct sarray* array, sidx index)
|
||||
{
|
||||
if(soffset_decode(index) < array->capacity)
|
||||
return sarray_get(array, index);
|
||||
else
|
||||
return (array->empty_bucket->elems[0]);
|
||||
}
|
||||
|
||||
#endif /* __sarray_INCLUDE_GNU */
|
458
libobjc/selector.c
Normal file
458
libobjc/selector.c
Normal file
@ -0,0 +1,458 @@
|
||||
/* GNU Objective C Runtime selector related functions
|
||||
Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Kresten Krab Thorup
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include "runtime.h"
|
||||
#include "objc/sarray.h"
|
||||
#include "encoding.h"
|
||||
|
||||
/* Initial selector hash table size. Value doesn't matter much */
|
||||
#define SELECTOR_HASH_SIZE 128
|
||||
|
||||
/* Tables mapping selector names to uid and opposite */
|
||||
static struct sarray* __objc_selector_array = 0; /* uid -> sel !T:MUTEX */
|
||||
static struct sarray* __objc_selector_names = 0; /* uid -> name !T:MUTEX */
|
||||
static cache_ptr __objc_selector_hash = 0; /* name -> uid !T:MUTEX */
|
||||
|
||||
static void register_selectors_from_list(MethodList_t);
|
||||
|
||||
/* Number of selectors stored in each of the above tables */
|
||||
int __objc_selector_max_index = 0; /* !T:MUTEX */
|
||||
|
||||
void __objc_init_selector_tables()
|
||||
{
|
||||
__objc_selector_array = sarray_new (SELECTOR_HASH_SIZE, 0);
|
||||
__objc_selector_names = sarray_new (SELECTOR_HASH_SIZE, 0);
|
||||
__objc_selector_hash
|
||||
= hash_new (SELECTOR_HASH_SIZE,
|
||||
(hash_func_type) hash_string,
|
||||
(compare_func_type) compare_strings);
|
||||
}
|
||||
|
||||
/* This routine is given a class and records all of the methods in its class
|
||||
structure in the record table. */
|
||||
void
|
||||
__objc_register_selectors_from_class (Class class)
|
||||
{
|
||||
MethodList_t method_list;
|
||||
|
||||
method_list = class->methods;
|
||||
while (method_list)
|
||||
{
|
||||
register_selectors_from_list (method_list);
|
||||
method_list = method_list->method_next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* This routine is given a list of methods and records each of the methods in
|
||||
the record table. This is the routine that does the actual recording
|
||||
work.
|
||||
|
||||
This one is only called for Class objects. For categories,
|
||||
class_add_method_list is called.
|
||||
*/
|
||||
static void
|
||||
register_selectors_from_list (MethodList_t method_list)
|
||||
{
|
||||
int i = 0;
|
||||
while (i < method_list->method_count)
|
||||
{
|
||||
Method_t method = &method_list->method_list[i];
|
||||
method->method_name
|
||||
= sel_register_typed_name ((const char*)method->method_name,
|
||||
method->method_types);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Register instance methods as class methods for root classes */
|
||||
void __objc_register_instance_methods_to_class(Class class)
|
||||
{
|
||||
MethodList_t method_list;
|
||||
MethodList_t class_method_list;
|
||||
int max_methods_no = 16;
|
||||
MethodList_t new_list;
|
||||
Method_t curr_method;
|
||||
|
||||
/* Only if a root class. */
|
||||
if(class->super_class)
|
||||
return;
|
||||
|
||||
/* Allocate a method list to hold the new class methods */
|
||||
new_list = objc_calloc(sizeof(struct objc_method_list)
|
||||
+ sizeof(struct objc_method[max_methods_no]), 1);
|
||||
method_list = class->methods;
|
||||
class_method_list = class->class_pointer->methods;
|
||||
curr_method = &new_list->method_list[0];
|
||||
|
||||
/* Iterate through the method lists for the class */
|
||||
while (method_list)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Iterate through the methods from this method list */
|
||||
for (i = 0; i < method_list->method_count; i++)
|
||||
{
|
||||
Method_t mth = &method_list->method_list[i];
|
||||
if (mth->method_name
|
||||
&& !search_for_method_in_list (class_method_list,
|
||||
mth->method_name))
|
||||
{
|
||||
/* This instance method isn't a class method.
|
||||
Add it into the new_list. */
|
||||
*curr_method = *mth;
|
||||
|
||||
/* Reallocate the method list if necessary */
|
||||
if(++new_list->method_count == max_methods_no)
|
||||
new_list =
|
||||
objc_realloc(new_list, sizeof(struct objc_method_list)
|
||||
+ sizeof(struct
|
||||
objc_method[max_methods_no += 16]));
|
||||
curr_method = &new_list->method_list[new_list->method_count];
|
||||
}
|
||||
}
|
||||
|
||||
method_list = method_list->method_next;
|
||||
}
|
||||
|
||||
/* If we created any new class methods
|
||||
then attach the method list to the class */
|
||||
if (new_list->method_count)
|
||||
{
|
||||
new_list =
|
||||
objc_realloc(new_list, sizeof(struct objc_method_list)
|
||||
+ sizeof(struct objc_method[new_list->method_count]));
|
||||
new_list->method_next = class->class_pointer->methods;
|
||||
class->class_pointer->methods = new_list;
|
||||
}
|
||||
|
||||
__objc_update_dispatch_table_for_class (class->class_pointer);
|
||||
}
|
||||
|
||||
|
||||
/* Returns YES iff t1 and t2 have same method types, but we ignore
|
||||
the argframe layout */
|
||||
BOOL
|
||||
sel_types_match (const char* t1, const char* t2)
|
||||
{
|
||||
if (!t1 || !t2)
|
||||
return NO;
|
||||
while (*t1 && *t2)
|
||||
{
|
||||
if (*t1 == '+') t1++;
|
||||
if (*t2 == '+') t2++;
|
||||
while (isdigit(*t1)) t1++;
|
||||
while (isdigit(*t2)) t2++;
|
||||
/* xxx Remove these next two lines when qualifiers are put in
|
||||
all selectors, not just Protocol selectors. */
|
||||
t1 = objc_skip_type_qualifiers(t1);
|
||||
t2 = objc_skip_type_qualifiers(t2);
|
||||
if (!*t1 && !*t2)
|
||||
return YES;
|
||||
if (*t1 != *t2)
|
||||
return NO;
|
||||
t1++;
|
||||
t2++;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
/* return selector representing name */
|
||||
SEL
|
||||
sel_get_typed_uid (const char *name, const char *types)
|
||||
{
|
||||
struct objc_list *l;
|
||||
sidx i;
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
i = (sidx) hash_value_for_key (__objc_selector_hash, name);
|
||||
if (i == 0)
|
||||
{
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (l = (struct objc_list*)sarray_get_safe (__objc_selector_array, i);
|
||||
l; l = l->tail)
|
||||
{
|
||||
SEL s = (SEL)l->head;
|
||||
if (types == 0 || s->sel_types == 0)
|
||||
{
|
||||
if (s->sel_types == types)
|
||||
{
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
else if (sel_types_match (s->sel_types, types))
|
||||
{
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return selector representing name; prefer a selector with non-NULL type */
|
||||
SEL
|
||||
sel_get_any_typed_uid (const char *name)
|
||||
{
|
||||
struct objc_list *l;
|
||||
sidx i;
|
||||
SEL s = NULL;
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
i = (sidx) hash_value_for_key (__objc_selector_hash, name);
|
||||
if (i == 0)
|
||||
{
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (l = (struct objc_list*)sarray_get_safe (__objc_selector_array, i);
|
||||
l; l = l->tail)
|
||||
{
|
||||
s = (SEL) l->head;
|
||||
if (s->sel_types)
|
||||
{
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
return s;
|
||||
}
|
||||
|
||||
/* return selector representing name */
|
||||
SEL
|
||||
sel_get_any_uid (const char *name)
|
||||
{
|
||||
struct objc_list *l;
|
||||
sidx i;
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
i = (sidx) hash_value_for_key (__objc_selector_hash, name);
|
||||
if (soffset_decode (i) == 0)
|
||||
{
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
l = (struct objc_list*)sarray_get_safe (__objc_selector_array, i);
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
|
||||
if (l == 0)
|
||||
return 0;
|
||||
|
||||
return (SEL)l->head;
|
||||
}
|
||||
|
||||
/* return selector representing name */
|
||||
SEL
|
||||
sel_get_uid (const char *name)
|
||||
{
|
||||
return sel_register_typed_name (name, 0);
|
||||
}
|
||||
|
||||
/* Get name of selector. If selector is unknown, the empty string ""
|
||||
is returned */
|
||||
const char*
|
||||
sel_get_name (SEL selector)
|
||||
{
|
||||
const char *ret;
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
if ((soffset_decode((sidx)selector->sel_id) > 0)
|
||||
&& (soffset_decode((sidx)selector->sel_id) <= __objc_selector_max_index))
|
||||
ret = sarray_get_safe (__objc_selector_names, (sidx) selector->sel_id);
|
||||
else
|
||||
ret = 0;
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
BOOL
|
||||
sel_is_mapped (SEL selector)
|
||||
{
|
||||
unsigned int idx = soffset_decode ((sidx)selector->sel_id);
|
||||
return ((idx > 0) && (idx <= __objc_selector_max_index));
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
sel_get_type (SEL selector)
|
||||
{
|
||||
if (selector)
|
||||
return selector->sel_types;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The uninstalled dispatch table */
|
||||
extern struct sarray* __objc_uninstalled_dtable;
|
||||
|
||||
/* Store the passed selector name in the selector record and return its
|
||||
selector value (value returned by sel_get_uid).
|
||||
Assumes that the calling function has locked down __objc_runtime_mutex. */
|
||||
/* is_const parameter tells us if the name and types parameters
|
||||
are really constant or not. If YES then they are constant and
|
||||
we can just store the pointers. If NO then we need to copy
|
||||
name and types because the pointers may disappear later on. */
|
||||
SEL
|
||||
__sel_register_typed_name (const char *name, const char *types,
|
||||
struct objc_selector *orig, BOOL is_const)
|
||||
{
|
||||
struct objc_selector* j;
|
||||
sidx i;
|
||||
struct objc_list *l;
|
||||
|
||||
i = (sidx) hash_value_for_key (__objc_selector_hash, name);
|
||||
if (soffset_decode (i) != 0)
|
||||
{
|
||||
for (l = (struct objc_list*)sarray_get_safe (__objc_selector_array, i);
|
||||
l; l = l->tail)
|
||||
{
|
||||
SEL s = (SEL)l->head;
|
||||
if (types == 0 || s->sel_types == 0)
|
||||
{
|
||||
if (s->sel_types == types)
|
||||
{
|
||||
if (orig)
|
||||
{
|
||||
orig->sel_id = (void*)i;
|
||||
return orig;
|
||||
}
|
||||
else
|
||||
return s;
|
||||
}
|
||||
}
|
||||
else if (!strcmp (s->sel_types, types))
|
||||
{
|
||||
if (orig)
|
||||
{
|
||||
orig->sel_id = (void*)i;
|
||||
return orig;
|
||||
}
|
||||
else
|
||||
return s;
|
||||
}
|
||||
}
|
||||
if (orig)
|
||||
j = orig;
|
||||
else
|
||||
j = objc_malloc (sizeof (struct objc_selector));
|
||||
|
||||
j->sel_id = (void*)i;
|
||||
/* Can we use the pointer or must copy types? Don't copy if NULL */
|
||||
if ((is_const) || (types == 0))
|
||||
j->sel_types = (const char*)types;
|
||||
else {
|
||||
j->sel_types = (char *) objc_malloc(strlen(types)+1);
|
||||
strcpy((char *)j->sel_types, types);
|
||||
}
|
||||
l = (struct objc_list*)sarray_get_safe (__objc_selector_array, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
__objc_selector_max_index += 1;
|
||||
i = soffset_encode(__objc_selector_max_index);
|
||||
if (orig)
|
||||
j = orig;
|
||||
else
|
||||
j = objc_malloc (sizeof (struct objc_selector));
|
||||
|
||||
j->sel_id = (void*)i;
|
||||
/* Can we use the pointer or must copy types? Don't copy if NULL */
|
||||
if ((is_const) || (types == 0))
|
||||
j->sel_types = (const char*)types;
|
||||
else {
|
||||
j->sel_types = (char *) objc_malloc(strlen(types)+1);
|
||||
strcpy((char *)j->sel_types, types);
|
||||
}
|
||||
l = 0;
|
||||
}
|
||||
|
||||
DEBUG_PRINTF ("Record selector %s[%s] as: %ld\n", name, types,
|
||||
soffset_decode (i));
|
||||
|
||||
{
|
||||
int is_new = (l == 0);
|
||||
const char *new_name;
|
||||
|
||||
/* Can we use the pointer or must copy name? Don't copy if NULL */
|
||||
if ((is_const) || (name == 0))
|
||||
new_name = name;
|
||||
else {
|
||||
new_name = (char *) objc_malloc(strlen(name)+1);
|
||||
strcpy((char *)new_name, name);
|
||||
}
|
||||
|
||||
l = list_cons ((void*)j, l);
|
||||
sarray_at_put_safe (__objc_selector_names, i, (void *) new_name);
|
||||
sarray_at_put_safe (__objc_selector_array, i, (void *) l);
|
||||
if (is_new)
|
||||
hash_add (&__objc_selector_hash, (void *) new_name, (void *) i);
|
||||
}
|
||||
|
||||
sarray_realloc(__objc_uninstalled_dtable, __objc_selector_max_index+1);
|
||||
|
||||
return (SEL) j;
|
||||
}
|
||||
|
||||
SEL
|
||||
sel_register_name (const char *name)
|
||||
{
|
||||
SEL ret;
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
/* Assume that name is not constant static memory and needs to be
|
||||
copied before put into a runtime structure. is_const == NO */
|
||||
ret = __sel_register_typed_name (name, 0, 0, NO);
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SEL
|
||||
sel_register_typed_name (const char *name, const char *type)
|
||||
{
|
||||
SEL ret;
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
/* Assume that name and type are not constant static memory and need to
|
||||
be copied before put into a runtime structure. is_const == NO */
|
||||
ret = __sel_register_typed_name (name, type, 0, NO);
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
648
libobjc/sendmsg.c
Normal file
648
libobjc/sendmsg.c
Normal file
@ -0,0 +1,648 @@
|
||||
/* GNU Objective C Runtime message lookup
|
||||
Copyright (C) 1993, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
|
||||
Contributed by Kresten Krab Thorup
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include "../tconfig.h"
|
||||
#include "runtime.h"
|
||||
#include "sarray.h"
|
||||
#include "encoding.h"
|
||||
#include "runtime-info.h"
|
||||
|
||||
/* this is how we hack STRUCT_VALUE to be 1 or 0 */
|
||||
#define gen_rtx(args...) 1
|
||||
#define gen_rtx_MEM(args...) 1
|
||||
#define rtx int
|
||||
|
||||
#if !defined(STRUCT_VALUE) || STRUCT_VALUE == 0
|
||||
#define INVISIBLE_STRUCT_RETURN 1
|
||||
#else
|
||||
#define INVISIBLE_STRUCT_RETURN 0
|
||||
#endif
|
||||
|
||||
/* The uninstalled dispatch table */
|
||||
struct sarray* __objc_uninstalled_dtable = 0; /* !T:MUTEX */
|
||||
|
||||
/* Send +initialize to class */
|
||||
static void __objc_send_initialize(Class);
|
||||
|
||||
static void __objc_install_dispatch_table_for_class (Class);
|
||||
|
||||
/* Forward declare some functions */
|
||||
static void __objc_init_install_dtable(id, SEL);
|
||||
|
||||
/* Various forwarding functions that are used based upon the
|
||||
return type for the selector.
|
||||
__objc_block_forward for structures.
|
||||
__objc_double_forward for floats/doubles.
|
||||
__objc_word_forward for pointers or types that fit in registers.
|
||||
*/
|
||||
static double __objc_double_forward(id, SEL, ...);
|
||||
static id __objc_word_forward(id, SEL, ...);
|
||||
typedef struct { id many[8]; } __big;
|
||||
#if INVISIBLE_STRUCT_RETURN
|
||||
static __big
|
||||
#else
|
||||
static id
|
||||
#endif
|
||||
__objc_block_forward(id, SEL, ...);
|
||||
static Method_t search_for_method_in_hierarchy (Class class, SEL sel);
|
||||
Method_t search_for_method_in_list(MethodList_t list, SEL op);
|
||||
id nil_method(id, SEL, ...);
|
||||
|
||||
/* Given a selector, return the proper forwarding implementation. */
|
||||
__inline__
|
||||
IMP
|
||||
__objc_get_forward_imp (SEL sel)
|
||||
{
|
||||
const char *t = sel->sel_types;
|
||||
|
||||
if (t && (*t == '[' || *t == '(' || *t == '{')
|
||||
#ifdef OBJC_MAX_STRUCT_BY_VALUE
|
||||
&& objc_sizeof_type(t) > OBJC_MAX_STRUCT_BY_VALUE
|
||||
#endif
|
||||
)
|
||||
return (IMP)__objc_block_forward;
|
||||
else if (t && (*t == 'f' || *t == 'd'))
|
||||
return (IMP)__objc_double_forward;
|
||||
else
|
||||
return (IMP)__objc_word_forward;
|
||||
}
|
||||
|
||||
/* Given a class and selector, return the selector's implementation. */
|
||||
__inline__
|
||||
IMP
|
||||
get_imp (Class class, SEL sel)
|
||||
{
|
||||
void* res = sarray_get_safe (class->dtable, (size_t) sel->sel_id);
|
||||
if (res == 0)
|
||||
{
|
||||
/* Not a valid method */
|
||||
if(class->dtable == __objc_uninstalled_dtable)
|
||||
{
|
||||
/* The dispatch table needs to be installed. */
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
__objc_install_dispatch_table_for_class (class);
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
/* Call ourselves with the installed dispatch table
|
||||
and get the real method */
|
||||
res = get_imp(class, sel);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The dispatch table has been installed so the
|
||||
method just doesn't exist for the class.
|
||||
Return the forwarding implementation. */
|
||||
res = __objc_get_forward_imp(sel);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Query if an object can respond to a selector, returns YES if the
|
||||
object implements the selector otherwise NO. Does not check if the
|
||||
method can be forwarded. */
|
||||
__inline__
|
||||
BOOL
|
||||
__objc_responds_to (id object, SEL sel)
|
||||
{
|
||||
void* res;
|
||||
|
||||
/* Install dispatch table if need be */
|
||||
if (object->class_pointer->dtable == __objc_uninstalled_dtable)
|
||||
{
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
__objc_install_dispatch_table_for_class (object->class_pointer);
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
}
|
||||
|
||||
/* Get the method from the dispatch table */
|
||||
res = sarray_get_safe (object->class_pointer->dtable, (size_t) sel->sel_id);
|
||||
return (res != 0);
|
||||
}
|
||||
|
||||
/* This is the lookup function. All entries in the table are either a
|
||||
valid method *or* zero. If zero then either the dispatch table
|
||||
needs to be installed or it doesn't exist and forwarding is attempted. */
|
||||
__inline__
|
||||
IMP
|
||||
objc_msg_lookup(id receiver, SEL op)
|
||||
{
|
||||
IMP result;
|
||||
if(receiver)
|
||||
{
|
||||
result = sarray_get_safe (receiver->class_pointer->dtable,
|
||||
(sidx)op->sel_id);
|
||||
if (result == 0)
|
||||
{
|
||||
/* Not a valid method */
|
||||
if(receiver->class_pointer->dtable == __objc_uninstalled_dtable)
|
||||
{
|
||||
/* The dispatch table needs to be installed.
|
||||
This happens on the very first method call to the class. */
|
||||
__objc_init_install_dtable(receiver, op);
|
||||
|
||||
/* Get real method for this in newly installed dtable */
|
||||
result = get_imp(receiver->class_pointer, op);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The dispatch table has been installed so the
|
||||
method just doesn't exist for the class.
|
||||
Attempt to forward the method. */
|
||||
result = __objc_get_forward_imp(op);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
return nil_method;
|
||||
}
|
||||
|
||||
IMP
|
||||
objc_msg_lookup_super (Super_t super, SEL sel)
|
||||
{
|
||||
if (super->self)
|
||||
return get_imp (super->class, sel);
|
||||
else
|
||||
return nil_method;
|
||||
}
|
||||
|
||||
int method_get_sizeof_arguments (Method*);
|
||||
|
||||
retval_t
|
||||
objc_msg_sendv(id object, SEL op, arglist_t arg_frame)
|
||||
{
|
||||
Method* m = class_get_instance_method(object->class_pointer, op);
|
||||
const char *type;
|
||||
*((id*)method_get_first_argument (m, arg_frame, &type)) = object;
|
||||
*((SEL*)method_get_next_argument (arg_frame, &type)) = op;
|
||||
return __builtin_apply((apply_t)m->method_imp,
|
||||
arg_frame,
|
||||
method_get_sizeof_arguments (m));
|
||||
}
|
||||
|
||||
void
|
||||
__objc_init_dispatch_tables()
|
||||
{
|
||||
__objc_uninstalled_dtable
|
||||
= sarray_new(200, 0);
|
||||
}
|
||||
|
||||
/* This function is called by objc_msg_lookup when the
|
||||
dispatch table needs to be installed; thus it is called once
|
||||
for each class, namely when the very first message is sent to it. */
|
||||
static void
|
||||
__objc_init_install_dtable(id receiver, SEL op)
|
||||
{
|
||||
/* This may happen, if the programmer has taken the address of a
|
||||
method before the dtable was initialized... too bad for him! */
|
||||
if(receiver->class_pointer->dtable != __objc_uninstalled_dtable)
|
||||
return;
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
if(CLS_ISCLASS(receiver->class_pointer))
|
||||
{
|
||||
/* receiver is an ordinary object */
|
||||
assert(CLS_ISCLASS(receiver->class_pointer));
|
||||
|
||||
/* install instance methods table */
|
||||
__objc_install_dispatch_table_for_class (receiver->class_pointer);
|
||||
|
||||
/* call +initialize -- this will in turn install the factory
|
||||
dispatch table if not already done :-) */
|
||||
__objc_send_initialize(receiver->class_pointer);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* receiver is a class object */
|
||||
assert(CLS_ISCLASS((Class)receiver));
|
||||
assert(CLS_ISMETA(receiver->class_pointer));
|
||||
|
||||
/* Install real dtable for factory methods */
|
||||
__objc_install_dispatch_table_for_class (receiver->class_pointer);
|
||||
|
||||
__objc_send_initialize((Class)receiver);
|
||||
}
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
}
|
||||
|
||||
/* Install dummy table for class which causes the first message to
|
||||
that class (or instances hereof) to be initialized properly */
|
||||
void
|
||||
__objc_install_premature_dtable(Class class)
|
||||
{
|
||||
assert(__objc_uninstalled_dtable);
|
||||
class->dtable = __objc_uninstalled_dtable;
|
||||
}
|
||||
|
||||
/* Send +initialize to class if not already done */
|
||||
static void
|
||||
__objc_send_initialize(Class class)
|
||||
{
|
||||
/* This *must* be a class object */
|
||||
assert(CLS_ISCLASS(class));
|
||||
assert(!CLS_ISMETA(class));
|
||||
|
||||
if (!CLS_ISINITIALIZED(class))
|
||||
{
|
||||
CLS_SETINITIALIZED(class);
|
||||
CLS_SETINITIALIZED(class->class_pointer);
|
||||
|
||||
/* Create the garbage collector type memory description */
|
||||
__objc_generate_gc_type_description (class);
|
||||
|
||||
if(class->super_class)
|
||||
__objc_send_initialize(class->super_class);
|
||||
|
||||
{
|
||||
SEL op = sel_register_name ("initialize");
|
||||
IMP imp = 0;
|
||||
MethodList_t method_list = class->class_pointer->methods;
|
||||
|
||||
while (method_list) {
|
||||
int i;
|
||||
Method_t method;
|
||||
|
||||
for (i = 0; i< method_list->method_count; i++) {
|
||||
method = &(method_list->method_list[i]);
|
||||
if (method->method_name
|
||||
&& method->method_name->sel_id == op->sel_id) {
|
||||
imp = method->method_imp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (imp)
|
||||
break;
|
||||
|
||||
method_list = method_list->method_next;
|
||||
|
||||
}
|
||||
if (imp)
|
||||
(*imp)((id)class, op);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Walk on the methods list of class and install the methods in the reverse
|
||||
order of the lists. Since methods added by categories are before the methods
|
||||
of class in the methods list, this allows categories to substitute methods
|
||||
declared in class. However if more than one category replaces the same
|
||||
method nothing is guaranteed about what method will be used.
|
||||
Assumes that __objc_runtime_mutex is locked down. */
|
||||
static void
|
||||
__objc_install_methods_in_dtable (Class class, MethodList_t method_list)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!method_list)
|
||||
return;
|
||||
|
||||
if (method_list->method_next)
|
||||
__objc_install_methods_in_dtable (class, method_list->method_next);
|
||||
|
||||
for (i = 0; i < method_list->method_count; i++)
|
||||
{
|
||||
Method_t method = &(method_list->method_list[i]);
|
||||
sarray_at_put_safe (class->dtable,
|
||||
(sidx) method->method_name->sel_id,
|
||||
method->method_imp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Assumes that __objc_runtime_mutex is locked down. */
|
||||
static void
|
||||
__objc_install_dispatch_table_for_class (Class class)
|
||||
{
|
||||
Class super;
|
||||
|
||||
/* If the class has not yet had its class links resolved, we must
|
||||
re-compute all class links */
|
||||
if(!CLS_ISRESOLV(class))
|
||||
__objc_resolve_class_links();
|
||||
|
||||
super = class->super_class;
|
||||
|
||||
if (super != 0 && (super->dtable == __objc_uninstalled_dtable))
|
||||
__objc_install_dispatch_table_for_class (super);
|
||||
|
||||
/* Allocate dtable if necessary */
|
||||
if (super == 0)
|
||||
{
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
class->dtable = sarray_new (__objc_selector_max_index, 0);
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
}
|
||||
else
|
||||
class->dtable = sarray_lazy_copy (super->dtable);
|
||||
|
||||
__objc_install_methods_in_dtable (class, class->methods);
|
||||
}
|
||||
|
||||
void
|
||||
__objc_update_dispatch_table_for_class (Class class)
|
||||
{
|
||||
Class next;
|
||||
struct sarray *arr;
|
||||
|
||||
/* not yet installed -- skip it */
|
||||
if (class->dtable == __objc_uninstalled_dtable)
|
||||
return;
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
arr = class->dtable;
|
||||
__objc_install_premature_dtable (class); /* someone might require it... */
|
||||
sarray_free (arr); /* release memory */
|
||||
|
||||
/* could have been lazy... */
|
||||
__objc_install_dispatch_table_for_class (class);
|
||||
|
||||
if (class->subclass_list) /* Traverse subclasses */
|
||||
for (next = class->subclass_list; next; next = next->sibling_class)
|
||||
__objc_update_dispatch_table_for_class (next);
|
||||
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
}
|
||||
|
||||
|
||||
/* This function adds a method list to a class. This function is
|
||||
typically called by another function specific to the run-time. As
|
||||
such this function does not worry about thread safe issues.
|
||||
|
||||
This one is only called for categories. Class objects have their
|
||||
methods installed right away, and their selectors are made into
|
||||
SEL's by the function __objc_register_selectors_from_class. */
|
||||
void
|
||||
class_add_method_list (Class class, MethodList_t list)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Passing of a linked list is not allowed. Do multiple calls. */
|
||||
assert (!list->method_next);
|
||||
|
||||
/* Check for duplicates. */
|
||||
for (i = 0; i < list->method_count; ++i)
|
||||
{
|
||||
Method_t method = &list->method_list[i];
|
||||
|
||||
if (method->method_name) /* Sometimes these are NULL */
|
||||
{
|
||||
/* This is where selector names are transmogrified to SEL's */
|
||||
method->method_name =
|
||||
sel_register_typed_name ((const char*)method->method_name,
|
||||
method->method_types);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the methods to the class's method list. */
|
||||
list->method_next = class->methods;
|
||||
class->methods = list;
|
||||
|
||||
/* Update the dispatch table of class */
|
||||
__objc_update_dispatch_table_for_class (class);
|
||||
}
|
||||
|
||||
Method_t
|
||||
class_get_instance_method(Class class, SEL op)
|
||||
{
|
||||
return search_for_method_in_hierarchy(class, op);
|
||||
}
|
||||
|
||||
Method_t
|
||||
class_get_class_method(MetaClass class, SEL op)
|
||||
{
|
||||
return search_for_method_in_hierarchy(class, op);
|
||||
}
|
||||
|
||||
|
||||
/* Search for a method starting from the current class up its hierarchy.
|
||||
Return a pointer to the method's method structure if found. NULL
|
||||
otherwise. */
|
||||
|
||||
static Method_t
|
||||
search_for_method_in_hierarchy (Class cls, SEL sel)
|
||||
{
|
||||
Method_t method = NULL;
|
||||
Class class;
|
||||
|
||||
if (! sel_is_mapped (sel))
|
||||
return NULL;
|
||||
|
||||
/* Scan the method list of the class. If the method isn't found in the
|
||||
list then step to its super class. */
|
||||
for (class = cls; ((! method) && class); class = class->super_class)
|
||||
method = search_for_method_in_list (class->methods, sel);
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Given a linked list of method and a method's name. Search for the named
|
||||
method's method structure. Return a pointer to the method's method
|
||||
structure if found. NULL otherwise. */
|
||||
Method_t
|
||||
search_for_method_in_list (MethodList_t list, SEL op)
|
||||
{
|
||||
MethodList_t method_list = list;
|
||||
|
||||
if (! sel_is_mapped (op))
|
||||
return NULL;
|
||||
|
||||
/* If not found then we'll search the list. */
|
||||
while (method_list)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Search the method list. */
|
||||
for (i = 0; i < method_list->method_count; ++i)
|
||||
{
|
||||
Method_t method = &method_list->method_list[i];
|
||||
|
||||
if (method->method_name)
|
||||
if (method->method_name->sel_id == op->sel_id)
|
||||
return method;
|
||||
}
|
||||
|
||||
/* The method wasn't found. Follow the link to the next list of
|
||||
methods. */
|
||||
method_list = method_list->method_next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static retval_t __objc_forward (id object, SEL sel, arglist_t args);
|
||||
|
||||
/* Forwarding pointers/integers through the normal registers */
|
||||
static id
|
||||
__objc_word_forward (id rcv, SEL op, ...)
|
||||
{
|
||||
void *args, *res;
|
||||
|
||||
args = __builtin_apply_args ();
|
||||
res = __objc_forward (rcv, op, args);
|
||||
if (res)
|
||||
__builtin_return (res);
|
||||
else
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Specific routine for forwarding floats/double because of
|
||||
architectural differences on some processors. i386s for
|
||||
example which uses a floating point stack versus general
|
||||
registers for floating point numbers. This forward routine
|
||||
makes sure that GCC restores the proper return values */
|
||||
static double
|
||||
__objc_double_forward (id rcv, SEL op, ...)
|
||||
{
|
||||
void *args, *res;
|
||||
|
||||
args = __builtin_apply_args ();
|
||||
res = __objc_forward (rcv, op, args);
|
||||
__builtin_return (res);
|
||||
}
|
||||
|
||||
#if INVISIBLE_STRUCT_RETURN
|
||||
static __big
|
||||
#else
|
||||
static id
|
||||
#endif
|
||||
__objc_block_forward (id rcv, SEL op, ...)
|
||||
{
|
||||
void *args, *res;
|
||||
|
||||
args = __builtin_apply_args ();
|
||||
res = __objc_forward (rcv, op, args);
|
||||
if (res)
|
||||
__builtin_return (res);
|
||||
else
|
||||
#if INVISIBLE_STRUCT_RETURN
|
||||
return (__big) {{0, 0, 0, 0, 0, 0, 0, 0}};
|
||||
#else
|
||||
return nil;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* This function is installed in the dispatch table for all methods which are
|
||||
not implemented. Thus, it is called when a selector is not recognized. */
|
||||
static retval_t
|
||||
__objc_forward (id object, SEL sel, arglist_t args)
|
||||
{
|
||||
IMP imp;
|
||||
static SEL frwd_sel = 0; /* !T:SAFE2 */
|
||||
SEL err_sel;
|
||||
|
||||
/* first try if the object understands forward:: */
|
||||
if (!frwd_sel)
|
||||
frwd_sel = sel_get_any_uid("forward::");
|
||||
|
||||
if (__objc_responds_to (object, frwd_sel))
|
||||
{
|
||||
imp = get_imp(object->class_pointer, frwd_sel);
|
||||
return (*imp)(object, frwd_sel, sel, args);
|
||||
}
|
||||
|
||||
/* If the object recognizes the doesNotRecognize: method then we're going
|
||||
to send it. */
|
||||
err_sel = sel_get_any_uid ("doesNotRecognize:");
|
||||
if (__objc_responds_to (object, err_sel))
|
||||
{
|
||||
imp = get_imp (object->class_pointer, err_sel);
|
||||
return (*imp) (object, err_sel, sel);
|
||||
}
|
||||
|
||||
/* The object doesn't recognize the method. Check for responding to
|
||||
error:. If it does then sent it. */
|
||||
{
|
||||
size_t strlen (const char*);
|
||||
char msg[256 + strlen ((const char*)sel_get_name (sel))
|
||||
+ strlen ((const char*)object->class_pointer->name)];
|
||||
|
||||
sprintf (msg, "(%s) %s does not recognize %s",
|
||||
(CLS_ISMETA(object->class_pointer)
|
||||
? "class"
|
||||
: "instance" ),
|
||||
object->class_pointer->name, sel_get_name (sel));
|
||||
|
||||
err_sel = sel_get_any_uid ("error:");
|
||||
if (__objc_responds_to (object, err_sel))
|
||||
{
|
||||
imp = get_imp (object->class_pointer, err_sel);
|
||||
return (*imp) (object, sel_get_any_uid ("error:"), msg);
|
||||
}
|
||||
|
||||
/* The object doesn't respond to doesNotRecognize: or error:; Therefore,
|
||||
a default action is taken. */
|
||||
objc_error (object, OBJC_ERR_UNIMPLEMENTED, "%s\n", msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
__objc_print_dtable_stats()
|
||||
{
|
||||
int total = 0;
|
||||
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
printf("memory usage: (%s)\n",
|
||||
#ifdef OBJC_SPARSE2
|
||||
"2-level sparse arrays"
|
||||
#else
|
||||
"3-level sparse arrays"
|
||||
#endif
|
||||
);
|
||||
|
||||
printf("arrays: %d = %ld bytes\n", narrays,
|
||||
(long)narrays*sizeof(struct sarray));
|
||||
total += narrays*sizeof(struct sarray);
|
||||
printf("buckets: %d = %ld bytes\n", nbuckets,
|
||||
(long)nbuckets*sizeof(struct sbucket));
|
||||
total += nbuckets*sizeof(struct sbucket);
|
||||
|
||||
printf("idxtables: %d = %ld bytes\n", idxsize, (long)idxsize*sizeof(void*));
|
||||
total += idxsize*sizeof(void*);
|
||||
printf("-----------------------------------\n");
|
||||
printf("total: %d bytes\n", total);
|
||||
printf("===================================\n");
|
||||
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
}
|
||||
|
||||
/* Returns the uninstalled dispatch table indicator.
|
||||
If a class' dispatch table points to __objc_uninstalled_dtable
|
||||
then that means it needs its dispatch table to be installed. */
|
||||
__inline__
|
||||
struct sarray*
|
||||
objc_get_uninstalled_dtable()
|
||||
{
|
||||
return __objc_uninstalled_dtable;
|
||||
}
|
281
libobjc/thr-dce.c
Normal file
281
libobjc/thr-dce.c
Normal file
@ -0,0 +1,281 @@
|
||||
/* GNU Objective C Runtime Thread Interface
|
||||
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include <pthread.h>
|
||||
#include <objc/thr.h>
|
||||
#include "runtime.h"
|
||||
|
||||
/* Key structure for maintaining thread specific storage */
|
||||
static pthread_key_t _objc_thread_storage;
|
||||
|
||||
/* Backend initialization functions */
|
||||
|
||||
/* Initialize the threads subsystem. */
|
||||
int
|
||||
__objc_init_thread_system(void)
|
||||
{
|
||||
/* Initialize the thread storage key */
|
||||
return pthread_keycreate(&_objc_thread_storage, NULL);
|
||||
}
|
||||
|
||||
/* Close the threads subsystem. */
|
||||
int
|
||||
__objc_close_thread_system(void)
|
||||
{
|
||||
/* Destroy the thread storage key */
|
||||
/* Not implemented yet */
|
||||
/* return pthread_key_delete(&_objc_thread_storage); */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backend thread functions */
|
||||
|
||||
/* Create a new thread of execution. */
|
||||
objc_thread_t
|
||||
__objc_thread_detach(void (*func)(void *arg), void *arg)
|
||||
{
|
||||
objc_thread_t thread_id;
|
||||
pthread_t new_thread_handle;
|
||||
|
||||
if (pthread_create(&new_thread_handle, pthread_attr_default,
|
||||
(void *)func, arg) == 0)
|
||||
{
|
||||
/* ??? May not work! (64bit) */
|
||||
thread_id = *(objc_thread_t *)&new_thread_handle;
|
||||
pthread_detach(&new_thread_handle); /* Fully detach thread. */
|
||||
}
|
||||
else
|
||||
thread_id = NULL;
|
||||
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
/* Set the current thread's priority. */
|
||||
int
|
||||
__objc_thread_set_priority(int priority)
|
||||
{
|
||||
int sys_priority = 0;
|
||||
|
||||
switch (priority)
|
||||
{
|
||||
case OBJC_THREAD_INTERACTIVE_PRIORITY:
|
||||
sys_priority = (PRI_FG_MIN_NP + PRI_FG_MAX_NP) / 2;
|
||||
break;
|
||||
default:
|
||||
case OBJC_THREAD_BACKGROUND_PRIORITY:
|
||||
sys_priority = (PRI_BG_MIN_NP + PRI_BG_MAX_NP) / 2;
|
||||
break;
|
||||
case OBJC_THREAD_LOW_PRIORITY:
|
||||
sys_priority = (PRI_BG_MIN_NP + PRI_BG_MAX_NP) / 2;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Change the priority. */
|
||||
if (pthread_setprio(pthread_self(), sys_priority) >= 0)
|
||||
return 0;
|
||||
else
|
||||
/* Failed */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return the current thread's priority. */
|
||||
int
|
||||
__objc_thread_get_priority(void)
|
||||
{
|
||||
int sys_priority;
|
||||
|
||||
if ((sys_priority = pthread_getprio(pthread_self())) >= 0) {
|
||||
if (sys_priority >= PRI_FG_MIN_NP && sys_priority <= PRI_FG_MAX_NP)
|
||||
return OBJC_THREAD_INTERACTIVE_PRIORITY;
|
||||
if (sys_priority >= PRI_BG_MIN_NP && sys_priority <= PRI_BG_MAX_NP)
|
||||
return OBJC_THREAD_BACKGROUND_PRIORITY;
|
||||
return OBJC_THREAD_LOW_PRIORITY;
|
||||
}
|
||||
|
||||
/* Failed */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Yield our process time to another thread. */
|
||||
void
|
||||
__objc_thread_yield(void)
|
||||
{
|
||||
pthread_yield();
|
||||
}
|
||||
|
||||
/* Terminate the current thread. */
|
||||
int
|
||||
__objc_thread_exit(void)
|
||||
{
|
||||
/* exit the thread */
|
||||
pthread_exit(&__objc_thread_exit_status);
|
||||
|
||||
/* Failed if we reached here */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Returns an integer value which uniquely describes a thread. */
|
||||
objc_thread_t
|
||||
__objc_thread_id(void)
|
||||
{
|
||||
pthread_t self = pthread_self();
|
||||
|
||||
return (objc_thread_t) pthread_getunique_np (&self);
|
||||
}
|
||||
|
||||
/* Sets the thread's local storage pointer. */
|
||||
int
|
||||
__objc_thread_set_data(void *value)
|
||||
{
|
||||
return pthread_setspecific(_objc_thread_storage, value);
|
||||
}
|
||||
|
||||
/* Returns the thread's local storage pointer. */
|
||||
void *
|
||||
__objc_thread_get_data(void)
|
||||
{
|
||||
void *value = NULL;
|
||||
|
||||
if ( !(pthread_getspecific(_objc_thread_storage, &value)) )
|
||||
return value;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Backend mutex functions */
|
||||
|
||||
/* Allocate a mutex. */
|
||||
int
|
||||
__objc_mutex_allocate(objc_mutex_t mutex)
|
||||
{
|
||||
if (pthread_mutex_init((pthread_mutex_t *)(&(mutex->backend)),
|
||||
pthread_mutexattr_default))
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deallocate a mutex. */
|
||||
int
|
||||
__objc_mutex_deallocate(objc_mutex_t mutex)
|
||||
{
|
||||
if (pthread_mutex_destroy((pthread_mutex_t *)(&(mutex->backend))))
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_lock(objc_mutex_t mutex)
|
||||
{
|
||||
return pthread_mutex_lock((pthread_mutex_t *)(&(mutex->backend)));
|
||||
}
|
||||
|
||||
/* Try to grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_trylock(objc_mutex_t mutex)
|
||||
{
|
||||
if (pthread_mutex_trylock((pthread_mutex_t *)(&(mutex->backend))) != 1)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unlock the mutex */
|
||||
int
|
||||
__objc_mutex_unlock(objc_mutex_t mutex)
|
||||
{
|
||||
return pthread_mutex_unlock((pthread_mutex_t *)(&(mutex->backend)));
|
||||
}
|
||||
|
||||
/* Backend condition mutex functions */
|
||||
|
||||
/* Allocate a condition. */
|
||||
int
|
||||
__objc_condition_allocate(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
|
||||
/*
|
||||
if (pthread_cond_init((pthread_cond_t *)(&(condition->backend)), NULL))
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
*/
|
||||
}
|
||||
|
||||
/* Deallocate a condition. */
|
||||
int
|
||||
__objc_condition_deallocate(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
|
||||
/*
|
||||
return pthread_cond_destroy((pthread_cond_t *)(&(condition->backend)));
|
||||
*/
|
||||
}
|
||||
|
||||
/* Wait on the condition */
|
||||
int
|
||||
__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
|
||||
/*
|
||||
return pthread_cond_wait((pthread_cond_t *)(&(condition->backend)),
|
||||
(pthread_mutex_t *)(&(mutex->backend)));
|
||||
*/
|
||||
}
|
||||
|
||||
/* Wake up all threads waiting on this condition. */
|
||||
int
|
||||
__objc_condition_broadcast(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
|
||||
/*
|
||||
return pthread_cond_broadcast((pthread_cond_t *)(&(condition->backend)));
|
||||
*/
|
||||
}
|
||||
|
||||
/* Wake up one thread waiting on this condition. */
|
||||
int
|
||||
__objc_condition_signal(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
|
||||
/*
|
||||
return pthread_cond_signal((pthread_cond_t *)(&(condition->backend)));
|
||||
*/
|
||||
}
|
||||
|
||||
/* End of File */
|
281
libobjc/thr-decosf1.c
Normal file
281
libobjc/thr-decosf1.c
Normal file
@ -0,0 +1,281 @@
|
||||
/* GNU Objective C Runtime Thread Interface
|
||||
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include <pthread.h>
|
||||
#include <objc/thr.h>
|
||||
#include "runtime.h"
|
||||
|
||||
/* Key structure for maintaining thread specific storage */
|
||||
static pthread_key_t _objc_thread_storage;
|
||||
|
||||
/* Backend initialization functions */
|
||||
|
||||
/* Initialize the threads subsystem. */
|
||||
int
|
||||
__objc_init_thread_system(void)
|
||||
{
|
||||
/* Initialize the thread storage key */
|
||||
return pthread_keycreate(&_objc_thread_storage, NULL);
|
||||
}
|
||||
|
||||
/* Close the threads subsystem. */
|
||||
int
|
||||
__objc_close_thread_system(void)
|
||||
{
|
||||
/* Destroy the thread storage key */
|
||||
/* Not implemented yet */
|
||||
/* return pthread_key_delete(&_objc_thread_storage); */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backend thread functions */
|
||||
|
||||
/* Create a new thread of execution. */
|
||||
objc_thread_t
|
||||
__objc_thread_detach(void (*func)(void *arg), void *arg)
|
||||
{
|
||||
objc_thread_t thread_id;
|
||||
pthread_t new_thread_handle;
|
||||
|
||||
if (pthread_create(&new_thread_handle, pthread_attr_default,
|
||||
(void *)func, arg) == 0)
|
||||
{
|
||||
/* ??? May not work! (64bit) */
|
||||
thread_id = *(objc_thread_t *)&new_thread_handle;
|
||||
pthread_detach(&new_thread_handle); /* Fully detach thread. */
|
||||
}
|
||||
else
|
||||
thread_id = NULL;
|
||||
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
/* Set the current thread's priority. */
|
||||
int
|
||||
__objc_thread_set_priority(int priority)
|
||||
{
|
||||
int sys_priority = 0;
|
||||
|
||||
switch (priority)
|
||||
{
|
||||
case OBJC_THREAD_INTERACTIVE_PRIORITY:
|
||||
sys_priority = (PRI_FG_MIN_NP + PRI_FG_MAX_NP) / 2;
|
||||
break;
|
||||
default:
|
||||
case OBJC_THREAD_BACKGROUND_PRIORITY:
|
||||
sys_priority = (PRI_BG_MIN_NP + PRI_BG_MAX_NP) / 2;
|
||||
break;
|
||||
case OBJC_THREAD_LOW_PRIORITY:
|
||||
sys_priority = (PRI_BG_MIN_NP + PRI_BG_MAX_NP) / 2;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Change the priority. */
|
||||
if (pthread_setprio(pthread_self(), sys_priority) >= 0)
|
||||
return 0;
|
||||
else
|
||||
/* Failed */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return the current thread's priority. */
|
||||
int
|
||||
__objc_thread_get_priority(void)
|
||||
{
|
||||
int sys_priority;
|
||||
|
||||
if ((sys_priority = pthread_getprio(pthread_self())) >= 0) {
|
||||
if (sys_priority >= PRI_FG_MIN_NP && sys_priority <= PRI_FG_MAX_NP)
|
||||
return OBJC_THREAD_INTERACTIVE_PRIORITY;
|
||||
if (sys_priority >= PRI_BG_MIN_NP && sys_priority <= PRI_BG_MAX_NP)
|
||||
return OBJC_THREAD_BACKGROUND_PRIORITY;
|
||||
return OBJC_THREAD_LOW_PRIORITY;
|
||||
}
|
||||
|
||||
/* Failed */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Yield our process time to another thread. */
|
||||
void
|
||||
__objc_thread_yield(void)
|
||||
{
|
||||
pthread_yield();
|
||||
}
|
||||
|
||||
/* Terminate the current thread. */
|
||||
int
|
||||
__objc_thread_exit(void)
|
||||
{
|
||||
/* exit the thread */
|
||||
pthread_exit(&__objc_thread_exit_status);
|
||||
|
||||
/* Failed if we reached here */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Returns an integer value which uniquely describes a thread. */
|
||||
objc_thread_t
|
||||
__objc_thread_id(void)
|
||||
{
|
||||
pthread_t self = pthread_self();
|
||||
|
||||
return (objc_thread_t) pthread_getunique_np (&self);
|
||||
}
|
||||
|
||||
/* Sets the thread's local storage pointer. */
|
||||
int
|
||||
__objc_thread_set_data(void *value)
|
||||
{
|
||||
return pthread_setspecific(_objc_thread_storage, value);
|
||||
}
|
||||
|
||||
/* Returns the thread's local storage pointer. */
|
||||
void *
|
||||
__objc_thread_get_data(void)
|
||||
{
|
||||
void *value = NULL;
|
||||
|
||||
if ( !(pthread_getspecific(_objc_thread_storage, &value)) )
|
||||
return value;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Backend mutex functions */
|
||||
|
||||
/* Allocate a mutex. */
|
||||
int
|
||||
__objc_mutex_allocate(objc_mutex_t mutex)
|
||||
{
|
||||
if (pthread_mutex_init((pthread_mutex_t *)(&(mutex->backend)),
|
||||
pthread_mutexattr_default))
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deallocate a mutex. */
|
||||
int
|
||||
__objc_mutex_deallocate(objc_mutex_t mutex)
|
||||
{
|
||||
if (pthread_mutex_destroy((pthread_mutex_t *)(&(mutex->backend))))
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_lock(objc_mutex_t mutex)
|
||||
{
|
||||
return pthread_mutex_lock((pthread_mutex_t *)(&(mutex->backend)));
|
||||
}
|
||||
|
||||
/* Try to grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_trylock(objc_mutex_t mutex)
|
||||
{
|
||||
if (pthread_mutex_trylock((pthread_mutex_t *)(&(mutex->backend))) != 1)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unlock the mutex */
|
||||
int
|
||||
__objc_mutex_unlock(objc_mutex_t mutex)
|
||||
{
|
||||
return pthread_mutex_unlock((pthread_mutex_t *)(&(mutex->backend)));
|
||||
}
|
||||
|
||||
/* Backend condition mutex functions */
|
||||
|
||||
/* Allocate a condition. */
|
||||
int
|
||||
__objc_condition_allocate(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
|
||||
/*
|
||||
if (pthread_cond_init((pthread_cond_t *)(&(condition->backend)), NULL))
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
*/
|
||||
}
|
||||
|
||||
/* Deallocate a condition. */
|
||||
int
|
||||
__objc_condition_deallocate(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
|
||||
/*
|
||||
return pthread_cond_destroy((pthread_cond_t *)(&(condition->backend)));
|
||||
*/
|
||||
}
|
||||
|
||||
/* Wait on the condition */
|
||||
int
|
||||
__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
|
||||
/*
|
||||
return pthread_cond_wait((pthread_cond_t *)(&(condition->backend)),
|
||||
(pthread_mutex_t *)(&(mutex->backend)));
|
||||
*/
|
||||
}
|
||||
|
||||
/* Wake up all threads waiting on this condition. */
|
||||
int
|
||||
__objc_condition_broadcast(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
|
||||
/*
|
||||
return pthread_cond_broadcast((pthread_cond_t *)(&(condition->backend)));
|
||||
*/
|
||||
}
|
||||
|
||||
/* Wake up one thread waiting on this condition. */
|
||||
int
|
||||
__objc_condition_signal(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
|
||||
/*
|
||||
return pthread_cond_signal((pthread_cond_t *)(&(condition->backend)));
|
||||
*/
|
||||
}
|
||||
|
||||
/* End of File */
|
235
libobjc/thr-irix.c
Normal file
235
libobjc/thr-irix.c
Normal file
@ -0,0 +1,235 @@
|
||||
/* GNU Objective C Runtime Thread Interface - SGI IRIX Implementation
|
||||
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysmp.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <ulocks.h>
|
||||
#include <objc/thr.h>
|
||||
#include "runtime.h"
|
||||
|
||||
/* Key structure for maintaining thread specific storage */
|
||||
static void * __objc_shared_arena_handle = NULL;
|
||||
|
||||
/* Backend initialization functions */
|
||||
|
||||
/* Initialize the threads subsystem. */
|
||||
int
|
||||
__objc_init_thread_system(void)
|
||||
{
|
||||
/* Name of IRIX arena. */
|
||||
char arena_name[64];
|
||||
|
||||
DEBUG_PRINTF("__objc_init_thread_system\n");
|
||||
|
||||
/* Construct a temporary name for arena. */
|
||||
sprintf(arena_name, "/usr/tmp/objc_%05u", (unsigned)getpid());
|
||||
|
||||
/* Up to 256 threads. Arena only for threads. */
|
||||
usconfig(CONF_INITUSERS, 256);
|
||||
usconfig(CONF_ARENATYPE, US_SHAREDONLY);
|
||||
|
||||
/* Initialize the arena */
|
||||
if (!(__objc_shared_arena_handle = usinit(arena_name)))
|
||||
/* Failed */
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Close the threads subsystem. */
|
||||
int
|
||||
__objc_close_thread_system(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backend thread functions */
|
||||
|
||||
/* Create a new thread of execution. */
|
||||
objc_thread_t
|
||||
__objc_thread_detach(void (*func)(void *arg), void *arg)
|
||||
{
|
||||
objc_thread_t thread_id;
|
||||
int sys_id;
|
||||
|
||||
if ((sys_id = sproc((void *)func, PR_SALL, arg)) >= 0)
|
||||
thread_id = (objc_thread_t)sys_id;
|
||||
else
|
||||
thread_id = NULL;
|
||||
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
/* Set the current thread's priority. */
|
||||
int
|
||||
__objc_thread_set_priority(int priority)
|
||||
{
|
||||
/* Not implemented yet */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return the current thread's priority. */
|
||||
int
|
||||
__objc_thread_get_priority(void)
|
||||
{
|
||||
/* Not implemented yet */
|
||||
return OBJC_THREAD_INTERACTIVE_PRIORITY;
|
||||
}
|
||||
|
||||
/* Yield our process time to another thread. */
|
||||
void
|
||||
__objc_thread_yield(void)
|
||||
{
|
||||
sginap(0);
|
||||
}
|
||||
|
||||
/* Terminate the current thread. */
|
||||
int
|
||||
__objc_thread_exit(void)
|
||||
{
|
||||
/* IRIX only has exit. */
|
||||
exit(__objc_thread_exit_status);
|
||||
|
||||
/* Failed if we reached here */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Returns an integer value which uniquely describes a thread. */
|
||||
objc_thread_t
|
||||
__objc_thread_id(void)
|
||||
{
|
||||
/* Threads are processes. */
|
||||
return (objc_thread_t)get_pid();
|
||||
}
|
||||
|
||||
/* Sets the thread's local storage pointer. */
|
||||
int
|
||||
__objc_thread_set_data(void *value)
|
||||
{
|
||||
*((void **)&PRDA->usr_prda) = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns the thread's local storage pointer. */
|
||||
void *
|
||||
__objc_thread_get_data(void)
|
||||
{
|
||||
return *((void **)&PRDA->usr_prda);
|
||||
}
|
||||
|
||||
/* Backend mutex functions */
|
||||
|
||||
/* Allocate a mutex. */
|
||||
int
|
||||
__objc_mutex_allocate(objc_mutex_t mutex)
|
||||
{
|
||||
if (!( (ulock_t)(mutex->backend) = usnewlock(__objc_shared_arena_handle) ))
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deallocate a mutex. */
|
||||
int
|
||||
__objc_mutex_deallocate(objc_mutex_t mutex)
|
||||
{
|
||||
usfreelock((ulock_t)(mutex->backend), __objc_shared_arena_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_lock(objc_mutex_t mutex)
|
||||
{
|
||||
if (ussetlock((ulock_t)(mutex->backend)) == 0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_trylock(objc_mutex_t mutex)
|
||||
{
|
||||
if (ustestlock((ulock_t)(mutex->backend)) == 0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unlock the mutex */
|
||||
int
|
||||
__objc_mutex_unlock(objc_mutex_t mutex)
|
||||
{
|
||||
usunsetlock((ulock_t)(mutex->backend));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backend condition mutex functions */
|
||||
|
||||
/* Allocate a condition. */
|
||||
int
|
||||
__objc_condition_allocate(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Deallocate a condition. */
|
||||
int
|
||||
__objc_condition_deallocate(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wait on the condition */
|
||||
int
|
||||
__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wake up all threads waiting on this condition. */
|
||||
int
|
||||
__objc_condition_broadcast(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wake up one thread waiting on this condition. */
|
||||
int
|
||||
__objc_condition_signal(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* End of File */
|
312
libobjc/thr-mach.c
Normal file
312
libobjc/thr-mach.c
Normal file
@ -0,0 +1,312 @@
|
||||
/* GNU Objective C Runtime Thread Implementation
|
||||
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
|
||||
Modified for Mach threads by Bill Bumgarner <bbum@friday.com>
|
||||
Condition functions added by Mircea Oancea <mircea@first.elcom.pub.ro>
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <mach/cthreads.h>
|
||||
#include <objc/thr.h>
|
||||
#include "runtime.h"
|
||||
|
||||
/*
|
||||
Obtain the maximum thread priority that can set for t. Under the
|
||||
mach threading model, it is possible for the developer to adjust the
|
||||
maximum priority downward only-- cannot be raised without superuser
|
||||
privileges. Once lowered, it cannot be raised.
|
||||
*/
|
||||
static int __mach_get_max_thread_priority(cthread_t t, int *base)
|
||||
{
|
||||
thread_t threadP;
|
||||
kern_return_t error;
|
||||
struct thread_sched_info info;
|
||||
unsigned int info_count=THREAD_SCHED_INFO_COUNT;
|
||||
|
||||
if (t == NULL)
|
||||
return -1;
|
||||
|
||||
threadP = cthread_thread(t); /* get thread underlying */
|
||||
|
||||
error=thread_info(threadP, THREAD_SCHED_INFO,
|
||||
(thread_info_t)&info, &info_count);
|
||||
|
||||
if (error != KERN_SUCCESS)
|
||||
return -1;
|
||||
|
||||
if (base != NULL)
|
||||
*base = info.base_priority;
|
||||
|
||||
return info.max_priority;
|
||||
}
|
||||
|
||||
/* Backend initialization functions */
|
||||
|
||||
/* Initialize the threads subsystem. */
|
||||
int
|
||||
__objc_init_thread_system(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Close the threads subsystem. */
|
||||
int
|
||||
__objc_close_thread_system(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backend thread functions */
|
||||
|
||||
/* Create a new thread of execution. */
|
||||
objc_thread_t
|
||||
__objc_thread_detach(void (*func)(void *arg), void *arg)
|
||||
{
|
||||
objc_thread_t thread_id;
|
||||
cthread_t new_thread_handle;
|
||||
|
||||
/* create thread */
|
||||
new_thread_handle = cthread_fork((cthread_fn_t)func, arg);
|
||||
|
||||
if(new_thread_handle)
|
||||
{
|
||||
/* this is not terribly portable */
|
||||
thread_id = *(objc_thread_t *)&new_thread_handle;
|
||||
cthread_detach(new_thread_handle);
|
||||
}
|
||||
else
|
||||
thread_id = NULL;
|
||||
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
/* Set the current thread's priority. */
|
||||
int
|
||||
__objc_thread_set_priority(int priority)
|
||||
{
|
||||
objc_thread_t *t = objc_thread_id();
|
||||
cthread_t cT = (cthread_t) t;
|
||||
int maxPriority = __mach_get_max_thread_priority(cT, NULL);
|
||||
int sys_priority = 0;
|
||||
|
||||
if (maxPriority == -1)
|
||||
return -1;
|
||||
|
||||
switch (priority)
|
||||
{
|
||||
case OBJC_THREAD_INTERACTIVE_PRIORITY:
|
||||
sys_priority = maxPriority;
|
||||
break;
|
||||
case OBJC_THREAD_BACKGROUND_PRIORITY:
|
||||
sys_priority = (maxPriority * 2) / 3;
|
||||
break;
|
||||
case OBJC_THREAD_LOW_PRIORITY:
|
||||
sys_priority = maxPriority / 3;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sys_priority == 0)
|
||||
return -1;
|
||||
|
||||
/* Change the priority */
|
||||
if (cthread_priority(cT, sys_priority, 0) == KERN_SUCCESS)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return the current thread's priority. */
|
||||
int
|
||||
__objc_thread_get_priority(void)
|
||||
{
|
||||
objc_thread_t *t = objc_thread_id();
|
||||
cthread_t cT = (cthread_t) t; /* see objc_thread_id() */
|
||||
int basePriority;
|
||||
int maxPriority;
|
||||
int sys_priority = 0;
|
||||
|
||||
int interactiveT, backgroundT, lowT; /* thresholds */
|
||||
|
||||
maxPriority = __mach_get_max_thread_priority(cT, &basePriority);
|
||||
|
||||
if(maxPriority == -1)
|
||||
return -1;
|
||||
|
||||
if (basePriority > ( (maxPriority * 2) / 3))
|
||||
return OBJC_THREAD_INTERACTIVE_PRIORITY;
|
||||
|
||||
if (basePriority > ( maxPriority / 3))
|
||||
return OBJC_THREAD_BACKGROUND_PRIORITY;
|
||||
|
||||
return OBJC_THREAD_LOW_PRIORITY;
|
||||
}
|
||||
|
||||
/* Yield our process time to another thread. */
|
||||
void
|
||||
__objc_thread_yield(void)
|
||||
{
|
||||
cthread_yield();
|
||||
}
|
||||
|
||||
/* Terminate the current thread. */
|
||||
int
|
||||
__objc_thread_exit(void)
|
||||
{
|
||||
/* exit the thread */
|
||||
cthread_exit(&__objc_thread_exit_status);
|
||||
|
||||
/* Failed if we reached here */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Returns an integer value which uniquely describes a thread. */
|
||||
objc_thread_t
|
||||
__objc_thread_id(void)
|
||||
{
|
||||
cthread_t self = cthread_self();
|
||||
|
||||
return *(objc_thread_t *)&self;
|
||||
}
|
||||
|
||||
/* Sets the thread's local storage pointer. */
|
||||
int
|
||||
__objc_thread_set_data(void *value)
|
||||
{
|
||||
cthread_set_data(cthread_self(), (any_t) value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns the thread's local storage pointer. */
|
||||
void *
|
||||
__objc_thread_get_data(void)
|
||||
{
|
||||
return (void *) cthread_data(cthread_self());
|
||||
}
|
||||
|
||||
/* Backend mutex functions */
|
||||
|
||||
/* Allocate a mutex. */
|
||||
int
|
||||
__objc_mutex_allocate(objc_mutex_t mutex)
|
||||
{
|
||||
int err = 0;
|
||||
mutex->backend = objc_malloc(sizeof(struct mutex));
|
||||
|
||||
err = mutex_init((mutex_t)(mutex->backend));
|
||||
|
||||
if (err != 0)
|
||||
{
|
||||
objc_free(mutex->backend);
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deallocate a mutex. */
|
||||
int
|
||||
__objc_mutex_deallocate(objc_mutex_t mutex)
|
||||
{
|
||||
mutex_clear((mutex_t)(mutex->backend));
|
||||
|
||||
objc_free(mutex->backend);
|
||||
mutex->backend = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_lock(objc_mutex_t mutex)
|
||||
{
|
||||
mutex_lock((mutex_t)(mutex->backend));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_trylock(objc_mutex_t mutex)
|
||||
{
|
||||
if (mutex_try_lock((mutex_t)(mutex->backend)) == 0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unlock the mutex */
|
||||
int
|
||||
__objc_mutex_unlock(objc_mutex_t mutex)
|
||||
{
|
||||
mutex_unlock((mutex_t)(mutex->backend));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backend condition mutex functions */
|
||||
|
||||
/* Allocate a condition. */
|
||||
int
|
||||
__objc_condition_allocate(objc_condition_t condition)
|
||||
{
|
||||
condition->backend = objc_malloc(sizeof(struct condition));
|
||||
condition_init((condition_t)(condition->backend));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deallocate a condition. */
|
||||
int
|
||||
__objc_condition_deallocate(objc_condition_t condition)
|
||||
{
|
||||
condition_clear((condition_t)(condition->backend));
|
||||
objc_free(condition->backend);
|
||||
condition->backend = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wait on the condition */
|
||||
int
|
||||
__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
|
||||
{
|
||||
condition_wait((condition_t)(condition->backend),
|
||||
(mutex_t)(mutex->backend));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wake up all threads waiting on this condition. */
|
||||
int
|
||||
__objc_condition_broadcast(objc_condition_t condition)
|
||||
{
|
||||
condition_broadcast((condition_t)(condition->backend));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wake up one thread waiting on this condition. */
|
||||
int
|
||||
__objc_condition_signal(objc_condition_t condition)
|
||||
{
|
||||
condition_signal((condition_t)(condition->backend));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* End of File */
|
267
libobjc/thr-os2.c
Normal file
267
libobjc/thr-os2.c
Normal file
@ -0,0 +1,267 @@
|
||||
/* GNU Objective C Runtime Thread Interface - OS/2 emx Implementation
|
||||
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Thomas Baier (baier@ci.tuwien.ac.at)
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include <objc/thr.h>
|
||||
#include "runtime.h"
|
||||
|
||||
#define INCL_DOSSEMAPHORES
|
||||
#define INCL_DOSPROCESS
|
||||
|
||||
/*
|
||||
* conflicts with objc.h: SEL, BOOL, id
|
||||
* solution: prefixing those with _OS2_ before including <os2.h>
|
||||
*/
|
||||
#define SEL _OS2_SEL
|
||||
#define BOOL _OS2_BOOL
|
||||
#define id _OS2_id
|
||||
#include <os2.h>
|
||||
#undef id
|
||||
#undef SEL
|
||||
#undef BOOL
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Backend initialization functions */
|
||||
|
||||
/* Initialize the threads subsystem. */
|
||||
int
|
||||
__objc_init_thread_system(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Close the threads subsystem. */
|
||||
int
|
||||
__objc_close_thread_system(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backend thread functions */
|
||||
|
||||
/* Create a new thread of execution. */
|
||||
objc_thread_t
|
||||
__objc_thread_detach(void (*func)(void *arg), void *arg)
|
||||
{
|
||||
int thread_id = 0;
|
||||
|
||||
if ((thread_id = _beginthread (func,NULL,32768,arg)) < 0)
|
||||
thread_id = 0;
|
||||
|
||||
return (objc_thread_t)thread_id;
|
||||
}
|
||||
|
||||
/* Set the current thread's priority. */
|
||||
int
|
||||
__objc_thread_set_priority(int priority)
|
||||
{
|
||||
ULONG sys_class = 0;
|
||||
ULONG sys_priority = 0;
|
||||
|
||||
/* OBJC_THREAD_INTERACTIVE_PRIORITY -> PRTYC_FOREGROUNDSERVER
|
||||
* OBJC_THREAD_BACKGROUND_PRIORITY -> PRTYC_REGULAR
|
||||
* OBJC_THREAD_LOW_PRIORITY -> PRTYC_IDLETIME */
|
||||
|
||||
switch (priority) {
|
||||
case OBJC_THREAD_INTERACTIVE_PRIORITY:
|
||||
sys_class = PRTYC_REGULAR;
|
||||
sys_priority = 10;
|
||||
break;
|
||||
default:
|
||||
case OBJC_THREAD_BACKGROUND_PRIORITY:
|
||||
sys_class = PRTYC_IDLETIME;
|
||||
sys_priority = 25;
|
||||
break;
|
||||
case OBJC_THREAD_LOW_PRIORITY:
|
||||
sys_class = PRTYC_IDLETIME;
|
||||
sys_priority = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Change priority */
|
||||
if (!DosSetPriority (PRTYS_THREAD,sys_class,sys_priority,*_threadid))
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return the current thread's priority. */
|
||||
int
|
||||
__objc_thread_get_priority(void)
|
||||
{
|
||||
PTIB ptib;
|
||||
PPIB ppib;
|
||||
|
||||
/* get information about current thread */
|
||||
DosGetInfoBlocks (&ptib,&ppib);
|
||||
|
||||
switch (ptib->tib_ptib2->tib2_ulpri)
|
||||
{
|
||||
case PRTYC_IDLETIME:
|
||||
case PRTYC_REGULAR:
|
||||
case PRTYC_TIMECRITICAL:
|
||||
case PRTYC_FOREGROUNDSERVER:
|
||||
default:
|
||||
return OBJC_THREAD_INTERACTIVE_PRIORITY;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Yield our process time to another thread. */
|
||||
void
|
||||
__objc_thread_yield(void)
|
||||
{
|
||||
DosSleep (0);
|
||||
}
|
||||
|
||||
/* Terminate the current thread. */
|
||||
int
|
||||
__objc_thread_exit(void)
|
||||
{
|
||||
/* terminate the thread, NEVER use DosExit () */
|
||||
_endthread ();
|
||||
|
||||
/* Failed if we reached here */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Returns an integer value which uniquely describes a thread. */
|
||||
objc_thread_t
|
||||
__objc_thread_id(void)
|
||||
{
|
||||
return (objc_thread_t) *_threadid;
|
||||
}
|
||||
|
||||
/* Sets the thread's local storage pointer. */
|
||||
int
|
||||
__objc_thread_set_data(void *value)
|
||||
{
|
||||
*_threadstore () = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns the thread's local storage pointer. */
|
||||
void *
|
||||
__objc_thread_get_data(void)
|
||||
{
|
||||
return *_threadstore ();
|
||||
}
|
||||
|
||||
/* Backend mutex functions */
|
||||
|
||||
/* Allocate a mutex. */
|
||||
int
|
||||
__objc_mutex_allocate(objc_mutex_t mutex)
|
||||
{
|
||||
if (DosCreateMutexSem (NULL, (HMTX)(&(mutex->backend)),0L,0) > 0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deallocate a mutex. */
|
||||
int
|
||||
__objc_mutex_deallocate(objc_mutex_t mutex)
|
||||
{
|
||||
DosCloseMutexSem ((HMTX)(mutex->backend));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_lock(objc_mutex_t mutex)
|
||||
{
|
||||
if (DosRequestMutexSem ((HMTX)(mutex->backend),-1L) != 0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_trylock(objc_mutex_t mutex)
|
||||
{
|
||||
if (DosRequestMutexSem ((HMTX)(mutex->backend),0L) != 0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unlock the mutex */
|
||||
int
|
||||
__objc_mutex_unlock(objc_mutex_t mutex)
|
||||
{
|
||||
if (DosReleaseMutexSem((HMTX)(mutex->backend)) != 0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backend condition mutex functions */
|
||||
|
||||
/* Allocate a condition. */
|
||||
int
|
||||
__objc_condition_allocate(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Deallocate a condition. */
|
||||
int
|
||||
__objc_condition_deallocate(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wait on the condition */
|
||||
int
|
||||
__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wake up all threads waiting on this condition. */
|
||||
int
|
||||
__objc_condition_broadcast(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wake up one thread waiting on this condition. */
|
||||
int
|
||||
__objc_condition_signal(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* End of File */
|
229
libobjc/thr-posix.c
Normal file
229
libobjc/thr-posix.c
Normal file
@ -0,0 +1,229 @@
|
||||
/* GNU Objective C Runtime Thread Interface for POSIX compliant threads
|
||||
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
|
||||
Modified for Linux/Pthreads by Kai-Uwe Sattler (kus@iti.cs.uni-magdeburg.de)
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include <objc/thr.h>
|
||||
#include "runtime.h"
|
||||
#include <pthread.h>
|
||||
|
||||
/* Key structure for maintaining thread specific storage */
|
||||
static pthread_key_t _objc_thread_storage;
|
||||
|
||||
/* Backend initialization functions */
|
||||
|
||||
/* Initialize the threads subsystem. */
|
||||
int
|
||||
__objc_init_thread_system(void)
|
||||
{
|
||||
/* Initialize the thread storage key */
|
||||
return pthread_key_create(&_objc_thread_storage, NULL);
|
||||
}
|
||||
|
||||
/* Close the threads subsystem. */
|
||||
int
|
||||
__objc_close_thread_system(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backend thread functions */
|
||||
|
||||
/* Create a new thread of execution. */
|
||||
objc_thread_t
|
||||
__objc_thread_detach(void (*func)(void *arg), void *arg)
|
||||
{
|
||||
objc_thread_t thread_id;
|
||||
pthread_t new_thread_handle;
|
||||
|
||||
if ( !(pthread_create(&new_thread_handle, NULL, (void *)func, arg)) )
|
||||
thread_id = *(objc_thread_t *)&new_thread_handle;
|
||||
else
|
||||
thread_id = NULL;
|
||||
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
/* Set the current thread's priority. */
|
||||
int
|
||||
__objc_thread_set_priority(int priority)
|
||||
{
|
||||
/* Not implemented yet */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return the current thread's priority. */
|
||||
int
|
||||
__objc_thread_get_priority(void)
|
||||
{
|
||||
/* Not implemented yet */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Yield our process time to another thread. */
|
||||
void
|
||||
__objc_thread_yield(void)
|
||||
{
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
/* Terminate the current thread. */
|
||||
int
|
||||
__objc_thread_exit(void)
|
||||
{
|
||||
/* exit the thread */
|
||||
pthread_exit(&__objc_thread_exit_status);
|
||||
|
||||
/* Failed if we reached here */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Returns an integer value which uniquely describes a thread. */
|
||||
objc_thread_t
|
||||
__objc_thread_id(void)
|
||||
{
|
||||
pthread_t self = pthread_self();
|
||||
|
||||
return *(objc_thread_t *)&self;
|
||||
}
|
||||
|
||||
/* Sets the thread's local storage pointer. */
|
||||
int
|
||||
__objc_thread_set_data(void *value)
|
||||
{
|
||||
return pthread_setspecific(_objc_thread_storage, value);
|
||||
}
|
||||
|
||||
/* Returns the thread's local storage pointer. */
|
||||
void *
|
||||
__objc_thread_get_data(void)
|
||||
{
|
||||
return pthread_getspecific(_objc_thread_storage);
|
||||
}
|
||||
|
||||
/* Backend mutex functions */
|
||||
|
||||
/* Allocate a mutex. */
|
||||
int
|
||||
__objc_mutex_allocate(objc_mutex_t mutex)
|
||||
{
|
||||
mutex->backend = objc_malloc(sizeof(pthread_mutex_t));
|
||||
|
||||
if (pthread_mutex_init((pthread_mutex_t *)mutex->backend, NULL))
|
||||
{
|
||||
objc_free(mutex->backend);
|
||||
mutex->backend = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deallocate a mutex. */
|
||||
int
|
||||
__objc_mutex_deallocate(objc_mutex_t mutex)
|
||||
{
|
||||
if (pthread_mutex_destroy((pthread_mutex_t *)mutex->backend))
|
||||
return -1;
|
||||
|
||||
objc_free(mutex->backend);
|
||||
mutex->backend = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_lock(objc_mutex_t mutex)
|
||||
{
|
||||
return pthread_mutex_lock((pthread_mutex_t *)mutex->backend);
|
||||
}
|
||||
|
||||
/* Try to grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_trylock(objc_mutex_t mutex)
|
||||
{
|
||||
return pthread_mutex_trylock((pthread_mutex_t *)mutex->backend);
|
||||
}
|
||||
|
||||
/* Unlock the mutex */
|
||||
int
|
||||
__objc_mutex_unlock(objc_mutex_t mutex)
|
||||
{
|
||||
return pthread_mutex_unlock((pthread_mutex_t *)mutex->backend);
|
||||
}
|
||||
|
||||
/* Backend condition mutex functions */
|
||||
|
||||
/* Allocate a condition. */
|
||||
int
|
||||
__objc_condition_allocate(objc_condition_t condition)
|
||||
{
|
||||
condition->backend = objc_malloc(sizeof(pthread_cond_t));
|
||||
|
||||
if (pthread_cond_init((pthread_cond_t *)condition->backend, NULL))
|
||||
{
|
||||
objc_free(condition->backend);
|
||||
condition->backend = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deallocate a condition. */
|
||||
int
|
||||
__objc_condition_deallocate(objc_condition_t condition)
|
||||
{
|
||||
if (pthread_cond_destroy((pthread_cond_t *)condition->backend))
|
||||
return -1;
|
||||
|
||||
objc_free(condition->backend);
|
||||
condition->backend = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wait on the condition */
|
||||
int
|
||||
__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
|
||||
{
|
||||
return pthread_cond_wait((pthread_cond_t *)condition->backend,
|
||||
(pthread_mutex_t *)mutex->backend);
|
||||
}
|
||||
|
||||
/* Wake up all threads waiting on this condition. */
|
||||
int
|
||||
__objc_condition_broadcast(objc_condition_t condition)
|
||||
{
|
||||
return pthread_cond_broadcast((pthread_cond_t *)condition->backend);
|
||||
}
|
||||
|
||||
/* Wake up one thread waiting on this condition. */
|
||||
int
|
||||
__objc_condition_signal(objc_condition_t condition)
|
||||
{
|
||||
return pthread_cond_signal((pthread_cond_t *)condition->backend);
|
||||
}
|
||||
|
||||
/* End of File */
|
218
libobjc/thr-pthreads.c
Normal file
218
libobjc/thr-pthreads.c
Normal file
@ -0,0 +1,218 @@
|
||||
/* GNU Objective C Runtime Thread Implementation for PCThreads under GNU/Linux.
|
||||
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Scott Christley <scottc@net-community.com>
|
||||
Condition functions added by: Mircea Oancea <mircea@first.elcom.pub.ro>
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include <pcthread.h>
|
||||
#include <objc/thr.h>
|
||||
#include "runtime.h"
|
||||
|
||||
/* Key structure for maintaining thread specific storage */
|
||||
static pthread_key_t _objc_thread_storage;
|
||||
|
||||
/* Backend initialization functions */
|
||||
|
||||
/* Initialize the threads subsystem. */
|
||||
int
|
||||
__objc_init_thread_system(void)
|
||||
{
|
||||
/* Initialize the thread storage key */
|
||||
return pthread_key_create(&_objc_thread_storage, NULL);
|
||||
}
|
||||
|
||||
/* Close the threads subsystem. */
|
||||
int
|
||||
__objc_close_thread_system(void)
|
||||
{
|
||||
/* Destroy the thread storage key */
|
||||
/* Not implemented yet */
|
||||
/* return pthread_key_delete(&_objc_thread_storage); */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backend thread functions */
|
||||
|
||||
/* Create a new thread of execution. */
|
||||
objc_thread_t
|
||||
__objc_thread_detach(void (*func)(void *arg), void *arg)
|
||||
{
|
||||
objc_thread_t thread_id;
|
||||
pthread_t new_thread_handle;
|
||||
|
||||
if ( !(pthread_create(&new_thread_handle, NULL, (void *)func, arg)) )
|
||||
thread_id = *(objc_thread_t *)&new_thread_handle;
|
||||
else
|
||||
thread_id = NULL;
|
||||
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
/* Set the current thread's priority. */
|
||||
int
|
||||
__objc_thread_set_priority(int priority)
|
||||
{
|
||||
/* Not implemented yet */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return the current thread's priority. */
|
||||
int
|
||||
__objc_thread_get_priority(void)
|
||||
{
|
||||
/* Not implemented yet */
|
||||
return OBJC_THREAD_INTERACTIVE_PRIORITY;
|
||||
}
|
||||
|
||||
/* Yield our process time to another thread. */
|
||||
void
|
||||
__objc_thread_yield(void)
|
||||
{
|
||||
pthread_yield(NULL);
|
||||
}
|
||||
|
||||
/* Terminate the current thread. */
|
||||
int
|
||||
__objc_thread_exit(void)
|
||||
{
|
||||
/* exit the thread */
|
||||
pthread_exit(&__objc_thread_exit_status);
|
||||
|
||||
/* Failed if we reached here */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Returns an integer value which uniquely describes a thread. */
|
||||
objc_thread_t
|
||||
__objc_thread_id(void)
|
||||
{
|
||||
pthread_t self = pthread_self();
|
||||
|
||||
return *(objc_thread_t *)&self;
|
||||
}
|
||||
|
||||
/* Sets the thread's local storage pointer. */
|
||||
int
|
||||
__objc_thread_set_data(void *value)
|
||||
{
|
||||
return pthread_setspecific(_objc_thread_storage, value);
|
||||
}
|
||||
|
||||
/* Returns the thread's local storage pointer. */
|
||||
void *
|
||||
__objc_thread_get_data(void)
|
||||
{
|
||||
void *value = NULL;
|
||||
|
||||
if ( !(pthread_getspecific(_objc_thread_storage, &value)) )
|
||||
return value;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Backend mutex functions */
|
||||
|
||||
/* Allocate a mutex. */
|
||||
int
|
||||
__objc_mutex_allocate(objc_mutex_t mutex)
|
||||
{
|
||||
if (pthread_mutex_init((pthread_mutex_t *)(&(mutex->backend)), NULL))
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deallocate a mutex. */
|
||||
int
|
||||
__objc_mutex_deallocate(objc_mutex_t mutex)
|
||||
{
|
||||
if (pthread_mutex_destroy((pthread_mutex_t *)(&(mutex->backend))))
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_lock(objc_mutex_t mutex)
|
||||
{
|
||||
return pthread_mutex_lock((pthread_mutex_t *)(&(mutex->backend)));
|
||||
}
|
||||
|
||||
/* Try to grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_trylock(objc_mutex_t mutex)
|
||||
{
|
||||
return pthread_mutex_trylock((pthread_mutex_t *)(&(mutex->backend)));
|
||||
}
|
||||
|
||||
/* Unlock the mutex */
|
||||
int
|
||||
__objc_mutex_unlock(objc_mutex_t mutex)
|
||||
{
|
||||
return pthread_mutex_unlock((pthread_mutex_t *)(&(mutex->backend)));
|
||||
}
|
||||
|
||||
/* Backend condition mutex functions */
|
||||
|
||||
/* Allocate a condition. */
|
||||
int
|
||||
__objc_condition_allocate(objc_condition_t condition)
|
||||
{
|
||||
if (pthread_cond_init((pthread_cond_t *)(&(condition->backend)), NULL))
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deallocate a condition. */
|
||||
int
|
||||
__objc_condition_deallocate(objc_condition_t condition)
|
||||
{
|
||||
return pthread_cond_destroy((pthread_cond_t *)(&(condition->backend)));
|
||||
}
|
||||
|
||||
/* Wait on the condition */
|
||||
int
|
||||
__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
|
||||
{
|
||||
return pthread_cond_wait((pthread_cond_t *)(&(condition->backend)),
|
||||
(pthread_mutex_t *)(&(mutex->backend)));
|
||||
}
|
||||
|
||||
/* Wake up all threads waiting on this condition. */
|
||||
int
|
||||
__objc_condition_broadcast(objc_condition_t condition)
|
||||
{
|
||||
return pthread_cond_broadcast((pthread_cond_t *)(&(condition->backend)));
|
||||
}
|
||||
|
||||
/* Wake up one thread waiting on this condition. */
|
||||
int
|
||||
__objc_condition_signal(objc_condition_t condition)
|
||||
{
|
||||
return pthread_cond_signal((pthread_cond_t *)(&(condition->backend)));
|
||||
}
|
||||
|
||||
/* End of File */
|
192
libobjc/thr-single.c
Normal file
192
libobjc/thr-single.c
Normal file
@ -0,0 +1,192 @@
|
||||
/* GNU Objective C Runtime Thread Implementation
|
||||
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include <objc/thr.h>
|
||||
#include "runtime.h"
|
||||
|
||||
/* Thread local storage for a single thread */
|
||||
static void *thread_local_storage = NULL;
|
||||
|
||||
/* Backend initialization functions */
|
||||
|
||||
/* Initialize the threads subsystem. */
|
||||
int
|
||||
__objc_init_thread_system(void)
|
||||
{
|
||||
/* No thread support available */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Close the threads subsystem. */
|
||||
int
|
||||
__objc_close_thread_system(void)
|
||||
{
|
||||
/* No thread support available */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Backend thread functions */
|
||||
|
||||
/* Create a new thread of execution. */
|
||||
objc_thread_t
|
||||
__objc_thread_detach(void (*func)(void *arg), void *arg)
|
||||
{
|
||||
/* No thread support available */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set the current thread's priority. */
|
||||
int
|
||||
__objc_thread_set_priority(int priority)
|
||||
{
|
||||
/* No thread support available */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return the current thread's priority. */
|
||||
int
|
||||
__objc_thread_get_priority(void)
|
||||
{
|
||||
return OBJC_THREAD_INTERACTIVE_PRIORITY;
|
||||
}
|
||||
|
||||
/* Yield our process time to another thread. */
|
||||
void
|
||||
__objc_thread_yield(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Terminate the current thread. */
|
||||
int
|
||||
__objc_thread_exit(void)
|
||||
{
|
||||
/* No thread support available */
|
||||
/* Should we really exit the program */
|
||||
/* exit(&__objc_thread_exit_status); */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Returns an integer value which uniquely describes a thread. */
|
||||
objc_thread_t
|
||||
__objc_thread_id(void)
|
||||
{
|
||||
/* No thread support, use 1. */
|
||||
return (objc_thread_t)1;
|
||||
}
|
||||
|
||||
/* Sets the thread's local storage pointer. */
|
||||
int
|
||||
__objc_thread_set_data(void *value)
|
||||
{
|
||||
thread_local_storage = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns the thread's local storage pointer. */
|
||||
void *
|
||||
__objc_thread_get_data(void)
|
||||
{
|
||||
return thread_local_storage;
|
||||
}
|
||||
|
||||
/* Backend mutex functions */
|
||||
|
||||
/* Allocate a mutex. */
|
||||
int
|
||||
__objc_mutex_allocate(objc_mutex_t mutex)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deallocate a mutex. */
|
||||
int
|
||||
__objc_mutex_deallocate(objc_mutex_t mutex)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_lock(objc_mutex_t mutex)
|
||||
{
|
||||
/* There can only be one thread, so we always get the lock */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_trylock(objc_mutex_t mutex)
|
||||
{
|
||||
/* There can only be one thread, so we always get the lock */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unlock the mutex */
|
||||
int
|
||||
__objc_mutex_unlock(objc_mutex_t mutex)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backend condition mutex functions */
|
||||
|
||||
/* Allocate a condition. */
|
||||
int
|
||||
__objc_condition_allocate(objc_condition_t condition)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deallocate a condition. */
|
||||
int
|
||||
__objc_condition_deallocate(objc_condition_t condition)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wait on the condition */
|
||||
int
|
||||
__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wake up all threads waiting on this condition. */
|
||||
int
|
||||
__objc_condition_broadcast(objc_condition_t condition)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wake up one thread waiting on this condition. */
|
||||
int
|
||||
__objc_condition_signal(objc_condition_t condition)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* End of File */
|
259
libobjc/thr-solaris.c
Normal file
259
libobjc/thr-solaris.c
Normal file
@ -0,0 +1,259 @@
|
||||
/* GNU Objective C Runtime Thread Interface
|
||||
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
|
||||
Conditions added by Mircea Oancea (mircea@first.elcom.pub.ro)
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include <objc/thr.h>
|
||||
#include "runtime.h"
|
||||
|
||||
#include <thread.h>
|
||||
#include <synch.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* Key structure for maintaining thread specific storage */
|
||||
static thread_key_t __objc_thread_data_key;
|
||||
|
||||
/* Backend initialization functions */
|
||||
|
||||
/* Initialize the threads subsystem. */
|
||||
int
|
||||
__objc_init_thread_system(void)
|
||||
{
|
||||
/* Initialize the thread storage key */
|
||||
if (thr_keycreate(&__objc_thread_data_key, NULL) == 0)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Close the threads subsystem. */
|
||||
int
|
||||
__objc_close_thread_system(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backend thread functions */
|
||||
|
||||
/* Create a new thread of execution. */
|
||||
objc_thread_t
|
||||
__objc_thread_detach(void (*func)(void *arg), void *arg)
|
||||
{
|
||||
objc_thread_t thread_id;
|
||||
thread_t new_thread_id = 0;
|
||||
|
||||
if (thr_create(NULL, 0, (void *)func, arg,
|
||||
THR_DETACHED | THR_NEW_LWP,
|
||||
&new_thread_id) == 0)
|
||||
thread_id = *(objc_thread_t *)&new_thread_id;
|
||||
else
|
||||
thread_id = NULL;
|
||||
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
/* Set the current thread's priority. */
|
||||
int
|
||||
__objc_thread_set_priority(int priority)
|
||||
{
|
||||
int sys_priority = 0;
|
||||
|
||||
switch (priority)
|
||||
{
|
||||
case OBJC_THREAD_INTERACTIVE_PRIORITY:
|
||||
sys_priority = 300;
|
||||
break;
|
||||
default:
|
||||
case OBJC_THREAD_BACKGROUND_PRIORITY:
|
||||
sys_priority = 200;
|
||||
break;
|
||||
case OBJC_THREAD_LOW_PRIORITY:
|
||||
sys_priority = 1000;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Change priority */
|
||||
if (thr_setprio(thr_self(), sys_priority) == 0)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return the current thread's priority. */
|
||||
int
|
||||
__objc_thread_get_priority(void)
|
||||
{
|
||||
int sys_priority;
|
||||
|
||||
if (thr_getprio(thr_self(), &sys_priority) == 0)
|
||||
{
|
||||
if (sys_priority >= 250)
|
||||
return OBJC_THREAD_INTERACTIVE_PRIORITY;
|
||||
else if (sys_priority >= 150)
|
||||
return OBJC_THREAD_BACKGROUND_PRIORITY;
|
||||
return OBJC_THREAD_LOW_PRIORITY;
|
||||
}
|
||||
|
||||
/* Couldn't get priority. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Yield our process time to another thread. */
|
||||
void
|
||||
__objc_thread_yield(void)
|
||||
{
|
||||
thr_yield();
|
||||
}
|
||||
|
||||
/* Terminate the current thread. */
|
||||
int
|
||||
__objc_thread_exit(void)
|
||||
{
|
||||
/* exit the thread */
|
||||
thr_exit(&__objc_thread_exit_status);
|
||||
|
||||
/* Failed if we reached here */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Returns an integer value which uniquely describes a thread. */
|
||||
objc_thread_t
|
||||
__objc_thread_id(void)
|
||||
{
|
||||
return (objc_thread_t)thr_self();
|
||||
}
|
||||
|
||||
/* Sets the thread's local storage pointer. */
|
||||
int
|
||||
__objc_thread_set_data(void *value)
|
||||
{
|
||||
if (thr_setspecific(__objc_thread_data_key, value) == 0)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Returns the thread's local storage pointer. */
|
||||
void *
|
||||
__objc_thread_get_data(void)
|
||||
{
|
||||
void *value = NULL;
|
||||
|
||||
if (thr_getspecific(__objc_thread_data_key, &value) == 0)
|
||||
return value;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Backend mutex functions */
|
||||
|
||||
/* Allocate a mutex. */
|
||||
int
|
||||
__objc_mutex_allocate(objc_mutex_t mutex)
|
||||
{
|
||||
if (mutex_init( (mutex_t *)(&(mutex->backend)), USYNC_THREAD, 0))
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Deallocate a mutex. */
|
||||
int
|
||||
__objc_mutex_deallocate(objc_mutex_t mutex)
|
||||
{
|
||||
mutex_destroy((mutex_t *)(&(mutex->backend)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_lock(objc_mutex_t mutex)
|
||||
{
|
||||
if (mutex_lock((mutex_t *)(&(mutex->backend))) != 0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_trylock(objc_mutex_t mutex)
|
||||
{
|
||||
if (mutex_trylock((mutex_t *)(&(mutex->backend))) != 0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unlock the mutex */
|
||||
int
|
||||
__objc_mutex_unlock(objc_mutex_t mutex)
|
||||
{
|
||||
if (mutex_unlock((mutex_t *)(&(mutex->backend))) != 0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backend condition mutex functions */
|
||||
|
||||
/* Allocate a condition. */
|
||||
int
|
||||
__objc_condition_allocate(objc_condition_t condition)
|
||||
{
|
||||
return cond_init((cond_t *)(&(condition->backend)), USYNC_THREAD, NULL);
|
||||
}
|
||||
|
||||
/* Deallocate a condition. */
|
||||
int
|
||||
__objc_condition_deallocate(objc_condition_t condition)
|
||||
{
|
||||
return cond_destroy((cond_t *)(&(condition->backend)));
|
||||
}
|
||||
|
||||
/* Wait on the condition */
|
||||
int
|
||||
__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
|
||||
{
|
||||
return cond_wait((cond_t *)(&(condition->backend)),
|
||||
(mutex_t *)(&(mutex->backend)));
|
||||
}
|
||||
|
||||
/* Wake up all threads waiting on this condition. */
|
||||
int
|
||||
__objc_condition_broadcast(objc_condition_t condition)
|
||||
{
|
||||
return cond_broadcast((cond_t *)(&(condition->backend)));
|
||||
}
|
||||
|
||||
/* Wake up one thread waiting on this condition. */
|
||||
int
|
||||
__objc_condition_signal(objc_condition_t condition)
|
||||
{
|
||||
return cond_signal((cond_t *)(&(condition->backend)));
|
||||
}
|
||||
|
||||
/* End of File */
|
192
libobjc/thr-vxworks.c
Normal file
192
libobjc/thr-vxworks.c
Normal file
@ -0,0 +1,192 @@
|
||||
/* GNU Objective C Runtime Thread Implementation
|
||||
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include <objc/thr.h>
|
||||
#include "runtime.h"
|
||||
|
||||
/* Thread local storage for a single thread */
|
||||
static void *thread_local_storage = NULL;
|
||||
|
||||
/* Backend initialization functions */
|
||||
|
||||
/* Initialize the threads subsystem. */
|
||||
int
|
||||
__objc_init_thread_system(void)
|
||||
{
|
||||
/* No thread support available */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Close the threads subsystem. */
|
||||
int
|
||||
__objc_close_thread_system(void)
|
||||
{
|
||||
/* No thread support available */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Backend thread functions */
|
||||
|
||||
/* Create a new thread of execution. */
|
||||
objc_thread_t
|
||||
__objc_thread_detach(void (*func)(void *arg), void *arg)
|
||||
{
|
||||
/* No thread support available */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set the current thread's priority. */
|
||||
int
|
||||
__objc_thread_set_priority(int priority)
|
||||
{
|
||||
/* No thread support available */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return the current thread's priority. */
|
||||
int
|
||||
__objc_thread_get_priority(void)
|
||||
{
|
||||
return OBJC_THREAD_INTERACTIVE_PRIORITY;
|
||||
}
|
||||
|
||||
/* Yield our process time to another thread. */
|
||||
void
|
||||
__objc_thread_yield(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Terminate the current thread. */
|
||||
int
|
||||
__objc_thread_exit(void)
|
||||
{
|
||||
/* No thread support available */
|
||||
/* Should we really exit the program */
|
||||
/* exit(&__objc_thread_exit_status); */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Returns an integer value which uniquely describes a thread. */
|
||||
objc_thread_t
|
||||
__objc_thread_id(void)
|
||||
{
|
||||
/* No thread support, use 1. */
|
||||
return (objc_thread_t)1;
|
||||
}
|
||||
|
||||
/* Sets the thread's local storage pointer. */
|
||||
int
|
||||
__objc_thread_set_data(void *value)
|
||||
{
|
||||
thread_local_storage = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns the thread's local storage pointer. */
|
||||
void *
|
||||
__objc_thread_get_data(void)
|
||||
{
|
||||
return thread_local_storage;
|
||||
}
|
||||
|
||||
/* Backend mutex functions */
|
||||
|
||||
/* Allocate a mutex. */
|
||||
int
|
||||
__objc_mutex_allocate(objc_mutex_t mutex)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deallocate a mutex. */
|
||||
int
|
||||
__objc_mutex_deallocate(objc_mutex_t mutex)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_lock(objc_mutex_t mutex)
|
||||
{
|
||||
/* There can only be one thread, so we always get the lock */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_trylock(objc_mutex_t mutex)
|
||||
{
|
||||
/* There can only be one thread, so we always get the lock */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unlock the mutex */
|
||||
int
|
||||
__objc_mutex_unlock(objc_mutex_t mutex)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backend condition mutex functions */
|
||||
|
||||
/* Allocate a condition. */
|
||||
int
|
||||
__objc_condition_allocate(objc_condition_t condition)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deallocate a condition. */
|
||||
int
|
||||
__objc_condition_deallocate(objc_condition_t condition)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wait on the condition */
|
||||
int
|
||||
__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wake up all threads waiting on this condition. */
|
||||
int
|
||||
__objc_condition_broadcast(objc_condition_t condition)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wake up one thread waiting on this condition. */
|
||||
int
|
||||
__objc_condition_signal(objc_condition_t condition)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* End of File */
|
272
libobjc/thr-win32.c
Normal file
272
libobjc/thr-win32.c
Normal file
@ -0,0 +1,272 @@
|
||||
/* GNU Objective C Runtime Thread Interface - Win32 Implementation
|
||||
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include <objc/thr.h>
|
||||
#include "runtime.h"
|
||||
|
||||
#ifndef __OBJC__
|
||||
#define __OBJC__
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
/* Key structure for maintaining thread specific storage */
|
||||
static DWORD __objc_data_tls = (DWORD)-1;
|
||||
|
||||
/* Backend initialization functions */
|
||||
|
||||
/* Initialize the threads subsystem. */
|
||||
int
|
||||
__objc_init_thread_system(void)
|
||||
{
|
||||
/* Initialize the thread storage key */
|
||||
if ((__objc_data_tls = TlsAlloc()) != (DWORD)-1)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Close the threads subsystem. */
|
||||
int
|
||||
__objc_close_thread_system(void)
|
||||
{
|
||||
if (__objc_data_tls != (DWORD)-1)
|
||||
TlsFree(__objc_data_tls);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backend thread functions */
|
||||
|
||||
/* Create a new thread of execution. */
|
||||
objc_thread_t
|
||||
__objc_thread_detach(void (*func)(void *arg), void *arg)
|
||||
{
|
||||
DWORD thread_id = 0;
|
||||
HANDLE win32_handle;
|
||||
|
||||
if (!(win32_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func,
|
||||
arg, 0, &thread_id)))
|
||||
thread_id = 0;
|
||||
|
||||
return (objc_thread_t)thread_id;
|
||||
}
|
||||
|
||||
/* Set the current thread's priority. */
|
||||
int
|
||||
__objc_thread_set_priority(int priority)
|
||||
{
|
||||
int sys_priority = 0;
|
||||
|
||||
switch (priority)
|
||||
{
|
||||
case OBJC_THREAD_INTERACTIVE_PRIORITY:
|
||||
sys_priority = THREAD_PRIORITY_NORMAL;
|
||||
break;
|
||||
default:
|
||||
case OBJC_THREAD_BACKGROUND_PRIORITY:
|
||||
sys_priority = THREAD_PRIORITY_BELOW_NORMAL;
|
||||
break;
|
||||
case OBJC_THREAD_LOW_PRIORITY:
|
||||
sys_priority = THREAD_PRIORITY_LOWEST;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Change priority */
|
||||
if (SetThreadPriority(GetCurrentThread(), sys_priority))
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return the current thread's priority. */
|
||||
int
|
||||
__objc_thread_get_priority(void)
|
||||
{
|
||||
int sys_priority;
|
||||
|
||||
sys_priority = GetThreadPriority(GetCurrentThread());
|
||||
|
||||
switch (sys_priority)
|
||||
{
|
||||
case THREAD_PRIORITY_HIGHEST:
|
||||
case THREAD_PRIORITY_TIME_CRITICAL:
|
||||
case THREAD_PRIORITY_ABOVE_NORMAL:
|
||||
case THREAD_PRIORITY_NORMAL:
|
||||
return OBJC_THREAD_INTERACTIVE_PRIORITY;
|
||||
|
||||
default:
|
||||
case THREAD_PRIORITY_BELOW_NORMAL:
|
||||
return OBJC_THREAD_BACKGROUND_PRIORITY;
|
||||
|
||||
case THREAD_PRIORITY_IDLE:
|
||||
case THREAD_PRIORITY_LOWEST:
|
||||
return OBJC_THREAD_LOW_PRIORITY;
|
||||
}
|
||||
|
||||
/* Couldn't get priority. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Yield our process time to another thread. */
|
||||
void
|
||||
__objc_thread_yield(void)
|
||||
{
|
||||
Sleep(0);
|
||||
}
|
||||
|
||||
/* Terminate the current thread. */
|
||||
int
|
||||
__objc_thread_exit(void)
|
||||
{
|
||||
/* exit the thread */
|
||||
ExitThread(__objc_thread_exit_status);
|
||||
|
||||
/* Failed if we reached here */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Returns an integer value which uniquely describes a thread. */
|
||||
objc_thread_t
|
||||
__objc_thread_id(void)
|
||||
{
|
||||
return (objc_thread_t)GetCurrentThreadId();
|
||||
}
|
||||
|
||||
/* Sets the thread's local storage pointer. */
|
||||
int
|
||||
__objc_thread_set_data(void *value)
|
||||
{
|
||||
if (TlsSetValue(__objc_data_tls, value))
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Returns the thread's local storage pointer. */
|
||||
void *
|
||||
__objc_thread_get_data(void)
|
||||
{
|
||||
return TlsGetValue(__objc_data_tls); /* Return thread data. */
|
||||
}
|
||||
|
||||
/* Backend mutex functions */
|
||||
|
||||
/* Allocate a mutex. */
|
||||
int
|
||||
__objc_mutex_allocate(objc_mutex_t mutex)
|
||||
{
|
||||
if ((mutex->backend = (void *)CreateMutex(NULL, 0, NULL)) == NULL)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deallocate a mutex. */
|
||||
int
|
||||
__objc_mutex_deallocate(objc_mutex_t mutex)
|
||||
{
|
||||
CloseHandle((HANDLE)(mutex->backend));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_lock(objc_mutex_t mutex)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = WaitForSingleObject((HANDLE)(mutex->backend), INFINITE);
|
||||
if (status != WAIT_OBJECT_0 && status != WAIT_ABANDONED)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to grab a lock on a mutex. */
|
||||
int
|
||||
__objc_mutex_trylock(objc_mutex_t mutex)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = WaitForSingleObject((HANDLE)(mutex->backend), 0);
|
||||
if (status != WAIT_OBJECT_0 && status != WAIT_ABANDONED)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unlock the mutex */
|
||||
int
|
||||
__objc_mutex_unlock(objc_mutex_t mutex)
|
||||
{
|
||||
if (ReleaseMutex((HANDLE)(mutex->backend)) == 0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backend condition mutex functions */
|
||||
|
||||
/* Allocate a condition. */
|
||||
int
|
||||
__objc_condition_allocate(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Deallocate a condition. */
|
||||
int
|
||||
__objc_condition_deallocate(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wait on the condition */
|
||||
int
|
||||
__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wake up all threads waiting on this condition. */
|
||||
int
|
||||
__objc_condition_broadcast(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wake up one thread waiting on this condition. */
|
||||
int
|
||||
__objc_condition_signal(objc_condition_t condition)
|
||||
{
|
||||
/* Unimplemented. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* End of File */
|
534
libobjc/thr.c
Normal file
534
libobjc/thr.c
Normal file
@ -0,0 +1,534 @@
|
||||
/* GNU Objective C Runtime Thread Interface
|
||||
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled with
|
||||
GCC to produce an executable, this does not cause the resulting executable
|
||||
to be covered by the GNU General Public License. This exception does not
|
||||
however invalidate any other reasons why the executable file might be
|
||||
covered by the GNU General Public License. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "runtime.h"
|
||||
|
||||
/* Global exit status. */
|
||||
int __objc_thread_exit_status = 0;
|
||||
|
||||
/* Flag which lets us know if we ever became multi threaded */
|
||||
int __objc_is_multi_threaded = 0;
|
||||
|
||||
/* The hook function called when the runtime becomes multi threaded */
|
||||
objc_thread_callback _objc_became_multi_threaded = NULL;
|
||||
|
||||
/*
|
||||
Use this to set the hook function that will be called when the
|
||||
runtime initially becomes multi threaded.
|
||||
The hook function is only called once, meaning only when the
|
||||
2nd thread is spawned, not for each and every thread.
|
||||
|
||||
It returns the previous hook function or NULL if there is none.
|
||||
|
||||
A program outside of the runtime could set this to some function so
|
||||
it can be informed; for example, the GNUstep Base Library sets it
|
||||
so it can implement the NSBecomingMultiThreaded notification.
|
||||
*/
|
||||
objc_thread_callback objc_set_thread_callback(objc_thread_callback func)
|
||||
{
|
||||
objc_thread_callback temp = _objc_became_multi_threaded;
|
||||
_objc_became_multi_threaded = func;
|
||||
return temp;
|
||||
}
|
||||
|
||||
/*
|
||||
Private functions
|
||||
|
||||
These functions are utilized by the frontend, but they are not
|
||||
considered part of the public interface.
|
||||
*/
|
||||
|
||||
/*
|
||||
First function called in a thread, starts everything else.
|
||||
|
||||
This function is passed to the backend by objc_thread_detach
|
||||
as the starting function for a new thread.
|
||||
*/
|
||||
struct __objc_thread_start_state
|
||||
{
|
||||
SEL selector;
|
||||
id object;
|
||||
id argument;
|
||||
};
|
||||
|
||||
static volatile void
|
||||
__objc_thread_detach_function(struct __objc_thread_start_state *istate)
|
||||
{
|
||||
/* Valid state? */
|
||||
if (istate) {
|
||||
id (*imp)(id,SEL,id);
|
||||
SEL selector = istate->selector;
|
||||
id object = istate->object;
|
||||
id argument = istate->argument;
|
||||
|
||||
/* Don't need anymore so free it */
|
||||
objc_free(istate);
|
||||
|
||||
/* Clear out the thread local storage */
|
||||
objc_thread_set_data(NULL);
|
||||
|
||||
/* Check to see if we just became multi threaded */
|
||||
if (!__objc_is_multi_threaded)
|
||||
{
|
||||
__objc_is_multi_threaded = 1;
|
||||
|
||||
/* Call the hook function */
|
||||
if (_objc_became_multi_threaded != NULL)
|
||||
(*_objc_became_multi_threaded)();
|
||||
}
|
||||
|
||||
/* Call the method */
|
||||
if ((imp = (id(*)(id, SEL, id))objc_msg_lookup(object, selector)))
|
||||
(*imp)(object, selector, argument);
|
||||
else
|
||||
objc_error(object, OBJC_ERR_UNIMPLEMENTED,
|
||||
"objc_thread_detach called with bad selector.\n");
|
||||
}
|
||||
else
|
||||
objc_error(nil, OBJC_ERR_BAD_STATE,
|
||||
"objc_thread_detach called with NULL state.\n");
|
||||
|
||||
/* Exit the thread */
|
||||
objc_thread_exit();
|
||||
}
|
||||
|
||||
/*
|
||||
Frontend functions
|
||||
|
||||
These functions constitute the public interface to the Objective-C thread
|
||||
and mutex functionality.
|
||||
*/
|
||||
|
||||
/* Frontend thread functions */
|
||||
|
||||
/*
|
||||
Detach a new thread of execution and return its id. Returns NULL if fails.
|
||||
Thread is started by sending message with selector to object. Message
|
||||
takes a single argument.
|
||||
*/
|
||||
objc_thread_t
|
||||
objc_thread_detach(SEL selector, id object, id argument)
|
||||
{
|
||||
struct __objc_thread_start_state *istate;
|
||||
objc_thread_t thread_id = NULL;
|
||||
|
||||
/* Allocate the state structure */
|
||||
if (!(istate = (struct __objc_thread_start_state *)
|
||||
objc_malloc(sizeof(*istate))))
|
||||
return NULL;
|
||||
|
||||
/* Initialize the state structure */
|
||||
istate->selector = selector;
|
||||
istate->object = object;
|
||||
istate->argument = argument;
|
||||
|
||||
/* lock access */
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
|
||||
/* Call the backend to spawn the thread */
|
||||
if ((thread_id = __objc_thread_detach((void *)__objc_thread_detach_function,
|
||||
istate)) == NULL)
|
||||
{
|
||||
/* failed! */
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
objc_free(istate);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Increment our thread counter */
|
||||
__objc_runtime_threads_alive++;
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
/* Set the current thread's priority. */
|
||||
int
|
||||
objc_thread_set_priority(int priority)
|
||||
{
|
||||
/* Call the backend */
|
||||
return __objc_thread_set_priority(priority);
|
||||
}
|
||||
|
||||
/* Return the current thread's priority. */
|
||||
int
|
||||
objc_thread_get_priority(void)
|
||||
{
|
||||
/* Call the backend */
|
||||
return __objc_thread_get_priority();
|
||||
}
|
||||
|
||||
/*
|
||||
Yield our process time to another thread. Any BUSY waiting that is done
|
||||
by a thread should use this function to make sure that other threads can
|
||||
make progress even on a lazy uniprocessor system.
|
||||
*/
|
||||
void
|
||||
objc_thread_yield(void)
|
||||
{
|
||||
/* Call the backend */
|
||||
__objc_thread_yield();
|
||||
}
|
||||
|
||||
/*
|
||||
Terminate the current tread. Doesn't return.
|
||||
Actually, if it failed returns -1.
|
||||
*/
|
||||
int
|
||||
objc_thread_exit(void)
|
||||
{
|
||||
/* Decrement our counter of the number of threads alive */
|
||||
objc_mutex_lock(__objc_runtime_mutex);
|
||||
__objc_runtime_threads_alive--;
|
||||
objc_mutex_unlock(__objc_runtime_mutex);
|
||||
|
||||
/* Call the backend to terminate the thread */
|
||||
return __objc_thread_exit();
|
||||
}
|
||||
|
||||
/*
|
||||
Returns an integer value which uniquely describes a thread. Must not be
|
||||
NULL which is reserved as a marker for "no thread".
|
||||
*/
|
||||
objc_thread_t
|
||||
objc_thread_id(void)
|
||||
{
|
||||
/* Call the backend */
|
||||
return __objc_thread_id();
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the thread's local storage pointer.
|
||||
Returns 0 if successful or -1 if failed.
|
||||
*/
|
||||
int
|
||||
objc_thread_set_data(void *value)
|
||||
{
|
||||
/* Call the backend */
|
||||
return __objc_thread_set_data(value);
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the thread's local storage pointer. Returns NULL on failure.
|
||||
*/
|
||||
void *
|
||||
objc_thread_get_data(void)
|
||||
{
|
||||
/* Call the backend */
|
||||
return __objc_thread_get_data();
|
||||
}
|
||||
|
||||
/* Frontend mutex functions */
|
||||
|
||||
/*
|
||||
Allocate a mutex. Return the mutex pointer if successful or NULL if the
|
||||
allocation failed for any reason.
|
||||
*/
|
||||
objc_mutex_t
|
||||
objc_mutex_allocate(void)
|
||||
{
|
||||
objc_mutex_t mutex;
|
||||
|
||||
/* Allocate the mutex structure */
|
||||
if (!(mutex = (objc_mutex_t)objc_malloc(sizeof(struct objc_mutex))))
|
||||
return NULL;
|
||||
|
||||
/* Call backend to create the mutex */
|
||||
if (__objc_mutex_allocate(mutex))
|
||||
{
|
||||
/* failed! */
|
||||
objc_free(mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize mutex */
|
||||
mutex->owner = NULL;
|
||||
mutex->depth = 0;
|
||||
return mutex;
|
||||
}
|
||||
|
||||
/*
|
||||
Deallocate a mutex. Note that this includes an implicit mutex_lock to
|
||||
insure that no one else is using the lock. It is legal to deallocate
|
||||
a lock if we have a lock on it, but illegal to deallocate a lock held
|
||||
by anyone else.
|
||||
Returns the number of locks on the thread. (1 for deallocate).
|
||||
*/
|
||||
int
|
||||
objc_mutex_deallocate(objc_mutex_t mutex)
|
||||
{
|
||||
int depth;
|
||||
|
||||
/* Valid mutex? */
|
||||
if (!mutex)
|
||||
return -1;
|
||||
|
||||
/* Acquire lock on mutex */
|
||||
depth = objc_mutex_lock(mutex);
|
||||
|
||||
/* Call backend to destroy mutex */
|
||||
if (__objc_mutex_deallocate(mutex))
|
||||
return -1;
|
||||
|
||||
/* Free the mutex structure */
|
||||
objc_free(mutex);
|
||||
|
||||
/* Return last depth */
|
||||
return depth;
|
||||
}
|
||||
|
||||
/*
|
||||
Grab a lock on a mutex. If this thread already has a lock on this mutex
|
||||
then we increment the lock count. If another thread has a lock on the
|
||||
mutex we block and wait for the thread to release the lock.
|
||||
Returns the lock count on the mutex held by this thread.
|
||||
*/
|
||||
int
|
||||
objc_mutex_lock(objc_mutex_t mutex)
|
||||
{
|
||||
objc_thread_t thread_id;
|
||||
int status;
|
||||
|
||||
/* Valid mutex? */
|
||||
if (!mutex)
|
||||
return -1;
|
||||
|
||||
/* If we already own the lock then increment depth */
|
||||
thread_id = objc_thread_id();
|
||||
if (mutex->owner == thread_id)
|
||||
return ++mutex->depth;
|
||||
|
||||
/* Call the backend to lock the mutex */
|
||||
status = __objc_mutex_lock(mutex);
|
||||
|
||||
/* Failed? */
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
/* Successfully locked the thread */
|
||||
mutex->owner = thread_id;
|
||||
return mutex->depth = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
Try to grab a lock on a mutex. If this thread already has a lock on
|
||||
this mutex then we increment the lock count and return it. If another
|
||||
thread has a lock on the mutex returns -1.
|
||||
*/
|
||||
int
|
||||
objc_mutex_trylock(objc_mutex_t mutex)
|
||||
{
|
||||
objc_thread_t thread_id;
|
||||
int status;
|
||||
|
||||
/* Valid mutex? */
|
||||
if (!mutex)
|
||||
return -1;
|
||||
|
||||
/* If we already own the lock then increment depth */
|
||||
thread_id = objc_thread_id();
|
||||
if (mutex->owner == thread_id)
|
||||
return ++mutex->depth;
|
||||
|
||||
/* Call the backend to try to lock the mutex */
|
||||
status = __objc_mutex_trylock(mutex);
|
||||
|
||||
/* Failed? */
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
/* Successfully locked the thread */
|
||||
mutex->owner = thread_id;
|
||||
return mutex->depth = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
Unlocks the mutex by one level.
|
||||
Decrements the lock count on this mutex by one.
|
||||
If the lock count reaches zero, release the lock on the mutex.
|
||||
Returns the lock count on the mutex.
|
||||
It is an error to attempt to unlock a mutex which this thread
|
||||
doesn't hold in which case return -1 and the mutex is unaffected.
|
||||
*/
|
||||
int
|
||||
objc_mutex_unlock(objc_mutex_t mutex)
|
||||
{
|
||||
objc_thread_t thread_id;
|
||||
int status;
|
||||
|
||||
/* Valid mutex? */
|
||||
if (!mutex)
|
||||
return -1;
|
||||
|
||||
/* If another thread owns the lock then abort */
|
||||
thread_id = objc_thread_id();
|
||||
if (mutex->owner != thread_id)
|
||||
return -1;
|
||||
|
||||
/* Decrement depth and return */
|
||||
if (mutex->depth > 1)
|
||||
return --mutex->depth;
|
||||
|
||||
/* Depth down to zero so we are no longer the owner */
|
||||
mutex->depth = 0;
|
||||
mutex->owner = NULL;
|
||||
|
||||
/* Have the backend unlock the mutex */
|
||||
status = __objc_mutex_unlock(mutex);
|
||||
|
||||
/* Failed? */
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Frontend condition mutex functions */
|
||||
|
||||
/*
|
||||
Allocate a condition. Return the condition pointer if successful or NULL
|
||||
if the allocation failed for any reason.
|
||||
*/
|
||||
objc_condition_t
|
||||
objc_condition_allocate(void)
|
||||
{
|
||||
objc_condition_t condition;
|
||||
|
||||
/* Allocate the condition mutex structure */
|
||||
if (!(condition =
|
||||
(objc_condition_t)objc_malloc(sizeof(struct objc_condition))))
|
||||
return NULL;
|
||||
|
||||
/* Call the backend to create the condition mutex */
|
||||
if (__objc_condition_allocate(condition))
|
||||
{
|
||||
/* failed! */
|
||||
objc_free(condition);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Success! */
|
||||
return condition;
|
||||
}
|
||||
|
||||
/*
|
||||
Deallocate a condition. Note that this includes an implicit
|
||||
condition_broadcast to insure that waiting threads have the opportunity
|
||||
to wake. It is legal to dealloc a condition only if no other
|
||||
thread is/will be using it. Here we do NOT check for other threads
|
||||
waiting but just wake them up.
|
||||
*/
|
||||
int
|
||||
objc_condition_deallocate(objc_condition_t condition)
|
||||
{
|
||||
/* Broadcast the condition */
|
||||
if (objc_condition_broadcast(condition))
|
||||
return -1;
|
||||
|
||||
/* Call the backend to destroy */
|
||||
if (__objc_condition_deallocate(condition))
|
||||
return -1;
|
||||
|
||||
/* Free the condition mutex structure */
|
||||
objc_free(condition);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Wait on the condition unlocking the mutex until objc_condition_signal()
|
||||
or objc_condition_broadcast() are called for the same condition. The
|
||||
given mutex *must* have the depth set to 1 so that it can be unlocked
|
||||
here, so that someone else can lock it and signal/broadcast the condition.
|
||||
The mutex is used to lock access to the shared data that make up the
|
||||
"condition" predicate.
|
||||
*/
|
||||
int
|
||||
objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
|
||||
{
|
||||
objc_thread_t thread_id;
|
||||
|
||||
/* Valid arguments? */
|
||||
if (!mutex || !condition)
|
||||
return -1;
|
||||
|
||||
/* Make sure we are owner of mutex */
|
||||
thread_id = objc_thread_id();
|
||||
if (mutex->owner != thread_id)
|
||||
return -1;
|
||||
|
||||
/* Cannot be locked more than once */
|
||||
if (mutex->depth > 1)
|
||||
return -1;
|
||||
|
||||
/* Virtually unlock the mutex */
|
||||
mutex->depth = 0;
|
||||
mutex->owner = (objc_thread_t)NULL;
|
||||
|
||||
/* Call the backend to wait */
|
||||
__objc_condition_wait(condition, mutex);
|
||||
|
||||
/* Make ourselves owner of the mutex */
|
||||
mutex->owner = thread_id;
|
||||
mutex->depth = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Wake up all threads waiting on this condition. It is recommended that
|
||||
the called would lock the same mutex as the threads in objc_condition_wait
|
||||
before changing the "condition predicate" and make this call and unlock it
|
||||
right away after this call.
|
||||
*/
|
||||
int
|
||||
objc_condition_broadcast(objc_condition_t condition)
|
||||
{
|
||||
/* Valid condition mutex? */
|
||||
if (!condition)
|
||||
return -1;
|
||||
|
||||
return __objc_condition_broadcast(condition);
|
||||
}
|
||||
|
||||
/*
|
||||
Wake up one thread waiting on this condition. It is recommended that
|
||||
the called would lock the same mutex as the threads in objc_condition_wait
|
||||
before changing the "condition predicate" and make this call and unlock it
|
||||
right away after this call.
|
||||
*/
|
||||
int
|
||||
objc_condition_signal(objc_condition_t condition)
|
||||
{
|
||||
/* Valid condition mutex? */
|
||||
if (!condition)
|
||||
return -1;
|
||||
|
||||
return __objc_condition_signal(condition);
|
||||
}
|
||||
|
||||
/* End of File */
|
143
libobjc/thr.h
Normal file
143
libobjc/thr.h
Normal file
@ -0,0 +1,143 @@
|
||||
/* Thread and mutex controls for Objective C.
|
||||
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||
Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2, or (at your option) any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU CC; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files
|
||||
compiled with GCC to produce an executable, this does not cause
|
||||
the resulting executable to be covered by the GNU General Public License.
|
||||
This exception does not however invalidate any other reasons why
|
||||
the executable file might be covered by the GNU General Public License. */
|
||||
|
||||
|
||||
#ifndef __thread_INCLUDE_GNU
|
||||
#define __thread_INCLUDE_GNU
|
||||
|
||||
#include "objc/objc.h"
|
||||
|
||||
/*************************************************************************
|
||||
* Universal static variables:
|
||||
*/
|
||||
extern int __objc_thread_exit_status; /* Global exit status. */
|
||||
|
||||
/********
|
||||
* Thread safe implementation types and functions.
|
||||
*/
|
||||
|
||||
/* Thread priorities */
|
||||
#define OBJC_THREAD_INTERACTIVE_PRIORITY 2
|
||||
#define OBJC_THREAD_BACKGROUND_PRIORITY 1
|
||||
#define OBJC_THREAD_LOW_PRIORITY 0
|
||||
|
||||
/* A thread */
|
||||
typedef void * objc_thread_t;
|
||||
|
||||
/* This structure represents a single mutual exclusion lock. */
|
||||
struct objc_mutex
|
||||
{
|
||||
volatile objc_thread_t owner; /* Id of thread that owns. */
|
||||
volatile int depth; /* # of acquires. */
|
||||
void * backend; /* Specific to backend */
|
||||
};
|
||||
typedef struct objc_mutex *objc_mutex_t;
|
||||
|
||||
/* This structure represents a single condition mutex */
|
||||
struct objc_condition
|
||||
{
|
||||
void * backend; /* Specific to backend */
|
||||
};
|
||||
typedef struct objc_condition *objc_condition_t;
|
||||
|
||||
/* Frontend mutex functions */
|
||||
objc_mutex_t objc_mutex_allocate(void);
|
||||
int objc_mutex_deallocate(objc_mutex_t mutex);
|
||||
int objc_mutex_lock(objc_mutex_t mutex);
|
||||
int objc_mutex_unlock(objc_mutex_t mutex);
|
||||
int objc_mutex_trylock(objc_mutex_t mutex);
|
||||
|
||||
/* Frontend condition mutex functions */
|
||||
objc_condition_t objc_condition_allocate(void);
|
||||
int objc_condition_deallocate(objc_condition_t condition);
|
||||
int objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex);
|
||||
int objc_condition_signal(objc_condition_t condition);
|
||||
int objc_condition_broadcast(objc_condition_t condition);
|
||||
|
||||
/* Frontend thread functions */
|
||||
objc_thread_t objc_thread_detach(SEL selector, id object, id argument);
|
||||
void objc_thread_yield(void);
|
||||
int objc_thread_exit(void);
|
||||
int objc_thread_set_priority(int priority);
|
||||
int objc_thread_get_priority(void);
|
||||
void * objc_thread_get_data(void);
|
||||
int objc_thread_set_data(void *value);
|
||||
objc_thread_t objc_thread_id(void);
|
||||
|
||||
/*
|
||||
Use this to set the hook function that will be called when the
|
||||
runtime initially becomes multi threaded.
|
||||
The hook function is only called once, meaning only when the
|
||||
2nd thread is spawned, not for each and every thread.
|
||||
|
||||
It returns the previous hook function or NULL if there is none.
|
||||
|
||||
A program outside of the runtime could set this to some function so
|
||||
it can be informed; for example, the GNUstep Base Library sets it
|
||||
so it can implement the NSBecomingMultiThreaded notification.
|
||||
*/
|
||||
typedef void (*objc_thread_callback)();
|
||||
objc_thread_callback objc_set_thread_callback(objc_thread_callback func);
|
||||
|
||||
/* Backend initialization functions */
|
||||
int __objc_init_thread_system(void);
|
||||
int __objc_fini_thread_system(void);
|
||||
|
||||
/* Backend mutex functions */
|
||||
int __objc_mutex_allocate(objc_mutex_t mutex);
|
||||
int __objc_mutex_deallocate(objc_mutex_t mutex);
|
||||
int __objc_mutex_lock(objc_mutex_t mutex);
|
||||
int __objc_mutex_trylock(objc_mutex_t mutex);
|
||||
int __objc_mutex_unlock(objc_mutex_t mutex);
|
||||
|
||||
/* Backend condition mutex functions */
|
||||
int __objc_condition_allocate(objc_condition_t condition);
|
||||
int __objc_condition_deallocate(objc_condition_t condition);
|
||||
int __objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex);
|
||||
int __objc_condition_broadcast(objc_condition_t condition);
|
||||
int __objc_condition_signal(objc_condition_t condition);
|
||||
|
||||
/* Backend thread functions */
|
||||
objc_thread_t __objc_thread_detach(void (*func)(void *arg), void *arg);
|
||||
int __objc_thread_set_priority(int priority);
|
||||
int __objc_thread_get_priority(void);
|
||||
void __objc_thread_yield(void);
|
||||
int __objc_thread_exit(void);
|
||||
objc_thread_t __objc_thread_id(void);
|
||||
int __objc_thread_set_data(void *value);
|
||||
void * __objc_thread_get_data(void);
|
||||
|
||||
#endif /* not __thread_INCLUDE_GNU */
|
132
libobjc/typedstream.h
Normal file
132
libobjc/typedstream.h
Normal file
@ -0,0 +1,132 @@
|
||||
/* GNU Objective-C Typed Streams interface.
|
||||
Copyright (C) 1993, 1995 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, if you link this library with files compiled
|
||||
with GCC to produce an executable, this does not cause the resulting
|
||||
executable to be covered by the GNU General Public License. This
|
||||
exception does not however invalidate any other reasons why the
|
||||
executable file might be covered by the GNU General Public License. */
|
||||
|
||||
#ifndef __typedstream_INCLUDE_GNU
|
||||
#define __typedstream_INCLUDE_GNU
|
||||
|
||||
#include "objc/objc.h"
|
||||
#include "objc/hash.h"
|
||||
#include <stdio.h>
|
||||
|
||||
typedef int (*objc_typed_read_func)(void*, char*, int);
|
||||
typedef int (*objc_typed_write_func)(void*, const char*, int);
|
||||
typedef int (*objc_typed_flush_func)(void*);
|
||||
typedef int (*objc_typed_eof_func)(void*);
|
||||
|
||||
#define OBJC_READONLY 0x01
|
||||
#define OBJC_WRITEONLY 0x02
|
||||
|
||||
#define OBJC_MANAGED_STREAM 0x01
|
||||
#define OBJC_FILE_STREAM 0x02
|
||||
#define OBJC_MEMORY_STREAM 0x04
|
||||
|
||||
#define OBJC_TYPED_STREAM_VERSION 0x01
|
||||
|
||||
typedef struct objc_typed_stream {
|
||||
void* physical;
|
||||
cache_ptr object_table; /* read/written objects */
|
||||
cache_ptr stream_table; /* other read/written but shared things.. */
|
||||
cache_ptr class_table; /* class version mapping */
|
||||
cache_ptr object_refs; /* forward references */
|
||||
int mode; /* OBJC_READONLY or OBJC_WRITEONLY */
|
||||
int type; /* MANAGED, FILE, MEMORY etc bit string */
|
||||
int version; /* version used when writing */
|
||||
int writing_root_p;
|
||||
objc_typed_read_func read;
|
||||
objc_typed_write_func write;
|
||||
objc_typed_eof_func eof;
|
||||
objc_typed_flush_func flush;
|
||||
} TypedStream;
|
||||
|
||||
/* opcode masks */
|
||||
#define _B_VALUE 0x1fU
|
||||
#define _B_CODE 0xe0U
|
||||
#define _B_SIGN 0x10U
|
||||
#define _B_NUMBER 0x0fU
|
||||
|
||||
/* standard opcodes */
|
||||
#define _B_INVALID 0x00U
|
||||
#define _B_SINT 0x20U
|
||||
#define _B_NINT 0x40U
|
||||
#define _B_SSTR 0x60U
|
||||
#define _B_NSTR 0x80U
|
||||
#define _B_RCOMM 0xa0U
|
||||
#define _B_UCOMM 0xc0U
|
||||
#define _B_EXT 0xe0U
|
||||
|
||||
/* eXtension opcodes */
|
||||
#define _BX_OBJECT 0x00U
|
||||
#define _BX_CLASS 0x01U
|
||||
#define _BX_SEL 0x02U
|
||||
#define _BX_OBJREF 0x03U
|
||||
#define _BX_OBJROOT 0x04U
|
||||
#define _BX_EXT 0x1fU
|
||||
|
||||
/*
|
||||
** Read and write objects as specified by TYPE. All the `last'
|
||||
** arguments are pointers to the objects to read/write.
|
||||
*/
|
||||
|
||||
int objc_write_type (TypedStream* stream, const char* type, const void* data);
|
||||
int objc_read_type (TypedStream* stream, const char* type, void* data);
|
||||
|
||||
int objc_write_types (TypedStream* stream, const char* type, ...);
|
||||
int objc_read_types (TypedStream* stream, const char* type, ...);
|
||||
|
||||
int objc_write_object_reference (TypedStream* stream, id object);
|
||||
int objc_write_root_object (TypedStream* stream, id object);
|
||||
|
||||
long objc_get_stream_class_version (TypedStream* stream, Class class);
|
||||
|
||||
|
||||
/*
|
||||
** Convenience functions
|
||||
*/
|
||||
|
||||
int objc_write_array (TypedStream* stream, const char* type,
|
||||
int count, const void* data);
|
||||
int objc_read_array (TypedStream* stream, const char* type,
|
||||
int count, void* data);
|
||||
|
||||
int objc_write_object (TypedStream* stream, id object);
|
||||
int objc_read_object (TypedStream* stream, id* object);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Open a typed stream for reading or writing. MODE may be either of
|
||||
** OBJC_READONLY or OBJC_WRITEONLY.
|
||||
*/
|
||||
|
||||
TypedStream* objc_open_typed_stream (FILE* physical, int mode);
|
||||
TypedStream* objc_open_typed_stream_for_file (const char* file_name, int mode);
|
||||
|
||||
void objc_close_typed_stream (TypedStream* stream);
|
||||
|
||||
BOOL objc_end_of_typed_stream (TypedStream* stream);
|
||||
void objc_flush_typed_stream (TypedStream* stream);
|
||||
|
||||
#endif /* not __typedstream_INCLUDE_GNU */
|
Loading…
Reference in New Issue
Block a user