source: trunk/third/gcc/objc/archive.c @ 8834

Revision 8834, 37.9 KB checked in by ghudson, 28 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r8833, which included commits to RCS files with non-trunk default branches.
Line 
1/* GNU Objective C Runtime archiving
2   Copyright (C) 1993, 1995 Free Software Foundation, Inc.
3   Contributed by Kresten Krab Thorup
4
5This file is part of GNU CC.
6
7GNU CC is free software; you can redistribute it and/or modify it under the
8terms of the GNU General Public License as published by the Free Software
9Foundation; either version 2, or (at your option) any later version.
10
11GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
14details.
15
16You should have received a copy of the GNU General Public License along with
17GNU CC; see the file COPYING.  If not, write to the Free Software
18Foundation, 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA.  */
20
21/* As a special exception, if you link this library with files compiled with
22   GCC to produce an executable, this does not cause the resulting executable
23   to be covered by the GNU General Public License. This exception does not
24   however invalidate any other reasons why the executable file might be
25   covered by the GNU General Public License.  */
26
27#include "runtime.h"
28#include "typedstream.h"
29#include "encoding.h"
30
31extern int fflush(FILE*);
32
33#define ROUND(V, A) \
34  ({ typeof(V) __v=(V); typeof(A) __a=(A);  \
35     __a*((__v+__a-1)/__a); })
36
37#define PTR2LONG(P) (((char*)(P))-(char*)0)
38#define LONG2PTR(L) (((char*)0)+(L))
39
40#define __objc_fatal(format, args...) \
41 { fprintf(stderr, "archiving: "); \
42   fprintf(stderr, format, ## args); \
43   fprintf(stderr, "\n"); abort(); }
44
45/* Declare some functions... */
46
47static int
48objc_read_class (struct objc_typed_stream* stream, Class* class);
49
50int objc_sizeof_type(const char* type);
51
52static int
53objc_write_use_common (struct objc_typed_stream* stream, unsigned long key);
54
55static int
56objc_write_register_common (struct objc_typed_stream* stream,
57                            unsigned long key);
58
59static int
60objc_write_class (struct objc_typed_stream* stream,
61                         struct objc_class* class);
62
63const char* objc_skip_type (const char* type);
64
65static void __objc_finish_write_root_object(struct objc_typed_stream*);
66static void __objc_finish_read_root_object(struct objc_typed_stream*);
67
68static __inline__ int
69__objc_code_unsigned_char (unsigned char* buf, unsigned char val)
70{
71  if ((val&_B_VALUE) == val)
72    {
73      buf[0] = val|_B_SINT;
74      return 1;
75    }
76  else
77    {
78      buf[0] = _B_NINT|0x01;
79      buf[1] = val;
80      return 2;
81    }
82}
83
84int
85objc_write_unsigned_char (struct objc_typed_stream* stream,
86                          unsigned char value)
87{
88  unsigned char buf[sizeof (unsigned char)+1];
89  int len = __objc_code_unsigned_char (buf, value);
90  return (*stream->write)(stream->physical, buf, len);
91}
92
93static __inline__ int
94__objc_code_char (unsigned char* buf, char val)
95{
96  if (val >= 0)
97    return __objc_code_unsigned_char (buf, val);
98  else
99    {
100      buf[0] = _B_NINT|_B_SIGN|0x01;
101      buf[1] = -val;
102      return 2;
103    }
104}
105
106int
107objc_write_char (struct objc_typed_stream* stream, char value)
108{
109  unsigned char buf[sizeof (char)+1];
110  int len = __objc_code_char (buf, value);
111  return (*stream->write)(stream->physical, buf, len);
112}
113
114static __inline__ int
115__objc_code_unsigned_short (unsigned char* buf, unsigned short val)
116{
117  if ((val&_B_VALUE) == val)
118    {
119      buf[0] = val|_B_SINT;
120      return 1;
121    }
122  else
123    {
124      int c, b;
125
126      buf[0] = _B_NINT;
127
128      for (c= sizeof(short); c != 0; c -= 1)
129        if (((val>>(8*(c-1)))%0x100) != 0)
130          break;
131
132      buf[0] |= c;
133
134      for (b = 1; c != 0; c--, b++)
135        {
136          buf[b] = (val >> (8*(c-1)))%0x100;
137        }
138
139      return b;
140    }
141}
142
143int
144objc_write_unsigned_short (struct objc_typed_stream* stream, unsigned short value)
145{
146  unsigned char buf[sizeof (unsigned short)+1];
147  int len = __objc_code_unsigned_short (buf, value);
148  return (*stream->write)(stream->physical, buf, len);
149}
150     
151static __inline__ int
152__objc_code_short (unsigned char* buf, short val)
153{
154  int sign = (val < 0);
155  int size = __objc_code_unsigned_short (buf, sign ? -val : val);
156  if (sign)
157    buf[0] |= _B_SIGN;
158  return size;
159}
160
161int
162objc_write_short (struct objc_typed_stream* stream, short value)
163{
164  unsigned char buf[sizeof (short)+1];
165  int len = __objc_code_short (buf, value);
166  return (*stream->write)(stream->physical, buf, len);
167}
168     
169
170static __inline__ int
171__objc_code_unsigned_int (unsigned char* buf, unsigned int val)
172{
173  if ((val&_B_VALUE) == val)
174    {
175      buf[0] = val|_B_SINT;
176      return 1;
177    }
178  else
179    {
180      int c, b;
181
182      buf[0] = _B_NINT;
183
184      for (c= sizeof(int); c != 0; c -= 1)
185        if (((val>>(8*(c-1)))%0x100) != 0)
186          break;
187
188      buf[0] |= c;
189
190      for (b = 1; c != 0; c--, b++)
191        {
192          buf[b] = (val >> (8*(c-1)))%0x100;
193        }
194
195      return b;
196    }
197}
198
199int
200objc_write_unsigned_int (struct objc_typed_stream* stream, unsigned int value)
201{
202  unsigned char buf[sizeof(unsigned int)+1];
203  int len = __objc_code_unsigned_int (buf, value);
204  return (*stream->write)(stream->physical, buf, len);
205}
206
207static __inline__ int
208__objc_code_int (unsigned char* buf, int val)
209{
210  int sign = (val < 0);
211  int size = __objc_code_unsigned_int (buf, sign ? -val : val);
212  if (sign)
213    buf[0] |= _B_SIGN;
214  return size;
215}
216
217int
218objc_write_int (struct objc_typed_stream* stream, int value)
219{
220  unsigned char buf[sizeof(int)+1];
221  int len = __objc_code_int (buf, value);
222  return (*stream->write)(stream->physical, buf, len);
223}
224
225static __inline__ int
226__objc_code_unsigned_long (unsigned char* buf, unsigned long val)
227{
228  if ((val&_B_VALUE) == val)
229    {
230      buf[0] = val|_B_SINT;
231      return 1;
232    }
233  else
234    {
235      int c, b;
236
237      buf[0] = _B_NINT;
238
239      for (c= sizeof(long); c != 0; c -= 1)
240        if (((val>>(8*(c-1)))%0x100) != 0)
241          break;
242
243      buf[0] |= c;
244
245      for (b = 1; c != 0; c--, b++)
246        {
247          buf[b] = (val >> (8*(c-1)))%0x100;
248        }
249
250      return b;
251    }
252}
253
254int
255objc_write_unsigned_long (struct objc_typed_stream* stream, unsigned long value)
256{
257  unsigned char buf[sizeof(unsigned long)+1];
258  int len = __objc_code_unsigned_long (buf, value);
259  return (*stream->write)(stream->physical, buf, len);
260}
261
262static __inline__ int
263__objc_code_long (unsigned char* buf, long val)
264{
265  int sign = (val < 0);
266  int size = __objc_code_unsigned_long (buf, sign ? -val : val);
267  if (sign)
268    buf[0] |= _B_SIGN;
269  return size;
270}
271
272int
273objc_write_long (struct objc_typed_stream* stream, long value)
274{
275  unsigned char buf[sizeof(long)+1];
276  int len = __objc_code_long (buf, value);
277  return (*stream->write)(stream->physical, buf, len);
278}
279
280
281int
282objc_write_string (struct objc_typed_stream* stream,
283                   const unsigned char* string, unsigned int nbytes)
284{
285  unsigned char buf[sizeof(unsigned int)+1];
286  int len = __objc_code_unsigned_int (buf, nbytes);
287 
288  if ((buf[0]&_B_CODE) == _B_SINT)
289    buf[0] = (buf[0]&_B_VALUE)|_B_SSTR;
290
291  else /* _B_NINT */
292    buf[0] = (buf[0]&_B_VALUE)|_B_NSTR;
293
294  if ((*stream->write)(stream->physical, buf, len) != 0)
295    return (*stream->write)(stream->physical, string, nbytes);
296  else
297    return 0;
298}
299
300int
301objc_write_string_atomic (struct objc_typed_stream* stream,
302                          unsigned char* string, unsigned int nbytes)
303{
304  unsigned long key;
305  if ((key = PTR2LONG(hash_value_for_key (stream->stream_table, string))))
306    return objc_write_use_common (stream, key);
307  else
308    {
309      int length;
310      hash_add (&stream->stream_table, LONG2PTR(key=PTR2LONG(string)), string);
311      if ((length = objc_write_register_common (stream, key)))
312        return objc_write_string (stream, string, nbytes);
313      return length;
314    }
315}
316
317static int
318objc_write_register_common (struct objc_typed_stream* stream, unsigned long key)
319{
320  unsigned char buf[sizeof (unsigned long)+2];
321  int len = __objc_code_unsigned_long (buf+1, key);
322  if (len == 1)
323    {
324      buf[0] = _B_RCOMM|0x01;
325      buf[1] &= _B_VALUE;
326      return (*stream->write)(stream->physical, buf, len+1);
327    }
328  else
329    {
330      buf[1] = (buf[1]&_B_VALUE)|_B_RCOMM;
331      return (*stream->write)(stream->physical, buf+1, len);
332    }
333}
334
335static int
336objc_write_use_common (struct objc_typed_stream* stream, unsigned long key)
337{
338  unsigned char buf[sizeof (unsigned long)+2];
339  int len = __objc_code_unsigned_long (buf+1, key);
340  if (len == 1)
341    {
342      buf[0] = _B_UCOMM|0x01;
343      buf[1] &= _B_VALUE;
344      return (*stream->write)(stream->physical, buf, 2);
345    }
346  else
347    {
348      buf[1] = (buf[1]&_B_VALUE)|_B_UCOMM;
349      return (*stream->write)(stream->physical, buf+1, len);
350    }
351}
352
353static __inline__ int
354__objc_write_extension (struct objc_typed_stream* stream, unsigned char code)
355{
356  if (code <= _B_VALUE)
357    {
358      unsigned char buf = code|_B_EXT;
359      return (*stream->write)(stream->physical, &buf, 1);
360    }
361  else
362    abort();
363}
364
365__inline__ int
366__objc_write_object (struct objc_typed_stream* stream, id object)
367{
368  unsigned char buf = '\0';
369  SEL write_sel = sel_get_any_uid ("write:");
370  if (object)
371    {
372      __objc_write_extension (stream, _BX_OBJECT);
373      objc_write_class (stream, object->class_pointer);
374      (*objc_msg_lookup(object, write_sel))(object, write_sel, stream);
375      return (*stream->write)(stream->physical, &buf, 1);
376    }
377  else
378    return objc_write_use_common(stream, 0);
379}
380
381int
382objc_write_object_reference (struct objc_typed_stream* stream, id object)
383{
384  unsigned long key;
385  if ((key = PTR2LONG(hash_value_for_key (stream->object_table, object))))
386    return objc_write_use_common (stream, key);
387
388  __objc_write_extension (stream, _BX_OBJREF);
389  return objc_write_unsigned_long (stream, PTR2LONG (object));
390}
391
392int
393objc_write_root_object (struct objc_typed_stream* stream, id object)
394{
395  int len;
396  if (stream->writing_root_p)
397    __objc_fatal ("objc_write_root_object called recursively")
398  else
399    {
400      stream->writing_root_p = 1;
401      __objc_write_extension (stream, _BX_OBJROOT);
402      if((len = objc_write_object (stream, object)))
403        __objc_finish_write_root_object(stream);
404      stream->writing_root_p = 0;
405    }
406  return len;
407}
408
409int
410objc_write_object (struct objc_typed_stream* stream, id object)
411{
412  unsigned long key;
413  if ((key = PTR2LONG(hash_value_for_key (stream->object_table, object))))
414    return objc_write_use_common (stream, key);
415
416  else if (object == nil)
417    return objc_write_use_common(stream, 0);
418
419  else
420    {
421      int length;
422      hash_add (&stream->object_table, LONG2PTR(key=PTR2LONG(object)), object);
423      if ((length = objc_write_register_common (stream, key)))
424        return __objc_write_object (stream, object);
425      return length;
426    }
427}
428
429#ifdef __alpha__
430extern int atoi (const char*);
431extern size_t strlen(const char*);
432extern size_t strcpy(char*, const char*);
433#endif
434
435__inline__ int
436__objc_write_class (struct objc_typed_stream* stream, struct objc_class* class)
437{
438  __objc_write_extension (stream, _BX_CLASS);
439  objc_write_string_atomic(stream, (char*)class->name,
440                           strlen((char*)class->name));
441  return objc_write_unsigned_long (stream, class->version);
442}
443
444
445static int
446objc_write_class (struct objc_typed_stream* stream,
447                         struct objc_class* class)
448{
449  unsigned long key;
450  if ((key = PTR2LONG(hash_value_for_key (stream->stream_table, class))))
451    return objc_write_use_common (stream, key);
452  else
453    {
454      int length;
455      hash_add (&stream->stream_table, LONG2PTR(key=PTR2LONG(class)), class);
456      if ((length = objc_write_register_common (stream, key)))
457        return __objc_write_class (stream, class);
458      return length;
459    }
460}
461
462
463__inline__ int
464__objc_write_selector (struct objc_typed_stream* stream, SEL selector)
465{
466  const char* sel_name;
467  __objc_write_extension (stream, _BX_SEL);
468  /* to handle NULL selectors */
469  if ((SEL)0 == selector)
470    return objc_write_string (stream, "", 0);
471  sel_name = sel_get_name (selector);
472  return objc_write_string (stream, sel_name, strlen ((char*)sel_name));
473}
474
475int
476objc_write_selector (struct objc_typed_stream* stream, SEL selector)
477{
478  const char* sel_name;
479  unsigned long key;
480
481  /* to handle NULL selectors */
482  if ((SEL)0 == selector)
483    return __objc_write_selector (stream, selector);
484
485  sel_name = sel_get_name (selector);
486  if ((key = PTR2LONG(hash_value_for_key (stream->stream_table, sel_name))))
487    return objc_write_use_common (stream, key);
488  else
489    {
490      int length;
491      hash_add (&stream->stream_table, LONG2PTR(key=PTR2LONG(sel_name)), (char*)sel_name);
492      if ((length = objc_write_register_common (stream, key)))
493        return __objc_write_selector (stream, selector);
494      return length;
495    }
496}
497
498
499
500/*
501** Read operations
502*/
503
504__inline__ int
505objc_read_char (struct objc_typed_stream* stream, char* val)
506{
507  unsigned char buf;
508  int len;
509  len = (*stream->read)(stream->physical, &buf, 1);
510  if (len != 0)
511    {
512      if ((buf & _B_CODE) == _B_SINT)
513        (*val) = (buf & _B_VALUE);
514
515      else if ((buf & _B_NUMBER) == 1)
516        {
517          len = (*stream->read)(stream->physical, val, 1);
518          if (buf&_B_SIGN)
519            (*val) = -1*(*val);
520        }
521
522      else
523        __objc_fatal("expected 8bit signed int, got %dbit int",
524                     (int)(buf&_B_NUMBER)*8);
525    }
526  return len;
527}
528
529
530__inline__ int
531objc_read_unsigned_char (struct objc_typed_stream* stream, unsigned char* val)
532{
533  unsigned char buf;
534  int len;
535  if ((len = (*stream->read)(stream->physical, &buf, 1)))
536    {
537      if ((buf & _B_CODE) == _B_SINT)
538        (*val) = (buf & _B_VALUE);
539
540      else if ((buf & _B_NUMBER) == 1)
541        len = (*stream->read)(stream->physical, val, 1);
542
543      else
544        __objc_fatal("expected 8bit unsigned int, got %dbit int",
545                     (int)(buf&_B_NUMBER)*8);
546    }
547  return len;
548}
549
550__inline__ int
551objc_read_short (struct objc_typed_stream* stream, short* value)
552{
553  unsigned char buf[sizeof(short)+1];
554  int len;
555  if ((len = (*stream->read)(stream->physical, buf, 1)))
556    {
557      if ((buf[0] & _B_CODE) == _B_SINT)
558        (*value) = (buf[0] & _B_VALUE);
559
560      else
561        {
562          int pos = 1;
563          int nbytes = buf[0] & _B_NUMBER;
564          if (nbytes > sizeof (short))
565            __objc_fatal("expected short, got bigger (%dbits)", nbytes*8);
566          len = (*stream->read)(stream->physical, buf+1, nbytes);
567          (*value) = 0;
568          while (pos <= nbytes)
569            (*value) = ((*value)*0x100) + buf[pos++];
570          if (buf[0] & _B_SIGN)
571            (*value) = -(*value);
572        }
573    }
574  return len;
575}
576
577__inline__ int
578objc_read_unsigned_short (struct objc_typed_stream* stream,
579                          unsigned short* value)
580{
581  unsigned char buf[sizeof(unsigned short)+1];
582  int len;
583  if ((len = (*stream->read)(stream->physical, buf, 1)))
584    {
585      if ((buf[0] & _B_CODE) == _B_SINT)
586        (*value) = (buf[0] & _B_VALUE);
587
588      else
589        {
590          int pos = 1;
591          int nbytes = buf[0] & _B_NUMBER;
592          if (nbytes > sizeof (short))
593            __objc_fatal("expected short, got int or bigger");
594          len = (*stream->read)(stream->physical, buf+1, nbytes);
595          (*value) = 0;
596          while (pos <= nbytes)
597            (*value) = ((*value)*0x100) + buf[pos++];
598        }
599    }
600  return len;
601}
602
603
604__inline__ int
605objc_read_int (struct objc_typed_stream* stream, int* value)
606{
607  unsigned char buf[sizeof(int)+1];
608  int len;
609  if ((len = (*stream->read)(stream->physical, buf, 1)))
610    {
611      if ((buf[0] & _B_CODE) == _B_SINT)
612        (*value) = (buf[0] & _B_VALUE);
613
614      else
615        {
616          int pos = 1;
617          int nbytes = buf[0] & _B_NUMBER;
618          if (nbytes > sizeof (int))
619            __objc_fatal("expected int, got bigger");
620          len = (*stream->read)(stream->physical, buf+1, nbytes);
621          (*value) = 0;
622          while (pos <= nbytes)
623            (*value) = ((*value)*0x100) + buf[pos++];
624          if (buf[0] & _B_SIGN)
625            (*value) = -(*value);
626        }
627    }
628  return len;
629}
630
631__inline__ int
632objc_read_long (struct objc_typed_stream* stream, long* value)
633{
634  unsigned char buf[sizeof(long)+1];
635  int len;
636  if ((len = (*stream->read)(stream->physical, buf, 1)))
637    {
638      if ((buf[0] & _B_CODE) == _B_SINT)
639        (*value) = (buf[0] & _B_VALUE);
640
641      else
642        {
643          int pos = 1;
644          int nbytes = buf[0] & _B_NUMBER;
645          if (nbytes > sizeof (long))
646            __objc_fatal("expected long, got bigger");
647          len = (*stream->read)(stream->physical, buf+1, nbytes);
648          (*value) = 0;
649          while (pos <= nbytes)
650            (*value) = ((*value)*0x100) + buf[pos++];
651          if (buf[0] & _B_SIGN)
652            (*value) = -(*value);
653        }
654    }
655  return len;
656}
657
658__inline__ int
659__objc_read_nbyte_uint (struct objc_typed_stream* stream,
660                       unsigned int nbytes, unsigned int* val)
661{
662  int len, pos = 0;
663  unsigned char buf[sizeof(unsigned int)+1];
664
665  if (nbytes > sizeof (int))
666    __objc_fatal("expected int, got bigger");
667
668  len = (*stream->read)(stream->physical, buf, nbytes);
669  (*val) = 0;
670  while (pos < nbytes)
671    (*val) = ((*val)*0x100) + buf[pos++];
672  return len;
673}
674 
675
676__inline__ int
677objc_read_unsigned_int (struct objc_typed_stream* stream,
678                        unsigned int* value)
679{
680  unsigned char buf[sizeof(unsigned int)+1];
681  int len;
682  if ((len = (*stream->read)(stream->physical, buf, 1)))
683    {
684      if ((buf[0] & _B_CODE) == _B_SINT)
685        (*value) = (buf[0] & _B_VALUE);
686
687      else
688        len = __objc_read_nbyte_uint (stream, (buf[0] & _B_VALUE), value);
689
690    }
691  return len;
692}
693
694int
695__objc_read_nbyte_ulong (struct objc_typed_stream* stream,
696                       unsigned int nbytes, unsigned long* val)
697{
698  int len, pos = 0;
699  unsigned char buf[sizeof(unsigned long)+1];
700
701  if (nbytes > sizeof (long))
702    __objc_fatal("expected long, got bigger");
703
704  len = (*stream->read)(stream->physical, buf, nbytes);
705  (*val) = 0;
706  while (pos < nbytes)
707    (*val) = ((*val)*0x100) + buf[pos++];
708  return len;
709}
710 
711
712__inline__ int
713objc_read_unsigned_long (struct objc_typed_stream* stream,
714                        unsigned long* value)
715{
716  unsigned char buf[sizeof(unsigned long)+1];
717  int len;
718  if ((len = (*stream->read)(stream->physical, buf, 1)))
719    {
720      if ((buf[0] & _B_CODE) == _B_SINT)
721        (*value) = (buf[0] & _B_VALUE);
722
723      else
724        len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), value);
725
726    }
727  return len;
728}
729
730__inline__ int
731objc_read_string (struct objc_typed_stream* stream,
732                  char** string)
733{
734  unsigned char buf[sizeof(unsigned int)+1];
735  int len;
736  if ((len = (*stream->read)(stream->physical, buf, 1)))
737    {
738      unsigned long key = 0;
739
740      if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */
741        {
742          len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key);
743          len = (*stream->read)(stream->physical, buf, 1);
744        }
745
746      switch (buf[0]&_B_CODE) {
747      case _B_SSTR:
748        {
749          int length = buf[0]&_B_VALUE;
750          (*string) = (char*)__objc_xmalloc(length+1);
751          if (key)
752            hash_add (&stream->stream_table, LONG2PTR(key), *string);
753          len = (*stream->read)(stream->physical, *string, length);
754          (*string)[length] = '\0';
755        }
756        break;
757
758      case _B_UCOMM:
759        {
760          char *tmp;
761          len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key);
762          tmp = hash_value_for_key (stream->stream_table, LONG2PTR (key));
763          *string = __objc_xmalloc (strlen(tmp) + 1);
764          strcpy (*string, tmp);
765        }
766        break;
767
768      case _B_NSTR:
769        {
770          unsigned int nbytes = buf[0]&_B_VALUE;
771          len = __objc_read_nbyte_uint(stream, nbytes, &nbytes);
772          if (len) {
773            (*string) = (char*)__objc_xmalloc(nbytes+1);
774            if (key)
775              hash_add (&stream->stream_table, LONG2PTR(key), *string);
776            len = (*stream->read)(stream->physical, *string, nbytes);
777            (*string)[nbytes] = '\0';
778          }
779        }
780        break;
781       
782      default:
783        __objc_fatal("expected string, got opcode %c\n", (buf[0]&_B_CODE));
784      }
785    }
786
787  return len;
788}
789
790
791int
792objc_read_object (struct objc_typed_stream* stream, id* object)
793{
794  unsigned char buf[sizeof (unsigned int)];
795  int len;
796  if ((len = (*stream->read)(stream->physical, buf, 1)))
797    {
798      SEL read_sel = sel_get_any_uid ("read:");
799      unsigned long key = 0;
800
801      if ((buf[0]&_B_CODE) == _B_RCOMM) /* register common */
802        {
803          len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key);
804          len = (*stream->read)(stream->physical, buf, 1);
805        }
806
807      if (buf[0] == (_B_EXT | _BX_OBJECT))
808        {
809          Class class;
810
811          /* get class */
812          len = objc_read_class (stream, &class);
813
814          /* create instance */
815          (*object) = class_create_instance(class);
816
817          /* register? */
818          if (key)
819            hash_add (&stream->object_table, LONG2PTR(key), *object);
820
821          /* send -read: */
822          if (__objc_responds_to (*object, read_sel))
823            (*get_imp(class, read_sel))(*object, read_sel, stream);
824
825          /* check null-byte */
826          len = (*stream->read)(stream->physical, buf, 1);
827          if (buf[0] != '\0')
828            __objc_fatal("expected null-byte, got opcode %c", buf[0]);
829        }
830
831      else if ((buf[0]&_B_CODE) == _B_UCOMM)
832        {
833          if (key)
834            __objc_fatal("cannot register use upcode...");
835          len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key);
836          (*object) = hash_value_for_key (stream->object_table, LONG2PTR(key));
837        }
838
839      else if (buf[0] == (_B_EXT | _BX_OBJREF)) /* a forward reference */
840        {
841          struct objc_list* other;
842          len = objc_read_unsigned_long (stream, &key);
843          other = (struct objc_list*)hash_value_for_key (stream->object_refs, LONG2PTR(key));
844          hash_add (&stream->object_refs, LONG2PTR(key), (void*)list_cons(object, other));
845        }
846
847      else if (buf[0] == (_B_EXT | _BX_OBJROOT)) /* a root object */
848        {
849          if (key)
850            __objc_fatal("cannot register root object...");
851          len = objc_read_object (stream, object);
852          __objc_finish_read_root_object (stream);
853        }
854
855      else
856        __objc_fatal("expected object, got opcode %c", buf[0]);
857    }
858  return len;
859}
860
861static int
862objc_read_class (struct objc_typed_stream* stream, Class* class)
863{
864  unsigned char buf[sizeof (unsigned int)];
865  int len;
866  if ((len = (*stream->read)(stream->physical, buf, 1)))
867    {
868      unsigned long key = 0;
869
870      if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */
871        {
872          len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key);
873          len = (*stream->read)(stream->physical, buf, 1);
874        }
875
876      if (buf[0] == (_B_EXT | _BX_CLASS))
877        {
878          char* class_name;
879          unsigned long version;
880
881          /* get class */
882          len = objc_read_string (stream, &class_name);
883          (*class) = objc_get_class(class_name);
884          free (class_name);
885
886          /* register */
887          if (key)
888            hash_add (&stream->stream_table, LONG2PTR(key), *class);
889
890          objc_read_unsigned_long(stream, &version);
891          hash_add (&stream->class_table, (*class)->name, (void*)version);
892        }
893
894      else if ((buf[0]&_B_CODE) == _B_UCOMM)
895        {
896          if (key)
897            __objc_fatal("cannot register use upcode...");
898          len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key);
899          (*class) = hash_value_for_key (stream->stream_table, LONG2PTR(key));
900          if (!*class)
901            __objc_fatal("cannot find class for key %lu", key);
902        }
903
904      else
905        __objc_fatal("expected class, got opcode %c", buf[0]);
906    }
907  return len;
908}
909
910int
911objc_read_selector (struct objc_typed_stream* stream, SEL* selector)
912{
913  unsigned char buf[sizeof (unsigned int)];
914  int len;
915  if ((len = (*stream->read)(stream->physical, buf, 1)))
916    {
917      unsigned long key = 0;
918
919      if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */
920        {
921          len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key);
922          len = (*stream->read)(stream->physical, buf, 1);
923        }
924
925      if (buf[0] == (_B_EXT|_BX_SEL)) /* selector! */
926        {
927          char* selector_name;
928
929          /* get selector */
930          len = objc_read_string (stream, &selector_name);
931          /* To handle NULL selectors */
932          if (0 == strlen(selector_name))
933            {
934              (*selector) = (SEL)0;
935              return 0;
936            }
937          else
938            (*selector) = sel_get_any_uid(selector_name);
939          free (selector_name);
940
941          /* register */
942          if (key)
943            hash_add (&stream->stream_table, LONG2PTR(key), (void*)*selector);
944        }
945
946      else if ((buf[0]&_B_CODE) == _B_UCOMM)
947        {
948          if (key)
949            __objc_fatal("cannot register use upcode...");
950          len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key);
951          (*selector) = hash_value_for_key (stream->stream_table, LONG2PTR(key));
952        }
953
954      else
955        __objc_fatal("expected selector, got opcode %c", buf[0]);
956    }
957  return len;
958}
959
960/*
961** USER LEVEL FUNCTIONS
962*/
963
964/*
965** Write one object, encoded in TYPE and pointed to by DATA to the
966** typed stream STREAM. 
967*/
968
969int
970objc_write_type(TypedStream* stream, const char* type, const void* data)
971{
972  switch(*type) {
973  case _C_ID:
974    return objc_write_object (stream, *(id*)data);
975    break;
976
977  case _C_CLASS:
978    return objc_write_class (stream, *(Class*)data);
979    break;
980
981  case _C_SEL:
982    return objc_write_selector (stream, *(SEL*)data);
983    break;
984
985  case _C_CHR:
986    return objc_write_char(stream, *(char*)data);
987    break;
988   
989  case _C_UCHR:
990    return objc_write_unsigned_char(stream, *(unsigned char*)data);
991    break;
992
993  case _C_SHT:
994    return objc_write_short(stream, *(short*)data);
995    break;
996
997  case _C_USHT:
998    return objc_write_unsigned_short(stream, *(unsigned short*)data);
999    break;
1000
1001  case _C_INT:
1002    return objc_write_int(stream, *(int*)data);
1003    break;
1004
1005  case _C_UINT:
1006    return objc_write_unsigned_int(stream, *(unsigned int*)data);
1007    break;
1008
1009  case _C_LNG:
1010    return objc_write_long(stream, *(long*)data);
1011    break;
1012
1013  case _C_ULNG:
1014    return objc_write_unsigned_long(stream, *(unsigned long*)data);
1015    break;
1016
1017  case _C_CHARPTR:
1018    return objc_write_string (stream, *(char**)data, strlen(*(char**)data));
1019    break;
1020
1021  case _C_ATOM:
1022    return objc_write_string_atomic (stream, *(char**)data, strlen(*(char**)data));
1023    break;
1024
1025  case _C_ARY_B:
1026    {
1027      int len = atoi(type+1);
1028      while (isdigit(*++type));
1029      return objc_write_array (stream, type, len, data);
1030    }
1031    break;
1032
1033  case _C_STRUCT_B:
1034    {
1035      int acc_size = 0;
1036      int align;
1037      while (*type != _C_STRUCT_E && *type++ != '='); /* skip "<name>=" */
1038      while (*type != _C_STRUCT_E);
1039        {
1040          align = objc_alignof_type (type);       /* padd to alignment */
1041          acc_size += ROUND (acc_size, align);
1042          objc_write_type (stream, type, ((char*)data)+acc_size);
1043          acc_size += objc_sizeof_type (type);   /* add component size */
1044          type = objc_skip_typespec (type);      /* skip component */
1045        }
1046      return 1;
1047    }
1048
1049  default:
1050    fprintf(stderr, "objc_write_type: cannot parse typespec: %s\n", type);
1051    abort();
1052  }
1053}
1054
1055/*
1056** Read one object, encoded in TYPE and pointed to by DATA to the
1057** typed stream STREAM.  DATA specifies the address of the types to
1058** read.  Expected type is checked against the type actually present
1059** on the stream.
1060*/
1061
1062int
1063objc_read_type(TypedStream* stream, const char* type, void* data)
1064{
1065  char c;
1066  switch(c = *type) {
1067  case _C_ID:
1068    return objc_read_object (stream, (id*)data);
1069    break;
1070
1071  case _C_CLASS:
1072    return objc_read_class (stream, (Class*)data);
1073    break;
1074
1075  case _C_SEL:
1076    return objc_read_selector (stream, (SEL*)data);
1077    break;
1078
1079  case _C_CHR:
1080    return objc_read_char (stream, (char*)data);
1081    break;
1082   
1083  case _C_UCHR:
1084    return objc_read_unsigned_char (stream, (unsigned char*)data);
1085    break;
1086
1087  case _C_SHT:
1088    return objc_read_short (stream, (short*)data);
1089    break;
1090
1091  case _C_USHT:
1092    return objc_read_unsigned_short (stream, (unsigned short*)data);
1093    break;
1094
1095  case _C_INT:
1096    return objc_read_int (stream, (int*)data);
1097    break;
1098
1099  case _C_UINT:
1100    return objc_read_unsigned_int (stream, (unsigned int*)data);
1101    break;
1102
1103  case _C_LNG:
1104    return objc_read_long (stream, (long*)data);
1105    break;
1106
1107  case _C_ULNG:
1108    return objc_read_unsigned_long (stream, (unsigned long*)data);
1109    break;
1110
1111  case _C_CHARPTR:
1112  case _C_ATOM:
1113    return objc_read_string (stream, (char**)data);
1114    break;
1115
1116  case _C_ARY_B:
1117    {
1118      int len = atoi(type+1);
1119      while (isdigit(*++type));
1120      return objc_read_array (stream, type, len, data);
1121    }
1122    break;
1123
1124  case _C_STRUCT_B:
1125    {
1126      int acc_size = 0;
1127      int align;
1128      while (*type != _C_STRUCT_E && *type++ != '='); /* skip "<name>=" */
1129      while (*type != _C_STRUCT_E);
1130        {
1131          align = objc_alignof_type (type);       /* padd to alignment */
1132          acc_size += ROUND (acc_size, align);
1133          objc_read_type (stream, type, ((char*)data)+acc_size);
1134          acc_size += objc_sizeof_type (type);   /* add component size */
1135          type = objc_skip_typespec (type);      /* skip component */
1136        }
1137      return 1;
1138    }
1139
1140  default:
1141    fprintf(stderr, "objc_read_type: cannot parse typespec: %s\n", type);
1142    abort();
1143  }
1144}
1145
1146/*
1147** Write the object specified by the template TYPE to STREAM.  Last
1148** arguments specify addresses of values to be written.  It might
1149** seem surprising to specify values by address, but this is extremely
1150** convenient for copy-paste with objc_read_types calls.  A more
1151** down-to-the-earth cause for this passing of addresses is that values
1152** of arbitrary size is not well supported in ANSI C for functions with
1153** variable number of arguments.
1154*/
1155
1156int
1157objc_write_types (TypedStream* stream, const char* type, ...)
1158{
1159  va_list args;
1160  const char *c;
1161  int res = 0;
1162
1163  va_start(args, type);
1164
1165  for (c = type; *c; c = objc_skip_typespec (c))
1166    {
1167      switch(*c) {
1168      case _C_ID:
1169        res = objc_write_object (stream, *va_arg (args, id*));
1170        break;
1171
1172      case _C_CLASS:
1173        res = objc_write_class (stream, *va_arg(args, Class*));
1174        break;
1175
1176      case _C_SEL:
1177        res = objc_write_selector (stream, *va_arg(args, SEL*));
1178        break;
1179       
1180      case _C_CHR:
1181        res = objc_write_char (stream, *va_arg (args, char*));
1182        break;
1183       
1184      case _C_UCHR:
1185        res = objc_write_unsigned_char (stream,
1186                                        *va_arg (args, unsigned char*));
1187        break;
1188       
1189      case _C_SHT:
1190        res = objc_write_short (stream, *va_arg(args, short*));
1191        break;
1192
1193      case _C_USHT:
1194        res = objc_write_unsigned_short (stream,
1195                                         *va_arg(args, unsigned short*));
1196        break;
1197
1198      case _C_INT:
1199        res = objc_write_int(stream, *va_arg(args, int*));
1200        break;
1201       
1202      case _C_UINT:
1203        res = objc_write_unsigned_int(stream, *va_arg(args, unsigned int*));
1204        break;
1205
1206      case _C_LNG:
1207        res = objc_write_long(stream, *va_arg(args, long*));
1208        break;
1209       
1210      case _C_ULNG:
1211        res = objc_write_unsigned_long(stream, *va_arg(args, unsigned long*));
1212        break;
1213
1214      case _C_CHARPTR:
1215        {
1216          char** str = va_arg(args, char**);
1217          res = objc_write_string (stream, *str, strlen(*str));
1218        }
1219        break;
1220
1221      case _C_ATOM:
1222        {
1223          char** str = va_arg(args, char**);
1224          res = objc_write_string_atomic (stream, *str, strlen(*str));
1225        }
1226        break;
1227
1228      case _C_ARY_B:
1229        {
1230          int len = atoi(c+1);
1231          const char* t = c;
1232          while (isdigit(*++t));
1233          res = objc_write_array (stream, t, len, va_arg(args, void*));
1234          t = objc_skip_typespec (t);
1235          if (*t != _C_ARY_E)
1236            __objc_fatal("expected `]', got: %s", t);
1237        }
1238        break;
1239       
1240      default:
1241        fprintf(stderr, "objc_write_types: cannot parse typespec: %s\n", type);
1242        abort();
1243      }
1244    }
1245  va_end(args);
1246  return res;
1247}
1248
1249
1250/*
1251** Last arguments specify addresses of values to be read.  Expected
1252** type is checked against the type actually present on the stream.
1253*/
1254
1255int
1256objc_read_types(TypedStream* stream, const char* type, ...)
1257{
1258  va_list args;
1259  const char *c;
1260  int res = 0;
1261
1262  va_start(args, type);
1263
1264  for (c = type; *c; c = objc_skip_typespec(c))
1265    {
1266      switch(*c) {
1267      case _C_ID:
1268        res = objc_read_object(stream, va_arg(args, id*));
1269        break;
1270
1271      case _C_CLASS:
1272        res = objc_read_class(stream, va_arg(args, Class*));
1273        break;
1274
1275      case _C_SEL:
1276        res = objc_read_selector(stream, va_arg(args, SEL*));
1277        break;
1278       
1279      case _C_CHR:
1280        res = objc_read_char(stream, va_arg(args, char*));
1281        break;
1282       
1283      case _C_UCHR:
1284        res = objc_read_unsigned_char(stream, va_arg(args, unsigned char*));
1285        break;
1286       
1287      case _C_SHT:
1288        res = objc_read_short(stream, va_arg(args, short*));
1289        break;
1290
1291      case _C_USHT:
1292        res = objc_read_unsigned_short(stream, va_arg(args, unsigned short*));
1293        break;
1294
1295      case _C_INT:
1296        res = objc_read_int(stream, va_arg(args, int*));
1297        break;
1298       
1299      case _C_UINT:
1300        res = objc_read_unsigned_int(stream, va_arg(args, unsigned int*));
1301        break;
1302
1303      case _C_LNG:
1304        res = objc_read_long(stream, va_arg(args, long*));
1305        break;
1306       
1307      case _C_ULNG:
1308        res = objc_read_unsigned_long(stream, va_arg(args, unsigned long*));
1309        break;
1310
1311      case _C_CHARPTR:
1312      case _C_ATOM:
1313        {
1314          char** str = va_arg(args, char**);
1315          res = objc_read_string (stream, str);
1316        }
1317        break;
1318
1319      case _C_ARY_B:
1320        {
1321          int len = atoi(c+1);
1322          const char* t = c;
1323          while (isdigit(*++t));
1324          res = objc_read_array (stream, t, len, va_arg(args, void*));
1325          t = objc_skip_typespec (t);
1326          if (*t != _C_ARY_E)
1327            __objc_fatal("expected `]', got: %s", t);
1328        }
1329        break;
1330       
1331      default:
1332        fprintf(stderr, "objc_read_types: cannot parse typespec: %s\n", type);
1333        abort();
1334      }
1335    }
1336  va_end(args);
1337  return res;
1338}
1339
1340/*
1341** Write an array of COUNT elements of TYPE from the memory address DATA.
1342** This is equivalent of objc_write_type (stream, "[N<type>]", data)
1343*/
1344
1345int
1346objc_write_array (TypedStream* stream, const char* type,
1347                  int count, const void* data)
1348{
1349  int off = objc_sizeof_type(type);
1350  const char* where = data;
1351
1352  while (count-- > 0)
1353    {
1354      objc_write_type(stream, type, where);
1355      where += off;
1356    }
1357  return 1;
1358}
1359
1360/*
1361** Read an array of COUNT elements of TYPE into the memory address
1362** DATA.  The memory pointed to by data is supposed to be allocated
1363** by the callee.  This is equivalent of
1364**   objc_read_type (stream, "[N<type>]", data)
1365*/
1366
1367int
1368objc_read_array (TypedStream* stream, const char* type,
1369                 int count, void* data)
1370{
1371  int off = objc_sizeof_type(type);
1372  char* where = (char*)data;
1373
1374  while (count-- > 0)
1375    {
1376      objc_read_type(stream, type, where);
1377      where += off;
1378    }
1379  return 1;
1380}
1381
1382static void
1383__objc_free (void* p)
1384{
1385  free (p);
1386}
1387
1388static int
1389__objc_fread(FILE* file, char* data, int len)
1390{
1391  return fread(data, len, 1, file);
1392}
1393
1394static int
1395__objc_fwrite(FILE* file, char* data, int len)
1396{
1397  return fwrite(data, len, 1, file);
1398}
1399
1400static int
1401__objc_feof(FILE* file)
1402{
1403  return feof(file);
1404}
1405
1406static int
1407__objc_no_write(FILE* file, char* data, int len)
1408{
1409  __objc_fatal ("TypedStream not open for writing");
1410}
1411
1412static int
1413__objc_no_read(FILE* file, char* data, int len)
1414{
1415  __objc_fatal ("TypedStream not open for reading");
1416}
1417
1418static int
1419__objc_read_typed_stream_signature (TypedStream* stream)
1420{
1421  char buffer[80];
1422  int pos = 0;
1423  do
1424    (*stream->read)(stream->physical, buffer+pos, 1);
1425  while (buffer[pos++] != '\0');
1426  sscanf (buffer, "GNU TypedStream %d", &stream->version);
1427  if (stream->version != OBJC_TYPED_STREAM_VERSION)
1428    __objc_fatal ("cannot handle TypedStream version %d", stream->version);
1429  return 1;
1430}
1431
1432static int
1433__objc_write_typed_stream_signature (TypedStream* stream)
1434{
1435  char buffer[80];
1436  sprintf(buffer, "GNU TypedStream %d", OBJC_TYPED_STREAM_VERSION);
1437  stream->version = OBJC_TYPED_STREAM_VERSION;
1438  (*stream->write)(stream->physical, buffer, strlen(buffer)+1);
1439  return 1;
1440}
1441
1442static void __objc_finish_write_root_object(struct objc_typed_stream* stream)
1443{
1444  hash_delete (stream->object_table);
1445  stream->object_table = hash_new(64,
1446                                  (hash_func_type)hash_ptr,
1447                                  (compare_func_type)compare_ptrs);
1448}
1449
1450static void __objc_finish_read_root_object(struct objc_typed_stream* stream)
1451{
1452  node_ptr node;
1453  struct objc_list* free_list;
1454  SEL awake_sel = sel_get_any_uid ("awake");
1455
1456  /* resolve object forward references */
1457  free_list = list_cons(NULL, NULL);
1458  for (node = hash_next (stream->object_refs, NULL); node;
1459       node = hash_next (stream->object_refs, node))
1460    {
1461      struct objc_list* reflist = node->value;
1462      const void* key = node->key;
1463      id object = hash_value_for_key (stream->object_table, key);
1464      while(reflist)
1465        {
1466          *((id*)reflist->head) = object;
1467          if (list_find(&free_list, reflist) == NULL)
1468            free_list = list_cons (reflist, free_list);
1469          reflist = reflist->tail;
1470        }
1471    }
1472  list_mapcar (free_list, __objc_free);
1473  list_free (free_list);
1474
1475  /* empty object reference table */
1476  hash_delete (stream->object_refs);
1477  stream->object_refs = hash_new(8, (hash_func_type)hash_ptr,
1478                                 (compare_func_type)compare_ptrs);
1479 
1480  /* call -awake for all objects read  */
1481  if (awake_sel)
1482    {
1483      for (node = hash_next (stream->object_table, NULL); node;
1484           node = hash_next (stream->object_table, node))
1485        {
1486          id object = node->value;
1487          if (__objc_responds_to (object, awake_sel))
1488            (*objc_msg_lookup(object, awake_sel))(object, awake_sel);
1489        }
1490    }
1491
1492  /* empty object table */
1493  hash_delete (stream->object_table);
1494  stream->object_table = hash_new(64,
1495                                  (hash_func_type)hash_ptr,
1496                                  (compare_func_type)compare_ptrs);
1497}
1498
1499/*
1500** Open the stream PHYSICAL in MODE
1501*/
1502
1503TypedStream*
1504objc_open_typed_stream (FILE* physical, int mode)
1505{
1506  TypedStream* s = (TypedStream*)__objc_xmalloc(sizeof(TypedStream));
1507
1508  s->mode = mode;
1509  s->physical = physical;
1510  s->stream_table = hash_new(64,
1511                             (hash_func_type)hash_ptr,
1512                             (compare_func_type)compare_ptrs);
1513  s->object_table = hash_new(64,
1514                             (hash_func_type)hash_ptr,
1515                             (compare_func_type)compare_ptrs);
1516  s->eof = (objc_typed_eof_func)__objc_feof;
1517  s->flush = (objc_typed_flush_func)fflush;
1518  s->writing_root_p = 0;
1519  if (mode == OBJC_READONLY)
1520    {
1521      s->class_table = hash_new(8, (hash_func_type)hash_string,
1522                                (compare_func_type)compare_strings);
1523      s->object_refs = hash_new(8, (hash_func_type)hash_ptr,
1524                                (compare_func_type)compare_ptrs);
1525      s->read = (objc_typed_read_func)__objc_fread;
1526      s->write = (objc_typed_write_func)__objc_no_write;
1527      __objc_read_typed_stream_signature (s);
1528    }
1529  else if (mode == OBJC_WRITEONLY)
1530    {
1531      s->class_table = 0;
1532      s->object_refs = 0;
1533      s->read = (objc_typed_read_func)__objc_no_read;
1534      s->write = (objc_typed_write_func)__objc_fwrite;
1535      __objc_write_typed_stream_signature (s);
1536    }     
1537  else
1538    {
1539      objc_close_typed_stream (s);
1540      return NULL;
1541    }
1542  s->type = OBJC_FILE_STREAM;
1543  return s;
1544}
1545
1546/*
1547** Open the file named by FILE_NAME in MODE
1548*/
1549
1550TypedStream*
1551objc_open_typed_stream_for_file (const char* file_name, int mode)
1552{
1553  FILE* file = NULL;
1554  TypedStream* s;
1555
1556  if (mode == OBJC_READONLY)
1557    file = fopen (file_name, "r");
1558  else
1559    file = fopen (file_name, "w");
1560
1561  if (file)
1562    {
1563      s = objc_open_typed_stream (file, mode);
1564      if (s)
1565        s->type |= OBJC_MANAGED_STREAM;
1566      return s;
1567    }
1568  else
1569    return NULL;
1570}
1571
1572/*
1573** Close STREAM freeing the structure it self.  If it was opened with
1574** objc_open_typed_stream_for_file, the file will also be closed.
1575*/
1576
1577void
1578objc_close_typed_stream (TypedStream* stream)
1579{
1580  if (stream->mode == OBJC_READONLY)
1581    {
1582      __objc_finish_read_root_object (stream); /* Just in case... */
1583      hash_delete (stream->class_table);
1584      hash_delete (stream->object_refs);
1585    }
1586
1587  hash_delete (stream->stream_table);
1588  hash_delete (stream->object_table);
1589
1590  if (stream->type == (OBJC_MANAGED_STREAM | OBJC_FILE_STREAM))
1591    fclose ((FILE*)stream->physical);
1592
1593  free (stream);
1594}
1595
1596BOOL
1597objc_end_of_typed_stream (TypedStream* stream)
1598{
1599  return (*stream->eof)(stream->physical);
1600}
1601
1602void
1603objc_flush_typed_stream (TypedStream* stream)
1604{
1605  (*stream->flush)(stream->physical);
1606}
1607
1608long
1609objc_get_stream_class_version (TypedStream* stream, Class class)
1610{
1611  if (stream->class_table)
1612    return PTR2LONG(hash_value_for_key (stream->class_table, class->name));
1613  else
1614    return class_get_version (class);
1615}
1616
Note: See TracBrowser for help on using the repository browser.