Commit a39d31bc by Kresten Krab Thorup

This patch makes selectors in the Objective-C language be pointers to a struct { void *sel_id...

        This patch makes selectors in the Objective-C language be pointers
        to a struct { void *sel_id, char *sel_types }, where the sel_types
        element is the type encoding of the method arguments.

From-SVN: r7622
parent 7a1dd323
/* Interface for the Object class for Objective-C.
Copyright (C) 1993 Free Software Foundation, Inc.
Copyright (C) 1993, 1994 Free Software Foundation, Inc.
This file is part of GNU CC.
......@@ -93,8 +93,8 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
- - perform:(SEL)aSel with:anObject1 with:anObject2;
/* Forwarding */
- - forward:(SEL)aSel :(arglist_t)argFrame;
- - performv:(SEL)aSel :(arglist_t)argFrame;
- (retval_t)forward:(SEL)aSel :(arglist_t)argFrame;
- (retval_t)performv:(SEL)aSel :(arglist_t)argFrame;
/* Posing */
+ + poseAs:(Class*)aClassObject;
......@@ -112,14 +112,10 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/* Archiving */
+ (int)version;
+ + setVersion:(int)aVersion;
#ifndef __alpha__ /* TypedStream not supported on alpha yet. */
+ (int)streamVersion: (TypedStream*)aStream;
- - read: (TypedStream*)aStream;
- - write: (TypedStream*)aStream;
#endif
- - awake;
@end
......
......@@ -132,6 +132,27 @@ objc_get_class (const char *name)
abort();
}
/* 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)
{
/* make sure the table is there */
assert(__objc_class_hash);
*(node_ptr*)enum_state =
hash_next(__objc_class_hash, *(node_ptr*)enum_state);
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
......@@ -217,6 +238,9 @@ void __objc_resolve_class_links()
Class*
class_pose_as (Class* impostor, Class* super_class)
{
node_ptr node;
Class* class1;
if (!CLS_ISRESOLV (impostor))
__objc_resolve_class_links ();
......@@ -230,19 +254,13 @@ class_pose_as (Class* impostor, Class* super_class)
{
Class **subclass = &(super_class->subclass_list);
BOOL super_is_base_class = NO;
/* move subclasses of super_class to impostor */
while (*subclass)
{
Class *nextSub = (*subclass)->sibling_class;
/* this happens when super_class is a base class */
if (*subclass == CLASSOF (super_class))
{
super_is_base_class = YES;
}
else if (*subclass != impostor)
if (*subclass != impostor)
{
Class *sub = *subclass;
......@@ -250,11 +268,18 @@ class_pose_as (Class* impostor, Class* super_class)
sub->sibling_class = impostor->subclass_list;
sub->super_class = impostor;
impostor->subclass_list = sub;
/* meta classes */
CLASSOF (sub)->sibling_class = CLASSOF (impostor)->subclass_list;
CLASSOF (sub)->super_class = CLASSOF (impostor);
CLASSOF (impostor)->subclass_list = CLASSOF (sub);
/* It will happen that SUB is not a class object if it is
the top of the meta class hierachy chain. (root
meta-class objects inherit theit class object) If that is
the case... dont 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;
......@@ -267,66 +292,31 @@ class_pose_as (Class* impostor, Class* super_class)
/* set impostor to have no sibling classes */
impostor->sibling_class = 0;
CLASSOF (impostor)->sibling_class = 0;
/* impostor has a sibling... */
if (super_is_base_class)
{
CLASSOF (super_class)->sibling_class = 0;
impostor->sibling_class = CLASSOF (super_class);
}
}
/* check relationship of impostor and super_class */
/* check relationship of impostor and super_class is kept. */
assert (impostor->super_class == super_class);
assert (CLASSOF (impostor)->super_class == CLASSOF (super_class));
/* by now, the re-organization of the class hierachy
is done. We only need to update various tables. */
/* First, we change the names in the hash table.
This will change the behavior of objc_get_class () */
{
char* buffer = (char*) __objc_xmalloc(strlen (super_class->name) + 2);
strcpy (buffer+1, super_class->name);
buffer[0] = '*';
/* This is how to update the lookup table. Regardless of
what the keys of the hashtable is, change all values that are
suprecalss into impostor. */
/* keep on prepending '*' until the name is unique */
while (hash_value_for_key (__objc_class_hash, buffer))
{
char *bbuffer = (char*) __objc_xmalloc (strlen (buffer)+2);
strcpy (bbuffer+1, buffer);
bbuffer[0] = '*';
free (buffer);
buffer = bbuffer;
}
hash_remove (__objc_class_hash, super_class->name);
hash_add (&__objc_class_hash, buffer, super_class);
hash_add (&__objc_class_hash, super_class->name, impostor);
/* Note that -name and +name will still respond with
the same strings as before. This way any
-isKindOfGivenName: will always work. */
}
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 */
}
}
/* next, we update the dispatch tables... */
{
Class *subclass;
for (subclass = impostor->subclass_list;
subclass; subclass = subclass->sibling_class)
{
/* we use the opportunity to check what we did */
assert (subclass->super_class == impostor);
assert (CLASSOF (subclass)->super_class == CLASSOF (impostor));
__objc_update_dispatch_table_for_class (CLASSOF (subclass));
__objc_update_dispatch_table_for_class (subclass);
}
}
__objc_update_dispatch_table_for_class (CLASSOF (impostor));
__objc_update_dispatch_table_for_class (impostor);
return impostor;
}
......@@ -35,6 +35,10 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
({ 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); })
static inline int
atoi (const char* str)
......@@ -121,8 +125,7 @@ objc_sizeof_type(const char* type)
while (*type != _C_STRUCT_E);
{
align = objc_alignof_type (type); /* padd to alignment */
if ((acc_size % align) != 0)
acc_size += align - (acc_size % align);
acc_size += ROUND (acc_size, align);
acc_size += objc_sizeof_type (type); /* add component size */
type = objc_skip_typespec (type); /* skip component */
}
......@@ -244,11 +247,7 @@ objc_aligned_size (const char* type)
{
int size = objc_sizeof_type (type);
int align = objc_alignof_type (type);
if ((size % align) != 0)
return size + align - (size % align);
else
return size;
return ROUND (size, align);
}
/*
......@@ -262,10 +261,7 @@ objc_promoted_size (const char* type)
int size = objc_sizeof_type (type);
int wordsize = sizeof (void*);
if ((size % wordsize) != 0)
return size + wordsize - (size % wordsize);
else
return size;
return ROUND (size, wordsize);
}
/*
......
......@@ -39,6 +39,8 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#define EXPANSION(cache) \
((cache)->size * 2)
void *__objc_xcalloc (size_t, size_t);
cache_ptr
hash_new (unsigned int size, hash_func_type hash_func,
compare_func_type compare_func)
......
......@@ -28,7 +28,7 @@ You should have received a copy of the GNU General Public License along with
/* The version number of this runtime. This must match the number
defined in gcc (objc-act.c) */
#define OBJC_VERSION 5
#define OBJC_VERSION 6
#define PROTOCOL_VERSION 2
/* This list contains all modules currently loaded into the runtime */
......@@ -49,6 +49,11 @@ static void __objc_class_add_protocols (Class*, struct objc_protocol_list*);
/* Is all categories/classes resolved? */
BOOL __objc_dangling_categories = NO;
extern SEL
__sel_register_typed_name (const char *name, const char *types,
struct objc_selector *orig);
/* 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
......@@ -70,7 +75,7 @@ __objc_exec_class (Module_t module)
struct objc_list** cell;
/* The table of selector references for this module */
SEL *selectors = symtab->refs;
SEL selectors = symtab->refs;
/* dummy counter */
int i;
......@@ -91,6 +96,19 @@ __objc_exec_class (Module_t module)
/* Save the module pointer for later processing. (not currently used) */
__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;
__sel_register_typed_name (name, type,
(struct objc_selector*)&(selectors[i]));
}
}
/* 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)
......@@ -117,13 +135,6 @@ __objc_exec_class (Module_t module)
__objc_init_protocols (class->protocols);
}
/* Replace referenced selectors from names to SEL's. */
if (selectors)
{
for (i = 0; selectors[i]; ++i)
selectors[i] = sel_register_name ((const char *) selectors[i]);
}
/* Process category information from the module. */
for (i = 0; i < symtab->cat_def_cnt; ++i)
{
......@@ -166,7 +177,7 @@ __objc_exec_class (Module_t module)
categories to objects. */
for (cell = &unclaimed_categories;
*cell;
*cell && ((cell = &(*cell)->tail)))
({ if (*cell) cell = &(*cell)->tail; }))
{
Category_t category = (*cell)->head;
Class* class = objc_lookup_class (category->class_name);
......@@ -209,7 +220,7 @@ static void init_check_module_version(Module_t module)
if ((module->version != OBJC_VERSION) || (module->size != sizeof (Module)))
{
fprintf (stderr, "Module %s version %d doesn't match runtime %d\n",
module->name, module->version, OBJC_VERSION);
module->name, (int)module->version, OBJC_VERSION);
if(module->version > OBJC_VERSION)
fprintf (stderr, "Runtime (libobjc.a) is out of date\n");
else if (module->version < OBJC_VERSION)
......@@ -255,7 +266,7 @@ __objc_init_protocols (struct objc_protocol_list* protos)
{
fprintf (stderr,
"Version %d doesn't match runtime protocol version %d\n",
((size_t)protos->list[i]->class_pointer),
(int)((char*)protos->list[i]->class_pointer-(char*)0),
PROTOCOL_VERSION);
abort ();
}
......
......@@ -30,6 +30,12 @@ void objc_error(id object, const char* fmt, va_list);
void (*_objc_error)(id, const char*, va_list) = objc_error;
#ifdef __alpha__
#include <stdlib.h>
extern int write (int, const char*, int);
extern size_t strlen (const char*);
#endif
void
objc_error(id object, const char* fmt, va_list ap)
{
......@@ -40,7 +46,7 @@ objc_error(id object, const char* fmt, va_list ap)
volatile void
objc_fatal(const char* msg)
{
write(2, msg, (size_t)strlen((char*)msg));
write(2, msg, (int)strlen((const char*)msg));
abort();
}
......@@ -65,8 +71,12 @@ __objc_xrealloc(void* mem, size_t size)
void*
__objc_xcalloc(size_t nelem, size_t size)
{
void* res = (void*)calloc(nelem, size);
#ifdef __alpha__
extern bzero (void *, size_t);
#endif
void* res = (void*)malloc(nelem * size);
if(!res)
objc_fatal("Virtual memory exhausted\n");
bzero (res, nelem * size);
return res;
}
......@@ -44,8 +44,6 @@ struct objc_method_description
char *types; /* type encoding */
};
/* Filer types used to describe Ivars and Methods. */
#define _C_ID '@'
#define _C_CLASS '#'
......@@ -92,7 +90,7 @@ extern BOOL objc_trace;
*/
typedef struct objc_symtab {
unsigned long sel_ref_cnt; /* Unknown. */
SEL *refs; /* 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
......@@ -146,7 +144,7 @@ typedef struct objc_ivar_list {
const char* ivar_type; /* Description of the Ivar's
type. Useful for
debuggers. */
int ivar_offset; /* Byte offset from the base
int ivar_offset; /* Byte offset from the base
address of the instance
structure to the variable. */
......@@ -169,7 +167,7 @@ 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
int method_count; /* Number of methods defined in
this structure. */
struct objc_method {
SEL method_name; /* This variable is the method's
......@@ -303,12 +301,23 @@ 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_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);
......
......@@ -40,12 +40,24 @@ typedef char BOOL;
#define NO (BOOL)0
/*
** Definition of a selector. Selectors are really of type unsigned int.
** The runtime does this mapping from SEL's to names internally in the
** sel_... operations. You should never use the fact that it is actually
** an integer, since other Objective-C implementations use other conventions.
** Definition of a selector. Selectors themselves are not unique, but
** the sel_id is a unique identifier.
*/
typedef void* SEL;
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.
......@@ -131,36 +143,8 @@ typedef union {
} *arglist_t; /* argument frame */
#if defined(__OBJC__)
#include "objc/sarray.h"
/*
This is the function called when messages are send to nil. You may
set a breakpoint in your debugger at this function to catch messages
too nil.
*/
extern id nil_method(id rcv, SEL op, ...);
/*
The messager is inlined, thus it is defined here directly. The
inlining is quite time-consuming when optimizing. This will be
taken care of later by hand-coding the messager in the compiler.
*/
extern __inline__ IMP
objc_msg_lookup(id receiver, SEL op)
{
if(receiver)
return sarray_get(receiver->class_pointer->dtable, (size_t)(op));
else
return nil_method;
}
#else
IMP objc_msg_lookup(id receiver, SEL op);
#endif
#ifdef __cplusplus
}
#endif
......
......@@ -43,7 +43,7 @@ class_create_instance(Class* class)
new = (*_objc_object_alloc)(class);
if (new!=nil)
{
bzero (new, class->instance_size);
memchr (new, 0, class->instance_size);
new->class_pointer = class;
}
return new;
......
......@@ -61,12 +61,14 @@ extern BOOL __objc_class_links_resolved;
extern int __objc_selector_max_index;
#ifdef DEBUG
#define DEBUG_PRINTF printf
#define DEBUG_PRINTF(format, args...) printf (format, ## args)
#else
#define DEBUG_PRINTF
#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*);
#endif /* not __objc_runtime_INCLUDE_GNU */
......
......@@ -40,6 +40,11 @@ const char* __objc_sparse2_id = "2 level sparse indices";
const char* __objc_sparse3_id = "3 level sparse indices";
#endif
#ifdef __alpha__
const void *memcpy (void*, const void*, size_t);
void free (const void*);
#endif
void
sarray_at_put(struct sarray* array, sidx index, void* element)
{
......@@ -117,7 +122,7 @@ sarray_at_put(struct sarray* array, sidx index, void* element)
/* The bucket was previously empty (or something like that), */
/* allocate a new. This is the effect of `lazy' allocation */
*the_bucket = (struct sbucket*)__objc_xmalloc(sizeof(struct sbucket));
memcpy( *the_bucket,array->empty_bucket, sizeof(struct sbucket));
memcpy((void *) *the_bucket, (const void*)array->empty_bucket, sizeof(struct sbucket));
(*the_bucket)->version = array->version;
nbuckets += 1;
......
......@@ -31,7 +31,8 @@ You should have received a copy of the GNU General Public License along with
#define SELECTOR_HASH_SIZE 128
/* Tables mapping selector names to uid and opposite */
static struct sarray* __objc_selector_array = 0; /* uid -> name */
static struct sarray* __objc_selector_array = 0; /* uid -> sel */
static struct sarray* __objc_selector_names = 0; /* uid -> name */
static cache_ptr __objc_selector_hash = 0; /* name -> uid */
static void register_selectors_from_list(MethodList_t);
......@@ -42,6 +43,7 @@ int __objc_selector_max_index = 0;
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,
......@@ -78,16 +80,67 @@ register_selectors_from_list (MethodList_t method_list)
while (i < method_list->method_count)
{
Method_t method = &method_list->method_list[i];
method->method_name = sel_register_name ((char*)method->method_name);
method->method_name
= sel_register_typed_name ((const char*)method->method_name,
method->method_types);
i += 1;
}
}
/* return selector representing name */
SEL
sel_get_typed_uid (const char *name, const char *types)
{
struct objc_list *l;
sidx i;
i = (sidx) hash_value_for_key (__objc_selector_hash, name);
if (i == 0)
return 0;
for (l = (struct objc_list*)sarray_get (__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)
{
return s;
}
}
else if (! strcmp (s->sel_types, types))
{
return s;
}
}
return 0;
}
/* return selector representing name */
SEL
sel_get_any_uid (const char *name)
{
struct objc_list *l;
sidx i;
i = (sidx) hash_value_for_key (__objc_selector_hash, name);
if (soffset_decode (i) == 0)
return 0;
l = (struct objc_list*)sarray_get (__objc_selector_array, i);
if (l == 0)
return 0;
return (SEL)l->head;
}
/* return selector representing name */
SEL
sel_get_uid (const char *name)
{
return (SEL) hash_value_for_key (__objc_selector_hash, name);
return sel_register_typed_name (name, 0);
}
/* Get name of selector. If selector is unknown, the empty string ""
......@@ -95,45 +148,123 @@ sel_get_uid (const char *name)
const char*
sel_get_name (SEL selector)
{
if ((soffset_decode((sidx)selector) > 0)
&& (soffset_decode((sidx)selector) <= __objc_selector_max_index))
return sarray_get (__objc_selector_array, (sidx) selector);
if ((soffset_decode((sidx)selector->sel_id) > 0)
&& (soffset_decode((sidx)selector->sel_id) <= __objc_selector_max_index))
return sarray_get (__objc_selector_array, (sidx) selector->sel_id);
else
return NULL;
return 0;
}
BOOL
sel_is_mapped (SEL selector)
{
unsigned int idx = soffset_decode ((sidx)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). */
SEL
sel_register_name (const char *sel)
__sel_register_typed_name (const char *name, const char *types,
struct objc_selector *orig)
{
SEL j;
struct objc_selector* j;
sidx i;
struct objc_list *l;
if ((j = sel_get_uid ((const char *) sel)))
return j;
/* Save the selector name. */
__objc_selector_max_index += 1;
i = soffset_encode(__objc_selector_max_index);
i = (sidx) hash_value_for_key (__objc_selector_hash, name);
if (soffset_decode (i) != 0)
{
for (l = (struct objc_list*)sarray_get (__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_xmalloc (sizeof (struct objc_selector));
DEBUG_PRINTF ("Record selector %s as: %#x\n", sel, i);
j->sel_id = (void*)i;
j->sel_types = (const char*)types;
l = (struct objc_list*)sarray_get (__objc_selector_array, i);
}
else
{
__objc_selector_max_index += 1;
i = soffset_encode(__objc_selector_max_index);
if (orig)
j = orig;
else
j = __objc_xmalloc (sizeof (struct objc_selector));
j->sel_id = (void*)i;
j->sel_types = (const char*)types;
l = 0;
}
sarray_at_put_safe (__objc_selector_array, i, (void *) sel);
hash_add (&__objc_selector_hash, (void *) sel, (void *) i);
DEBUG_PRINTF ("Record selector %s[%s] as: %ld\n", name, types,
soffset_decode (i));
{
int is_new = (l == 0);
l = list_cons ((void*)j, l);
sarray_at_put_safe (__objc_selector_names, i, (void *) name);
sarray_at_put_safe (__objc_selector_array, i, (void *) l);
if (is_new)
hash_add (&__objc_selector_hash, (void *) name, (void *) i);
}
sarray_realloc(__objc_uninstalled_dtable, __objc_selector_max_index+1);
return (SEL) i;
return (SEL) j;
}
SEL
sel_register_name (const char *name)
{
return __sel_register_typed_name (name, 0, 0);
}
SEL
sel_register_typed_name (const char *name, const char *type)
{
return __sel_register_typed_name (name, type, 0);
}
......@@ -30,8 +30,6 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "objc/hash.h"
#include <stdio.h>
#ifndef __alpha__ /* alpha is currently not supported */
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*);
......@@ -100,7 +98,7 @@ 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);
int objc_get_stream_class_version (TypedStream* stream, Class* class);
long objc_get_stream_class_version (TypedStream* stream, Class* class);
/*
......@@ -130,6 +128,4 @@ void objc_close_typed_stream (TypedStream* stream);
BOOL objc_end_of_typed_stream (TypedStream* stream);
void objc_flush_typed_stream (TypedStream* stream);
#endif /* __alpha__ */
#endif /* not __typedstream_INCLUDE_GNU */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment