gcc/libobjc/archive.c
2009-04-09 17:00:19 +02:00

1667 lines
40 KiB
C

/* GNU Objective C Runtime archiving
Copyright (C) 1993, 1995, 1996, 1997, 2002, 2004, 2009 Free Software Foundation, Inc.
Contributed by Kresten Krab Thorup
This file is part of GCC.
GCC 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 3, or (at your option) any later version.
GCC 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.
Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
#include "tconfig.h"
#include "objc/runtime.h"
#include "objc/typedstream.h"
#include "objc/encoding.h"
#include <stdlib.h>
extern int fflush (FILE *);
#define ROUND(V, A) \
({ typeof (V) __v = (V); typeof (A) __a = (A); \
__a * ((__v + __a - 1)/__a); })
#define PTR2LONG(P) (((char *) (P))-(char *) 0)
#define LONG2PTR(L) (((char *) 0) + (L))
/* Declare some functions... */
static int
objc_read_class (struct objc_typed_stream *stream, Class *class);
int objc_sizeof_type (const char *type);
static int
objc_write_use_common (struct objc_typed_stream *stream, unsigned long key);
static int
objc_write_register_common (struct objc_typed_stream *stream,
unsigned long key);
static int
objc_write_class (struct objc_typed_stream *stream,
struct objc_class *class);
const char *objc_skip_type (const char *type);
static void __objc_finish_write_root_object (struct objc_typed_stream *);
static void __objc_finish_read_root_object (struct objc_typed_stream *);
static inline int
__objc_code_unsigned_char (unsigned char *buf, unsigned char val)
{
if ((val&_B_VALUE) == val)
{
buf[0] = val|_B_SINT;
return 1;
}
else
{
buf[0] = _B_NINT|0x01;
buf[1] = val;
return 2;
}
}
int
objc_write_unsigned_char (struct objc_typed_stream *stream,
unsigned char value)
{
unsigned char buf[sizeof (unsigned char) + 1];
int len = __objc_code_unsigned_char (buf, value);
return (*stream->write) (stream->physical, (char*)buf, len);
}
static inline int
__objc_code_char (unsigned char *buf, signed char val)
{
if (val >= 0)
return __objc_code_unsigned_char (buf, val);
else
{
buf[0] = _B_NINT|_B_SIGN|0x01;
buf[1] = -val;
return 2;
}
}
int
objc_write_char (struct objc_typed_stream *stream, signed char value)
{
unsigned char buf[sizeof (char) + 1];
int len = __objc_code_char (buf, value);
return (*stream->write) (stream->physical, (char*)buf, len);
}
static inline int
__objc_code_unsigned_short (unsigned char *buf, unsigned short val)
{
if ((val&_B_VALUE) == val)
{
buf[0] = val|_B_SINT;
return 1;
}
else
{
int c, b;
buf[0] = _B_NINT;
for (c = sizeof (short); c != 0; c -= 1)
if (((val >> (8*(c - 1)))%0x100) != 0)
break;
buf[0] |= c;
for (b = 1; c != 0; c--, b++)
{
buf[b] = (val >> (8*(c - 1)))%0x100;
}
return b;
}
}
int
objc_write_unsigned_short (struct objc_typed_stream *stream,
unsigned short value)
{
unsigned char buf[sizeof (unsigned short) + 1];
int len = __objc_code_unsigned_short (buf, value);
return (*stream->write) (stream->physical, (char*)buf, len);
}
static inline int
__objc_code_short (unsigned char *buf, short val)
{
int sign = (val < 0);
int size = __objc_code_unsigned_short (buf, sign ? -val : val);
if (sign)
buf[0] |= _B_SIGN;
return size;
}
int
objc_write_short (struct objc_typed_stream *stream, short value)
{
unsigned char buf[sizeof (short) + 1];
int len = __objc_code_short (buf, value);
return (*stream->write) (stream->physical, (char*)buf, len);
}
static inline int
__objc_code_unsigned_int (unsigned char *buf, unsigned int val)
{
if ((val&_B_VALUE) == val)
{
buf[0] = val|_B_SINT;
return 1;
}
else
{
int c, b;
buf[0] = _B_NINT;
for (c = sizeof (int); c != 0; c -= 1)
if (((val >> (8*(c - 1)))%0x100) != 0)
break;
buf[0] |= c;
for (b = 1; c != 0; c--, b++)
{
buf[b] = (val >> (8*(c-1)))%0x100;
}
return b;
}
}
int
objc_write_unsigned_int (struct objc_typed_stream *stream, unsigned int value)
{
unsigned char buf[sizeof (unsigned int) + 1];
int len = __objc_code_unsigned_int (buf, value);
return (*stream->write) (stream->physical, (char*)buf, len);
}
static inline int
__objc_code_int (unsigned char *buf, int val)
{
int sign = (val < 0);
int size = __objc_code_unsigned_int (buf, sign ? -val : val);
if (sign)
buf[0] |= _B_SIGN;
return size;
}
int
objc_write_int (struct objc_typed_stream *stream, int value)
{
unsigned char buf[sizeof (int) + 1];
int len = __objc_code_int (buf, value);
return (*stream->write) (stream->physical, (char*)buf, len);
}
static inline int
__objc_code_unsigned_long (unsigned char *buf, unsigned long val)
{
if ((val&_B_VALUE) == val)
{
buf[0] = val|_B_SINT;
return 1;
}
else
{
int c, b;
buf[0] = _B_NINT;
for (c = sizeof (long); c != 0; c -= 1)
if (((val >> (8*(c - 1)))%0x100) != 0)
break;
buf[0] |= c;
for (b = 1; c != 0; c--, b++)
{
buf[b] = (val >> (8*(c - 1)))%0x100;
}
return b;
}
}
int
objc_write_unsigned_long (struct objc_typed_stream *stream,
unsigned long value)
{
unsigned char buf[sizeof (unsigned long) + 1];
int len = __objc_code_unsigned_long (buf, value);
return (*stream->write) (stream->physical, (char*)buf, len);
}
static inline int
__objc_code_long (unsigned char *buf, long val)
{
int sign = (val < 0);
int size = __objc_code_unsigned_long (buf, sign ? -val : val);
if (sign)
buf[0] |= _B_SIGN;
return size;
}
int
objc_write_long (struct objc_typed_stream *stream, long value)
{
unsigned char buf[sizeof (long) + 1];
int len = __objc_code_long (buf, value);
return (*stream->write) (stream->physical, (char*)buf, len);
}
int
objc_write_string (struct objc_typed_stream *stream,
const unsigned char *string, unsigned int nbytes)
{
unsigned char buf[sizeof (unsigned int) + 1];
int len = __objc_code_unsigned_int (buf, nbytes);
if ((buf[0]&_B_CODE) == _B_SINT)
buf[0] = (buf[0]&_B_VALUE)|_B_SSTR;
else /* _B_NINT */
buf[0] = (buf[0]&_B_VALUE)|_B_NSTR;
if ((*stream->write) (stream->physical, (char*)buf, len) != 0)
return (*stream->write) (stream->physical, (char*)string, nbytes);
else
return 0;
}
int
objc_write_string_atomic (struct objc_typed_stream *stream,
unsigned char *string, unsigned int nbytes)
{
unsigned long key;
if ((key = PTR2LONG(objc_hash_value_for_key (stream->stream_table, string))))
return objc_write_use_common (stream, key);
else
{
int length;
objc_hash_add (&stream->stream_table,
LONG2PTR(key=PTR2LONG(string)), string);
if ((length = objc_write_register_common (stream, key)))
return objc_write_string (stream, string, nbytes);
return length;
}
}
static int
objc_write_register_common (struct objc_typed_stream *stream,
unsigned long key)
{
unsigned char buf[sizeof (unsigned long)+2];
int len = __objc_code_unsigned_long (buf + 1, key);
if (len == 1)
{
buf[0] = _B_RCOMM|0x01;
buf[1] &= _B_VALUE;
return (*stream->write) (stream->physical, (char*)buf, len + 1);
}
else
{
buf[1] = (buf[1]&_B_VALUE)|_B_RCOMM;
return (*stream->write) (stream->physical, (char*)buf + 1, len);
}
}
static int
objc_write_use_common (struct objc_typed_stream *stream, unsigned long key)
{
unsigned char buf[sizeof (unsigned long)+2];
int len = __objc_code_unsigned_long (buf + 1, key);
if (len == 1)
{
buf[0] = _B_UCOMM|0x01;
buf[1] &= _B_VALUE;
return (*stream->write) (stream->physical, (char*)buf, 2);
}
else
{
buf[1] = (buf[1]&_B_VALUE)|_B_UCOMM;
return (*stream->write) (stream->physical, (char*)buf + 1, len);
}
}
static inline int
__objc_write_extension (struct objc_typed_stream *stream, unsigned char code)
{
if (code <= _B_VALUE)
{
unsigned char buf = code|_B_EXT;
return (*stream->write) (stream->physical, (char*)&buf, 1);
}
else
{
objc_error (nil, OBJC_ERR_BAD_OPCODE,
"__objc_write_extension: bad opcode %c\n", code);
return -1;
}
}
inline int
__objc_write_object (struct objc_typed_stream *stream, id object)
{
unsigned char buf = '\0';
SEL write_sel = sel_get_any_uid ("write:");
if (object)
{
__objc_write_extension (stream, _BX_OBJECT);
objc_write_class (stream, object->class_pointer);
(*objc_msg_lookup (object, write_sel)) (object, write_sel, stream);
return (*stream->write) (stream->physical, (char*)&buf, 1);
}
else
return objc_write_use_common (stream, 0);
}
int
objc_write_object_reference (struct objc_typed_stream *stream, id object)
{
unsigned long key;
if ((key = PTR2LONG(objc_hash_value_for_key (stream->object_table, object))))
return objc_write_use_common (stream, key);
__objc_write_extension (stream, _BX_OBJREF);
return objc_write_unsigned_long (stream, PTR2LONG (object));
}
int
objc_write_root_object (struct objc_typed_stream *stream, id object)
{
int len = 0;
if (stream->writing_root_p)
objc_error (nil, OBJC_ERR_RECURSE_ROOT,
"objc_write_root_object called recursively");
else
{
stream->writing_root_p = 1;
__objc_write_extension (stream, _BX_OBJROOT);
if ((len = objc_write_object (stream, object)))
__objc_finish_write_root_object (stream);
stream->writing_root_p = 0;
}
return len;
}
int
objc_write_object (struct objc_typed_stream *stream, id object)
{
unsigned long key;
if ((key = PTR2LONG(objc_hash_value_for_key (stream->object_table, object))))
return objc_write_use_common (stream, key);
else if (object == nil)
return objc_write_use_common (stream, 0);
else
{
int length;
objc_hash_add (&stream->object_table,
LONG2PTR(key=PTR2LONG(object)), object);
if ((length = objc_write_register_common (stream, key)))
return __objc_write_object (stream, object);
return length;
}
}
inline int
__objc_write_class (struct objc_typed_stream *stream, struct objc_class *class)
{
__objc_write_extension (stream, _BX_CLASS);
objc_write_string_atomic (stream, (unsigned char *) class->name,
strlen ((char *) class->name));
return objc_write_unsigned_long (stream, class->version);
}
static int
objc_write_class (struct objc_typed_stream *stream,
struct objc_class *class)
{
unsigned long key;
if ((key = PTR2LONG(objc_hash_value_for_key (stream->stream_table, class))))
return objc_write_use_common (stream, key);
else
{
int length;
objc_hash_add (&stream->stream_table,
LONG2PTR(key = PTR2LONG(class)), class);
if ((length = objc_write_register_common (stream, key)))
return __objc_write_class (stream, class);
return length;
}
}
inline int
__objc_write_selector (struct objc_typed_stream *stream, SEL selector)
{
const char *sel_name;
__objc_write_extension (stream, _BX_SEL);
/* to handle NULL selectors */
if ((SEL)0 == selector)
return objc_write_string (stream, (unsigned char*)"", 0);
sel_name = sel_get_name (selector);
return objc_write_string (stream, (unsigned char*)sel_name, strlen ((char*)sel_name));
}
int
objc_write_selector (struct objc_typed_stream *stream, SEL selector)
{
const char *sel_name;
unsigned long key;
/* to handle NULL selectors */
if ((SEL)0 == selector)
return __objc_write_selector (stream, selector);
sel_name = sel_get_name (selector);
if ((key = PTR2LONG(objc_hash_value_for_key (stream->stream_table,
sel_name))))
return objc_write_use_common (stream, key);
else
{
int length;
objc_hash_add (&stream->stream_table,
LONG2PTR(key = PTR2LONG(sel_name)), (char *) sel_name);
if ((length = objc_write_register_common (stream, key)))
return __objc_write_selector (stream, selector);
return length;
}
}
/*
** Read operations
*/
inline int
objc_read_char (struct objc_typed_stream *stream, char *val)
{
unsigned char buf;
int len;
len = (*stream->read) (stream->physical, (char*)&buf, 1);
if (len != 0)
{
if ((buf & _B_CODE) == _B_SINT)
(*val) = (buf & _B_VALUE);
else if ((buf & _B_NUMBER) == 1)
{
len = (*stream->read) (stream->physical, val, 1);
if (buf&_B_SIGN)
(*val) = -1 * (*val);
}
else
objc_error (nil, OBJC_ERR_BAD_DATA,
"expected 8bit signed int, got %dbit int",
(int) (buf&_B_NUMBER)*8);
}
return len;
}
inline int
objc_read_unsigned_char (struct objc_typed_stream *stream, unsigned char *val)
{
unsigned char buf;
int len;
if ((len = (*stream->read) (stream->physical, (char*)&buf, 1)))
{
if ((buf & _B_CODE) == _B_SINT)
(*val) = (buf & _B_VALUE);
else if ((buf & _B_NUMBER) == 1)
len = (*stream->read) (stream->physical, (char*)val, 1);
else
objc_error (nil, OBJC_ERR_BAD_DATA,
"expected 8bit unsigned int, got %dbit int",
(int) (buf&_B_NUMBER)*8);
}
return len;
}
inline int
objc_read_short (struct objc_typed_stream *stream, short *value)
{
unsigned char buf[sizeof (short) + 1];
int len;
if ((len = (*stream->read) (stream->physical, (char*)buf, 1)))
{
if ((buf[0] & _B_CODE) == _B_SINT)
(*value) = (buf[0] & _B_VALUE);
else
{
int pos = 1;
int nbytes = buf[0] & _B_NUMBER;
if (nbytes > (int) sizeof (short))
objc_error (nil, OBJC_ERR_BAD_DATA,
"expected short, got bigger (%dbits)", nbytes*8);
len = (*stream->read) (stream->physical, (char*)buf + 1, nbytes);
(*value) = 0;
while (pos <= nbytes)
(*value) = ((*value)*0x100) + buf[pos++];
if (buf[0] & _B_SIGN)
(*value) = -(*value);
}
}
return len;
}
inline int
objc_read_unsigned_short (struct objc_typed_stream *stream,
unsigned short *value)
{
unsigned char buf[sizeof (unsigned short) + 1];
int len;
if ((len = (*stream->read) (stream->physical, (char*)buf, 1)))
{
if ((buf[0] & _B_CODE) == _B_SINT)
(*value) = (buf[0] & _B_VALUE);
else
{
int pos = 1;
int nbytes = buf[0] & _B_NUMBER;
if (nbytes > (int) sizeof (short))
objc_error (nil, OBJC_ERR_BAD_DATA,
"expected short, got int or bigger");
len = (*stream->read) (stream->physical, (char*)buf + 1, nbytes);
(*value) = 0;
while (pos <= nbytes)
(*value) = ((*value)*0x100) + buf[pos++];
}
}
return len;
}
inline int
objc_read_int (struct objc_typed_stream *stream, int *value)
{
unsigned char buf[sizeof (int) + 1];
int len;
if ((len = (*stream->read) (stream->physical, (char*)buf, 1)))
{
if ((buf[0] & _B_CODE) == _B_SINT)
(*value) = (buf[0] & _B_VALUE);
else
{
int pos = 1;
int nbytes = buf[0] & _B_NUMBER;
if (nbytes > (int) sizeof (int))
objc_error (nil, OBJC_ERR_BAD_DATA, "expected int, got bigger");
len = (*stream->read) (stream->physical, (char*)buf + 1, nbytes);
(*value) = 0;
while (pos <= nbytes)
(*value) = ((*value)*0x100) + buf[pos++];
if (buf[0] & _B_SIGN)
(*value) = -(*value);
}
}
return len;
}
inline int
objc_read_long (struct objc_typed_stream *stream, long *value)
{
unsigned char buf[sizeof (long) + 1];
int len;
if ((len = (*stream->read) (stream->physical, (char*)buf, 1)))
{
if ((buf[0] & _B_CODE) == _B_SINT)
(*value) = (buf[0] & _B_VALUE);
else
{
int pos = 1;
int nbytes = buf[0] & _B_NUMBER;
if (nbytes > (int) sizeof (long))
objc_error (nil, OBJC_ERR_BAD_DATA, "expected long, got bigger");
len = (*stream->read) (stream->physical, (char*)buf + 1, nbytes);
(*value) = 0;
while (pos <= nbytes)
(*value) = ((*value)*0x100) + buf[pos++];
if (buf[0] & _B_SIGN)
(*value) = -(*value);
}
}
return len;
}
inline int
__objc_read_nbyte_uint (struct objc_typed_stream *stream,
unsigned int nbytes, unsigned int *val)
{
int len;
unsigned int pos = 0;
unsigned char buf[sizeof (unsigned int) + 1];
if (nbytes > sizeof (int))
objc_error (nil, OBJC_ERR_BAD_DATA, "expected int, got bigger");
len = (*stream->read) (stream->physical, (char*)buf, nbytes);
(*val) = 0;
while (pos < nbytes)
(*val) = ((*val)*0x100) + buf[pos++];
return len;
}
inline int
objc_read_unsigned_int (struct objc_typed_stream *stream,
unsigned int *value)
{
unsigned char buf[sizeof (unsigned int) + 1];
int len;
if ((len = (*stream->read) (stream->physical, (char*)buf, 1)))
{
if ((buf[0] & _B_CODE) == _B_SINT)
(*value) = (buf[0] & _B_VALUE);
else
len = __objc_read_nbyte_uint (stream, (buf[0] & _B_VALUE), value);
}
return len;
}
int
__objc_read_nbyte_ulong (struct objc_typed_stream *stream,
unsigned int nbytes, unsigned long *val)
{
int len;
unsigned int pos = 0;
unsigned char buf[sizeof (unsigned long) + 1];
if (nbytes > sizeof (long))
objc_error (nil, OBJC_ERR_BAD_DATA, "expected long, got bigger");
len = (*stream->read) (stream->physical, (char*)buf, nbytes);
(*val) = 0;
while (pos < nbytes)
(*val) = ((*val)*0x100) + buf[pos++];
return len;
}
inline int
objc_read_unsigned_long (struct objc_typed_stream *stream,
unsigned long *value)
{
unsigned char buf[sizeof (unsigned long) + 1];
int len;
if ((len = (*stream->read) (stream->physical, (char*)buf, 1)))
{
if ((buf[0] & _B_CODE) == _B_SINT)
(*value) = (buf[0] & _B_VALUE);
else
len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), value);
}
return len;
}
inline int
objc_read_string (struct objc_typed_stream *stream,
char **string)
{
unsigned char buf[sizeof (unsigned int) + 1];
int len;
if ((len = (*stream->read) (stream->physical, (char*)buf, 1)))
{
unsigned long key = 0;
if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */
{
len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key);
len = (*stream->read) (stream->physical, (char*)buf, 1);
}
switch (buf[0]&_B_CODE) {
case _B_SSTR:
{
int length = buf[0]&_B_VALUE;
(*string) = (char*)objc_malloc (length + 1);
if (key)
objc_hash_add (&stream->stream_table, LONG2PTR(key), *string);
len = (*stream->read) (stream->physical, *string, length);
(*string)[length] = '\0';
}
break;
case _B_UCOMM:
{
char *tmp;
len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key);
tmp = objc_hash_value_for_key (stream->stream_table, LONG2PTR (key));
*string = objc_malloc (strlen (tmp) + 1);
strcpy (*string, tmp);
}
break;
case _B_NSTR:
{
unsigned int nbytes = buf[0]&_B_VALUE;
len = __objc_read_nbyte_uint (stream, nbytes, &nbytes);
if (len) {
(*string) = (char*)objc_malloc (nbytes + 1);
if (key)
objc_hash_add (&stream->stream_table, LONG2PTR(key), *string);
len = (*stream->read) (stream->physical, *string, nbytes);
(*string)[nbytes] = '\0';
}
}
break;
default:
objc_error (nil, OBJC_ERR_BAD_DATA,
"expected string, got opcode %c\n", (buf[0]&_B_CODE));
}
}
return len;
}
int
objc_read_object (struct objc_typed_stream *stream, id *object)
{
unsigned char buf[sizeof (unsigned int)];
int len;
if ((len = (*stream->read) (stream->physical, (char*)buf, 1)))
{
SEL read_sel = sel_get_any_uid ("read:");
unsigned long key = 0;
if ((buf[0]&_B_CODE) == _B_RCOMM) /* register common */
{
len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key);
len = (*stream->read) (stream->physical, (char*)buf, 1);
}
if (buf[0] == (_B_EXT | _BX_OBJECT))
{
Class class;
/* get class */
len = objc_read_class (stream, &class);
/* create instance */
(*object) = class_create_instance (class);
/* register? */
if (key)
objc_hash_add (&stream->object_table, LONG2PTR(key), *object);
/* send -read: */
if (__objc_responds_to (*object, read_sel))
(*get_imp (class, read_sel)) (*object, read_sel, stream);
/* check null-byte */
len = (*stream->read) (stream->physical, (char*)buf, 1);
if (buf[0] != '\0')
objc_error (nil, OBJC_ERR_BAD_DATA,
"expected null-byte, got opcode %c", buf[0]);
}
else if ((buf[0]&_B_CODE) == _B_UCOMM)
{
if (key)
objc_error (nil, OBJC_ERR_BAD_KEY, "cannot register use upcode...");
len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key);
(*object) = objc_hash_value_for_key (stream->object_table,
LONG2PTR(key));
}
else if (buf[0] == (_B_EXT | _BX_OBJREF)) /* a forward reference */
{
struct objc_list *other;
len = objc_read_unsigned_long (stream, &key);
other
= (struct objc_list *) objc_hash_value_for_key (stream->object_refs,
LONG2PTR(key));
objc_hash_add (&stream->object_refs, LONG2PTR(key),
(void *)list_cons (object, other));
}
else if (buf[0] == (_B_EXT | _BX_OBJROOT)) /* a root object */
{
if (key)
objc_error (nil, OBJC_ERR_BAD_KEY,
"cannot register root object...");
len = objc_read_object (stream, object);
__objc_finish_read_root_object (stream);
}
else
objc_error (nil, OBJC_ERR_BAD_DATA,
"expected object, got opcode %c", buf[0]);
}
return len;
}
static int
objc_read_class (struct objc_typed_stream *stream, Class *class)
{
unsigned char buf[sizeof (unsigned int)];
int len;
if ((len = (*stream->read) (stream->physical, (char*)buf, 1)))
{
unsigned long key = 0;
if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */
{
len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key);
len = (*stream->read) (stream->physical, (char*)buf, 1);
}
if (buf[0] == (_B_EXT | _BX_CLASS))
{
char temp[1] = "";
char *class_name = temp;
unsigned long version;
/* get class */
len = objc_read_string (stream, &class_name);
(*class) = objc_get_class (class_name);
objc_free (class_name);
/* register */
if (key)
objc_hash_add (&stream->stream_table, LONG2PTR(key), *class);
objc_read_unsigned_long (stream, &version);
objc_hash_add (&stream->class_table,
(*class)->name, (void *) ((size_t) version));
}
else if ((buf[0]&_B_CODE) == _B_UCOMM)
{
if (key)
objc_error (nil, OBJC_ERR_BAD_KEY, "cannot register use upcode...");
len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key);
*class = objc_hash_value_for_key (stream->stream_table,
LONG2PTR(key));
if (! *class)
objc_error (nil, OBJC_ERR_BAD_CLASS,
"cannot find class for key %lu", key);
}
else
objc_error (nil, OBJC_ERR_BAD_DATA,
"expected class, got opcode %c", buf[0]);
}
return len;
}
int
objc_read_selector (struct objc_typed_stream *stream, SEL* selector)
{
unsigned char buf[sizeof (unsigned int)];
int len;
if ((len = (*stream->read) (stream->physical, (char*)buf, 1)))
{
unsigned long key = 0;
if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */
{
len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key);
len = (*stream->read) (stream->physical, (char*)buf, 1);
}
if (buf[0] == (_B_EXT|_BX_SEL)) /* selector! */
{
char temp[1] = "";
char *selector_name = temp;
/* get selector */
len = objc_read_string (stream, &selector_name);
/* To handle NULL selectors */
if (0 == strlen (selector_name))
{
(*selector) = (SEL)0;
return 0;
}
else
(*selector) = sel_get_any_uid (selector_name);
objc_free (selector_name);
/* register */
if (key)
objc_hash_add (&stream->stream_table,
LONG2PTR(key), (void *) *selector);
}
else if ((buf[0]&_B_CODE) == _B_UCOMM)
{
if (key)
objc_error (nil, OBJC_ERR_BAD_KEY, "cannot register use upcode...");
len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key);
(*selector) = objc_hash_value_for_key (stream->stream_table,
LONG2PTR(key));
}
else
objc_error (nil, OBJC_ERR_BAD_DATA,
"expected selector, got opcode %c", buf[0]);
}
return len;
}
/*
** USER LEVEL FUNCTIONS
*/
/*
** Write one object, encoded in TYPE and pointed to by DATA to the
** typed stream STREAM.
*/
int
objc_write_type (TypedStream *stream, const char *type, const void *data)
{
switch (*type) {
case _C_ID:
return objc_write_object (stream, *(id *) data);
break;
case _C_CLASS:
return objc_write_class (stream, *(Class *) data);
break;
case _C_SEL:
return objc_write_selector (stream, *(SEL *) data);
break;
case _C_CHR:
return objc_write_char (stream, *(signed char *) data);
break;
case _C_UCHR:
return objc_write_unsigned_char (stream, *(unsigned char *) data);
break;
case _C_SHT:
return objc_write_short (stream, *(short *) data);
break;
case _C_USHT:
return objc_write_unsigned_short (stream, *(unsigned short *) data);
break;
case _C_INT:
return objc_write_int (stream, *(int *) data);
break;
case _C_UINT:
return objc_write_unsigned_int (stream, *(unsigned int *) data);
break;
case _C_LNG:
return objc_write_long (stream, *(long *) data);
break;
case _C_ULNG:
return objc_write_unsigned_long (stream, *(unsigned long *) data);
break;
case _C_CHARPTR:
return objc_write_string (stream,
*(unsigned char **) data, strlen (*(char **) data));
break;
case _C_ATOM:
return objc_write_string_atomic (stream, *(unsigned char **) data,
strlen (*(char **) data));
break;
case _C_ARY_B:
{
int len = atoi (type + 1);
while (isdigit ((unsigned char) *++type))
;
return objc_write_array (stream, type, len, data);
}
break;
case _C_STRUCT_B:
{
int acc_size = 0;
int align;
while (*type != _C_STRUCT_E && *type++ != '=')
; /* skip "<name>=" */
while (*type != _C_STRUCT_E)
{
align = objc_alignof_type (type); /* padd to alignment */
acc_size = ROUND (acc_size, align);
objc_write_type (stream, type, ((char *) data) + acc_size);
acc_size += objc_sizeof_type (type); /* add component size */
type = objc_skip_typespec (type); /* skip component */
}
return 1;
}
default:
{
objc_error (nil, OBJC_ERR_BAD_TYPE,
"objc_write_type: cannot parse typespec: %s\n", type);
return 0;
}
}
}
/*
** Read one object, encoded in TYPE and pointed to by DATA to the
** typed stream STREAM. DATA specifies the address of the types to
** read. Expected type is checked against the type actually present
** on the stream.
*/
int
objc_read_type(TypedStream *stream, const char *type, void *data)
{
char c;
switch (c = *type) {
case _C_ID:
return objc_read_object (stream, (id*)data);
break;
case _C_CLASS:
return objc_read_class (stream, (Class*)data);
break;
case _C_SEL:
return objc_read_selector (stream, (SEL*)data);
break;
case _C_CHR:
return objc_read_char (stream, (char*)data);
break;
case _C_UCHR:
return objc_read_unsigned_char (stream, (unsigned char*)data);
break;
case _C_SHT:
return objc_read_short (stream, (short*)data);
break;
case _C_USHT:
return objc_read_unsigned_short (stream, (unsigned short*)data);
break;
case _C_INT:
return objc_read_int (stream, (int*)data);
break;
case _C_UINT:
return objc_read_unsigned_int (stream, (unsigned int*)data);
break;
case _C_LNG:
return objc_read_long (stream, (long*)data);
break;
case _C_ULNG:
return objc_read_unsigned_long (stream, (unsigned long*)data);
break;
case _C_CHARPTR:
case _C_ATOM:
return objc_read_string (stream, (char**)data);
break;
case _C_ARY_B:
{
int len = atoi (type + 1);
while (isdigit ((unsigned char) *++type))
;
return objc_read_array (stream, type, len, data);
}
break;
case _C_STRUCT_B:
{
int acc_size = 0;
int align;
while (*type != _C_STRUCT_E && *type++ != '=')
; /* skip "<name>=" */
while (*type != _C_STRUCT_E)
{
align = objc_alignof_type (type); /* padd to alignment */
acc_size = ROUND (acc_size, align);
objc_read_type (stream, type, ((char*)data)+acc_size);
acc_size += objc_sizeof_type (type); /* add component size */
type = objc_skip_typespec (type); /* skip component */
}
return 1;
}
default:
{
objc_error (nil, OBJC_ERR_BAD_TYPE,
"objc_read_type: cannot parse typespec: %s\n", type);
return 0;
}
}
}
/*
** Write the object specified by the template TYPE to STREAM. Last
** arguments specify addresses of values to be written. It might
** seem surprising to specify values by address, but this is extremely
** convenient for copy-paste with objc_read_types calls. A more
** down-to-the-earth cause for this passing of addresses is that values
** of arbitrary size is not well supported in ANSI C for functions with
** variable number of arguments.
*/
int
objc_write_types (TypedStream *stream, const char *type, ...)
{
va_list args;
const char *c;
int res = 0;
va_start(args, type);
for (c = type; *c; c = objc_skip_typespec (c))
{
switch (*c) {
case _C_ID:
res = objc_write_object (stream, *va_arg (args, id*));
break;
case _C_CLASS:
res = objc_write_class (stream, *va_arg (args, Class*));
break;
case _C_SEL:
res = objc_write_selector (stream, *va_arg (args, SEL*));
break;
case _C_CHR:
res = objc_write_char (stream, *va_arg (args, char*));
break;
case _C_UCHR:
res = objc_write_unsigned_char (stream,
*va_arg (args, unsigned char*));
break;
case _C_SHT:
res = objc_write_short (stream, *va_arg (args, short*));
break;
case _C_USHT:
res = objc_write_unsigned_short (stream,
*va_arg (args, unsigned short*));
break;
case _C_INT:
res = objc_write_int(stream, *va_arg (args, int*));
break;
case _C_UINT:
res = objc_write_unsigned_int(stream, *va_arg (args, unsigned int*));
break;
case _C_LNG:
res = objc_write_long(stream, *va_arg (args, long*));
break;
case _C_ULNG:
res = objc_write_unsigned_long(stream, *va_arg (args, unsigned long*));
break;
case _C_CHARPTR:
{
unsigned char **str = va_arg (args, unsigned char **);
res = objc_write_string (stream, *str, strlen ((char*)*str));
}
break;
case _C_ATOM:
{
unsigned char **str = va_arg (args, unsigned char **);
res = objc_write_string_atomic (stream, *str, strlen ((char*)*str));
}
break;
case _C_ARY_B:
{
int len = atoi (c + 1);
const char *t = c;
while (isdigit ((unsigned char) *++t))
;
res = objc_write_array (stream, t, len, va_arg (args, void *));
t = objc_skip_typespec (t);
if (*t != _C_ARY_E)
objc_error (nil, OBJC_ERR_BAD_TYPE, "expected `]', got: %s", t);
}
break;
default:
objc_error (nil, OBJC_ERR_BAD_TYPE,
"objc_write_types: cannot parse typespec: %s\n", type);
}
}
va_end(args);
return res;
}
/*
** Last arguments specify addresses of values to be read. Expected
** type is checked against the type actually present on the stream.
*/
int
objc_read_types(TypedStream *stream, const char *type, ...)
{
va_list args;
const char *c;
int res = 0;
va_start (args, type);
for (c = type; *c; c = objc_skip_typespec(c))
{
switch (*c) {
case _C_ID:
res = objc_read_object(stream, va_arg (args, id*));
break;
case _C_CLASS:
res = objc_read_class(stream, va_arg (args, Class*));
break;
case _C_SEL:
res = objc_read_selector(stream, va_arg (args, SEL*));
break;
case _C_CHR:
res = objc_read_char(stream, va_arg (args, char*));
break;
case _C_UCHR:
res = objc_read_unsigned_char(stream, va_arg (args, unsigned char*));
break;
case _C_SHT:
res = objc_read_short(stream, va_arg (args, short*));
break;
case _C_USHT:
res = objc_read_unsigned_short(stream, va_arg (args, unsigned short*));
break;
case _C_INT:
res = objc_read_int(stream, va_arg (args, int*));
break;
case _C_UINT:
res = objc_read_unsigned_int(stream, va_arg (args, unsigned int*));
break;
case _C_LNG:
res = objc_read_long(stream, va_arg (args, long*));
break;
case _C_ULNG:
res = objc_read_unsigned_long(stream, va_arg (args, unsigned long*));
break;
case _C_CHARPTR:
case _C_ATOM:
{
char **str = va_arg (args, char **);
res = objc_read_string (stream, str);
}
break;
case _C_ARY_B:
{
int len = atoi (c + 1);
const char *t = c;
while (isdigit ((unsigned char) *++t))
;
res = objc_read_array (stream, t, len, va_arg (args, void *));
t = objc_skip_typespec (t);
if (*t != _C_ARY_E)
objc_error (nil, OBJC_ERR_BAD_TYPE, "expected `]', got: %s", t);
}
break;
default:
objc_error (nil, OBJC_ERR_BAD_TYPE,
"objc_read_types: cannot parse typespec: %s\n", type);
}
}
va_end (args);
return res;
}
/*
** Write an array of COUNT elements of TYPE from the memory address DATA.
** This is equivalent of objc_write_type (stream, "[N<type>]", data)
*/
int
objc_write_array (TypedStream *stream, const char *type,
int count, const void *data)
{
int off = objc_sizeof_type(type);
const char *where = data;
while (count-- > 0)
{
objc_write_type(stream, type, where);
where += off;
}
return 1;
}
/*
** Read an array of COUNT elements of TYPE into the memory address
** DATA. The memory pointed to by data is supposed to be allocated
** by the callee. This is equivalent of
** objc_read_type (stream, "[N<type>]", data)
*/
int
objc_read_array (TypedStream *stream, const char *type,
int count, void *data)
{
int off = objc_sizeof_type(type);
char *where = (char*)data;
while (count-- > 0)
{
objc_read_type(stream, type, where);
where += off;
}
return 1;
}
static int
__objc_fread (FILE *file, char *data, int len)
{
return fread(data, len, 1, file);
}
static int
__objc_fwrite (FILE *file, char *data, int len)
{
return fwrite(data, len, 1, file);
}
static int
__objc_feof (FILE *file)
{
return feof(file);
}
static int
__objc_no_write (FILE *file __attribute__ ((__unused__)),
const char *data __attribute__ ((__unused__)),
int len __attribute__ ((__unused__)))
{
objc_error (nil, OBJC_ERR_NO_WRITE, "TypedStream not open for writing");
return 0;
}
static int
__objc_no_read (FILE *file __attribute__ ((__unused__)),
const char *data __attribute__ ((__unused__)),
int len __attribute__ ((__unused__)))
{
objc_error (nil, OBJC_ERR_NO_READ, "TypedStream not open for reading");
return 0;
}
static int
__objc_read_typed_stream_signature (TypedStream *stream)
{
char buffer[80];
int pos = 0;
do
(*stream->read) (stream->physical, buffer+pos, 1);
while (buffer[pos++] != '\0')
;
sscanf (buffer, "GNU TypedStream %d", &stream->version);
if (stream->version != OBJC_TYPED_STREAM_VERSION)
objc_error (nil, OBJC_ERR_STREAM_VERSION,
"cannot handle TypedStream version %d", stream->version);
return 1;
}
static int
__objc_write_typed_stream_signature (TypedStream *stream)
{
char buffer[80];
sprintf(buffer, "GNU TypedStream %d", OBJC_TYPED_STREAM_VERSION);
stream->version = OBJC_TYPED_STREAM_VERSION;
(*stream->write) (stream->physical, buffer, strlen (buffer) + 1);
return 1;
}
static void __objc_finish_write_root_object(struct objc_typed_stream *stream)
{
objc_hash_delete (stream->object_table);
stream->object_table = objc_hash_new (64,
(hash_func_type) objc_hash_ptr,
(compare_func_type) objc_compare_ptrs);
}
static void __objc_finish_read_root_object(struct objc_typed_stream *stream)
{
node_ptr node;
SEL awake_sel = sel_get_any_uid ("awake");
cache_ptr free_list = objc_hash_new (64,
(hash_func_type) objc_hash_ptr,
(compare_func_type) objc_compare_ptrs);
/* resolve object forward references */
for (node = objc_hash_next (stream->object_refs, NULL); node;
node = objc_hash_next (stream->object_refs, node))
{
struct objc_list *reflist = node->value;
const void *key = node->key;
id object = objc_hash_value_for_key (stream->object_table, key);
while (reflist)
{
*((id*) reflist->head) = object;
if (objc_hash_value_for_key (free_list,reflist) == NULL)
objc_hash_add (&free_list,reflist,reflist);
reflist = reflist->tail;
}
}
/* apply __objc_free to all objects stored in free_list */
for (node = objc_hash_next (free_list, NULL); node;
node = objc_hash_next (free_list, node))
objc_free ((void *) node->key);
objc_hash_delete (free_list);
/* empty object reference table */
objc_hash_delete (stream->object_refs);
stream->object_refs = objc_hash_new (8, (hash_func_type) objc_hash_ptr,
(compare_func_type) objc_compare_ptrs);
/* call -awake for all objects read */
if (awake_sel)
{
for (node = objc_hash_next (stream->object_table, NULL); node;
node = objc_hash_next (stream->object_table, node))
{
id object = node->value;
if (__objc_responds_to (object, awake_sel))
(*objc_msg_lookup (object, awake_sel)) (object, awake_sel);
}
}
/* empty object table */
objc_hash_delete (stream->object_table);
stream->object_table = objc_hash_new(64,
(hash_func_type)objc_hash_ptr,
(compare_func_type)objc_compare_ptrs);
}
/*
** Open the stream PHYSICAL in MODE
*/
TypedStream *
objc_open_typed_stream (FILE *physical, int mode)
{
TypedStream *s = (TypedStream *) objc_malloc (sizeof (TypedStream));
s->mode = mode;
s->physical = physical;
s->stream_table = objc_hash_new (64,
(hash_func_type) objc_hash_ptr,
(compare_func_type) objc_compare_ptrs);
s->object_table = objc_hash_new (64,
(hash_func_type) objc_hash_ptr,
(compare_func_type) objc_compare_ptrs);
s->eof = (objc_typed_eof_func) __objc_feof;
s->flush = (objc_typed_flush_func) fflush;
s->writing_root_p = 0;
if (mode == OBJC_READONLY)
{
s->class_table
= objc_hash_new (8, (hash_func_type) objc_hash_string,
(compare_func_type) objc_compare_strings);
s->object_refs = objc_hash_new (8, (hash_func_type) objc_hash_ptr,
(compare_func_type) objc_compare_ptrs);
s->read = (objc_typed_read_func) __objc_fread;
s->write = (objc_typed_write_func) __objc_no_write;
__objc_read_typed_stream_signature (s);
}
else if (mode == OBJC_WRITEONLY)
{
s->class_table = 0;
s->object_refs = 0;
s->read = (objc_typed_read_func) __objc_no_read;
s->write = (objc_typed_write_func) __objc_fwrite;
__objc_write_typed_stream_signature (s);
}
else
{
objc_close_typed_stream (s);
return NULL;
}
s->type = OBJC_FILE_STREAM;
return s;
}
/*
** Open the file named by FILE_NAME in MODE
*/
TypedStream*
objc_open_typed_stream_for_file (const char *file_name, int mode)
{
FILE *file = NULL;
TypedStream *s;
if (mode == OBJC_READONLY)
file = fopen (file_name, "r");
else
file = fopen (file_name, "w");
if (file)
{
s = objc_open_typed_stream (file, mode);
if (s)
s->type |= OBJC_MANAGED_STREAM;
return s;
}
else
return NULL;
}
/*
** Close STREAM freeing the structure it self. If it was opened with
** objc_open_typed_stream_for_file, the file will also be closed.
*/
void
objc_close_typed_stream (TypedStream *stream)
{
if (stream->mode == OBJC_READONLY)
{
__objc_finish_read_root_object (stream); /* Just in case... */
objc_hash_delete (stream->class_table);
objc_hash_delete (stream->object_refs);
}
objc_hash_delete (stream->stream_table);
objc_hash_delete (stream->object_table);
if (stream->type == (OBJC_MANAGED_STREAM | OBJC_FILE_STREAM))
fclose ((FILE *)stream->physical);
objc_free(stream);
}
BOOL
objc_end_of_typed_stream (TypedStream *stream)
{
return (*stream->eof) (stream->physical);
}
void
objc_flush_typed_stream (TypedStream *stream)
{
(*stream->flush) (stream->physical);
}
long
objc_get_stream_class_version (TypedStream *stream, Class class)
{
if (stream->class_table)
return PTR2LONG(objc_hash_value_for_key (stream->class_table,
class->name));
else
return class_get_version (class);
}