1 #ifdef RCSID
2 static char RCSid[] =
3 "$Header: d:/cvsroot/tads/tads3/TCT3IMG.CPP,v 1.1 1999/07/11 00:46:57 MJRoberts Exp $";
4 #endif
5 
6 /*
7  *   Copyright (c) 1999, 2002 Michael J. Roberts.  All Rights Reserved.
8  *
9  *   Please see the accompanying license file, LICENSE.TXT, for information
10  *   on using and copying this software.
11  */
12 /*
13 Name
14   tct3.cpp - TADS 3 Compiler - T3 VM Code Generator - image writing functions
15 Function
16   Image writing routines for the T3-specific code generator
17 Notes
18 
19 Modified
20   05/08/99 MJRoberts  - Creation
21 */
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 
26 #include "t3std.h"
27 #include "os.h"
28 #include "tcprs.h"
29 #include "tct3.h"
30 #include "tcgen.h"
31 #include "vmtype.h"
32 #include "vmwrtimg.h"
33 #include "vmgram.h"
34 #include "vmfile.h"
35 #include "tcmain.h"
36 #include "tcerr.h"
37 #include "tcmake.h"
38 #include "tctok.h"
39 
40 
41 /* ------------------------------------------------------------------------ */
42 /*
43  *   Object file signature
44  */
45 static const char obj_file_sig[] = "TADS3.Object.000D\n\r\032";
46 
47 
48 /* ------------------------------------------------------------------------ */
49 /*
50  *   Write an object file.  The object file contains the raw byte streams
51  *   with the generated code; the fixup lists for the streams; the global
52  *   symbol table; and the function and metaclass dependency lists.
53  */
write_to_object_file(CVmFile * fp,CTcMake *)54 void CTcGenTarg::write_to_object_file(CVmFile *fp, CTcMake *)
55 {
56     ulong flags;
57 
58     /* write the signature */
59     fp->write_bytes(obj_file_sig, sizeof(obj_file_sig) - 1);
60 
61     /* compute the object file flags */
62     flags = 0;
63     if (G_debug)
64         flags |= TCT3_OBJHDR_DEBUG;
65 
66     /* write the flags */
67     fp->write_int4(flags);
68 
69     /* write the constant and code pool indivisible object maxima */
70     fp->write_int4(max_str_len_);
71     fp->write_int4(max_list_cnt_);
72     fp->write_int4(max_bytecode_len_);
73 
74     /*
75      *   Write the maximum object and property ID's.  When we load this
76      *   object file, we'll need to generate a translated ID number for
77      *   each object ID and for each property ID, to translate from the
78      *   numbering system in the object file to the final image file
79      *   numbering system.  It is helpful if we know early on how many of
80      *   each there are, so that we can allocate table space accordingly.
81      */
82     fp->write_int4(next_obj_);
83     fp->write_int4(next_prop_);
84     fp->write_int4(G_prs->get_enum_count());
85 
86     /* write the function set dependency table */
87     write_funcdep_to_object_file(fp);
88 
89     /*
90      *   write the metaclass dependency table - note that we must do this
91      *   before writing the global symbol table, because upon loading the
92      *   object file, we must have the dependency table loaded before we
93      *   can load the symbols (so that any metaclass symbols can be
94      *   resolved to their dependency table indices)
95      */
96     write_metadep_to_object_file(fp);
97 
98     /* write the global symbol table */
99     G_prs->write_to_object_file(fp);
100 
101     /* write the main code stream and its fixup list */
102     G_cs_main->write_to_object_file(fp);
103 
104     /* write the static code stream and its fixup list */
105     G_cs_static->write_to_object_file(fp);
106 
107     /* write the data stream and its fixup list */
108     G_ds->write_to_object_file(fp);
109 
110     /* write the object stream and its fixup list */
111     G_os->write_to_object_file(fp);
112 
113     /* write the intrinsic class modifier stream */
114     G_icmod_stream->write_to_object_file(fp);
115 
116     /* write the BigNumber stream and its fixup list */
117     G_bignum_stream->write_to_object_file(fp);
118 
119     /* write the static initializer ID stream */
120     G_static_init_id_stream->write_to_object_file(fp);
121 
122     /* write the object ID fixup list */
123     CTcIdFixup::write_to_object_file(fp, G_objfixup);
124 
125     /* write the property ID fixup list */
126     CTcIdFixup::write_to_object_file(fp, G_propfixup);
127 
128     /* write the enumerator ID fixup list */
129     CTcIdFixup::write_to_object_file(fp, G_enumfixup);
130 
131     /* write debugging information if we're compiling for the debugger */
132     if (G_debug)
133     {
134         tct3_debug_line_page *pg;
135 
136         /* write the source file list */
137         write_sources_to_object_file(fp);
138 
139         /*
140          *   Write the pointers to the debug line records in the code
141          *   stream, so that we can fix up the line records on re-loading
142          *   the object file (they'll need to be adjusted for the new
143          *   numbering system for the source file descriptors).  First,
144          *   write the total number of pointers.
145          */
146         fp->write_int4(debug_line_cnt_);
147 
148         /* now write the pointers, one page at a time */
149         for (pg = debug_line_head_ ; pg != 0 ; pg = pg->nxt)
150         {
151             size_t pgcnt;
152 
153             /*
154              *   if this is the last entry, it might only be partially
155              *   full; otherwise, it's completely full, because we always
156              *   fill a page before allocating a new one
157              */
158             if (pg->nxt == 0)
159                 pgcnt = (size_t)(debug_line_cnt_ % TCT3_DEBUG_LINE_PAGE_SIZE);
160             else
161                 pgcnt = TCT3_DEBUG_LINE_PAGE_SIZE;
162 
163             /*
164              *   Write the data - we prepared it in portable format, so we
165              *   can just copy it directly to the file.  Note that each
166              *   entry is four bytes.
167              */
168             fp->write_bytes((char *)pg->line_ofs,
169                             pgcnt * TCT3_DEBUG_LINE_REC_SIZE);
170         }
171 
172         /* write the #define symbols */
173         G_tok->write_macros_to_file_for_debug(fp);
174     }
175 }
176 
177 /* ------------------------------------------------------------------------ */
178 /*
179  *   Write the function-set dependency table to an object file
180  */
write_funcdep_to_object_file(CVmFile * fp)181 void CTcGenTarg::write_funcdep_to_object_file(CVmFile *fp)
182 {
183     tc_fnset_entry *cur;
184 
185     /* write the count */
186     fp->write_int2(fnset_cnt_);
187 
188     /* write the entries */
189     for (cur = fnset_head_ ; cur != 0 ; cur = cur->nxt)
190     {
191         size_t len;
192 
193         len = strlen(cur->nm);
194         fp->write_int2(len);
195         fp->write_bytes(cur->nm, len);
196     }
197 }
198 
199 /*
200  *   Write the metaclass dependency table to an object file
201  */
write_metadep_to_object_file(CVmFile * fp)202 void CTcGenTarg::write_metadep_to_object_file(CVmFile *fp)
203 {
204     tc_meta_entry *cur;
205 
206     /* write the count */
207     fp->write_int2(meta_cnt_);
208 
209     /* write the entries */
210     for (cur = meta_head_ ; cur != 0 ; cur = cur->nxt)
211     {
212         size_t len;
213 
214         len = strlen(cur->nm);
215         fp->write_int2(len);
216         fp->write_bytes(cur->nm, len);
217     }
218 }
219 
220 
221 /* ------------------------------------------------------------------------ */
222 /*
223  *   Load an object file.  We'll read the file, load its data into memory
224  *   (creating global symbol table entries and writing to the code and
225  *   data streams), fix up the fixups to the new base offsets in the
226  *   streams, and translate object and property ID values from the object
227  *   file numbering system to our in-memory numbering system (which will
228  *   usually differ after more than one object file is loaded, because the
229  *   numbering systems in the different files must be reconciled).
230  *
231  *   Returns zero on success; logs errors and returns non-zero on error.
232  */
load_object_file(CVmFile * fp,const textchar_t * fname)233 int CTcGenTarg::load_object_file(CVmFile *fp, const textchar_t *fname)
234 {
235     char buf[128];
236     ulong obj_cnt;
237     ulong prop_cnt;
238     ulong enum_cnt;
239     vm_obj_id_t *obj_xlat = 0;
240     vm_prop_id_t *prop_xlat = 0;
241     ulong *enum_xlat = 0;
242     int err;
243     ulong hdr_flags;
244     ulong siz;
245     ulong main_cs_start_ofs;
246     ulong static_cs_start_ofs;
247 
248     /*
249      *   Before loading anything from the file, go through all of the
250      *   streams and set their object file base offset.  All stream
251      *   offsets that we read from the object file will be relative to the
252      *   these values, since the object file stream data will be loaded in
253      *   after any data currently in the streams.
254      */
255     G_cs_main->set_object_file_start_ofs();
256     G_cs_static->set_object_file_start_ofs();
257     G_ds->set_object_file_start_ofs();
258     G_os->set_object_file_start_ofs();
259     G_icmod_stream->set_object_file_start_ofs();
260     G_bignum_stream->set_object_file_start_ofs();
261     G_static_init_id_stream->set_object_file_start_ofs();
262 
263     /*
264      *   note the main code stream's start offset, since we'll need this
265      *   later in order to process the debug line records; likewise, note
266      *   the static stream's start offset
267      */
268     main_cs_start_ofs = G_cs_main->get_ofs();
269     static_cs_start_ofs = G_cs_static->get_ofs();
270 
271     /* read the signature, and make sure it matches */
272     fp->read_bytes(buf, sizeof(obj_file_sig) - 1);
273     if (memcmp(buf, obj_file_sig, sizeof(obj_file_sig) - 1) != 0)
274     {
275         G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_INV_SIG);
276         return 1;
277     }
278 
279     /* read the file header flags */
280     hdr_flags = (ulong)fp->read_int4();
281 
282     /*
283      *   If we're linking with debugging information, but this object file
284      *   wasn't compiled with debugging information, we won't be able to
285      *   produce a complete debuggable image - log an error to that
286      *   effect.
287      */
288     if (G_debug && (hdr_flags & TCT3_OBJHDR_DEBUG) == 0)
289         G_tcmain->log_error(0, 0, TC_SEV_ERROR,
290                             TCERR_OBJFILE_NO_DBG, fname);
291 
292     /*
293      *   Read the constant and code pool indivisible object maxima.  Note
294      *   each maximum that exceeds the current maximum, since we must keep
295      *   track of the largest indivisible object of each type in the
296      *   entire program.
297      */
298 
299     /* read and note the maximum string constant length */
300     siz = (ulong)fp->read_int4();
301     if (siz > max_str_len_)
302         max_str_len_ = siz;
303 
304     /* read and note the maximum list size */
305     siz = (ulong)fp->read_int4();
306     if (siz > max_list_cnt_)
307         max_list_cnt_ = siz;
308 
309     /* read and note the maximum code pool object size */
310     siz = (ulong)fp->read_int4();
311     if (siz > max_bytecode_len_)
312         max_bytecode_len_ = siz;
313 
314     /*
315      *   read the object, property, and enumerator ID counts from the file
316      *   - these give the highest ID values that are assigned anywhere in
317      *   the object file's numbering system
318      */
319     obj_cnt = (ulong)fp->read_int4();
320     prop_cnt = (ulong)fp->read_int4();
321     enum_cnt = (ulong)fp->read_int4();
322 
323     /*
324      *   Allocate object and property ID translation tables.  These are
325      *   simply arrays of ID's.  Each element of an array gives the global
326      *   ID number assigned to the object whose local ID is the array
327      *   index.  So, obj_xlat[local_id] = global_id.  We need one element
328      *   in the object ID translation array for each local ID in the
329      *   object file, which is obj_cnt; likewise for properties and
330      *   prop_cnt.
331      *
332      *   We're being a bit lazy here by using flat arrays.  This could be
333      *   a problem for very large object files on 16-bit machines: if a
334      *   single object file has more than 16k object ID's (which means
335      *   that it defines and imports more than 16k unique objects), or
336      *   more than 32k property ID's, we'll go over the 64k allocation
337      *   limit on these machines.  This seems unlikely to become a problem
338      *   in practice, but to ensure a graceful failure in such cases,
339      *   check the allocation requirement to make sure we don't go over
340      *   the present platform's architectural limits.
341      */
342     if (obj_cnt * sizeof(obj_xlat[0]) > OSMALMAX
343         || prop_cnt * sizeof(prop_xlat[0]) > OSMALMAX
344         || enum_cnt * sizeof(enum_xlat[0]) > OSMALMAX)
345     {
346         G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_TOO_MANY_IDS);
347         return 2;
348     }
349 
350     /* allocate the translation arrays */
351     obj_xlat = (vm_obj_id_t *)
352                t3malloc((size_t)(obj_cnt * sizeof(obj_xlat[0])));
353     prop_xlat = (vm_prop_id_t *)
354                 t3malloc((size_t)(prop_cnt * sizeof(prop_xlat[0])));
355     enum_xlat = (ulong *)
356                 t3malloc((size_t)(enum_cnt * sizeof(enum_xlat[0])));
357 
358     /* check to make sure we got the memory */
359     if (obj_xlat == 0 || prop_xlat == 0 || enum_xlat == 0)
360     {
361         /* log an error and return failure */
362         G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_OUT_OF_MEM);
363         err = 3;
364         goto done;
365     }
366 
367     /*
368      *   Clear out the translation arrays initially.  We should, in the
369      *   course of loading the symbol table, assign a translation value
370      *   for every entry.  If anything is left at zero (which is invalid
371      *   as an object or property ID), something must be wrong.
372      */
373     memset(obj_xlat, 0, (size_t)(obj_cnt * sizeof(obj_xlat[0])));
374     memset(prop_xlat, 0, (size_t)(prop_cnt * sizeof(prop_xlat[0])));
375     memset(enum_xlat, 0, (size_t)(enum_cnt * sizeof(enum_xlat[0])));
376 
377     /* read the function set dependency table */
378     load_funcdep_from_object_file(fp, fname);
379 
380     /* read the metaclass dependency table */
381     load_metadep_from_object_file(fp, fname);
382 
383     /*
384      *   Read the symbol table.  This will create translation entries for
385      *   the object and property names found in the symbol table.
386      */
387     if ((err = G_prs->load_object_file(fp, fname, obj_xlat, prop_xlat,
388                                        enum_xlat)) != 0)
389     {
390         /* that failed - abort the load */
391         goto done;
392     }
393 
394     /* read the main code stream and its fixup list */
395     G_cs_main->load_object_file(fp, fname);
396 
397     /* read the static code stream and its fixup list */
398     G_cs_static->load_object_file(fp, fname);
399 
400     /* read the data stream and its fixup list */
401     G_ds->load_object_file(fp, fname);
402 
403     /* read the object data stream and its fixup list */
404     G_os->load_object_file(fp, fname);
405 
406     /* read the intrinsic class modifier stream */
407     G_icmod_stream->load_object_file(fp, fname);
408 
409     /* read the BigNumber stream and its fixup list */
410     G_bignum_stream->load_object_file(fp, fname);
411 
412     /* read the static initializer ID stream */
413     G_static_init_id_stream->load_object_file(fp, fname);
414 
415     /* read the object ID fixup list */
416     CTcIdFixup::load_object_file(fp, obj_xlat, obj_cnt, TCGEN_XLAT_OBJ, 4,
417                                  fname, G_keep_objfixups ? &G_objfixup : 0);
418 
419     /* read the property ID fixup list */
420     CTcIdFixup::load_object_file(fp, prop_xlat, prop_cnt, TCGEN_XLAT_PROP, 2,
421                                  fname, G_keep_propfixups ? &G_propfixup : 0);
422 
423     /* read the enum ID fixup list */
424     CTcIdFixup::load_object_file(fp, enum_xlat, enum_cnt, TCGEN_XLAT_ENUM, 2,
425                                  fname, G_keep_enumfixups ? &G_enumfixup : 0);
426 
427     /* if the object file contains debugging information, read that */
428     if ((hdr_flags & TCT3_OBJHDR_DEBUG) != 0)
429     {
430         /* load the debug records */
431         load_debug_records_from_object_file(fp, fname,
432                                             main_cs_start_ofs,
433                                             static_cs_start_ofs);
434     }
435 
436 done:
437     /*
438      *   free the ID translation arrays - we no longer need them after
439      *   loading the object file, because we translate everything in the
440      *   course of loading, so what's left in memory when we're done uses
441      *   the new global numbering system
442      */
443     if (obj_xlat != 0)
444         t3free(obj_xlat);
445     if (prop_xlat != 0)
446         t3free(prop_xlat);
447     if (enum_xlat != 0)
448         t3free(enum_xlat);
449 
450     /* return the result */
451     return err;
452 }
453 
454 
455 /* ------------------------------------------------------------------------ */
456 /*
457  *   Error handler for CTcTokenizer::load_macros_from_file()
458  */
459 class MyLoadMacErr: public CTcTokLoadMacErr
460 {
461 public:
MyLoadMacErr(const char * fname)462     MyLoadMacErr(const char *fname) { fname_ = fname; }
463 
464     /* log an error */
log_error(int err)465     virtual void log_error(int err)
466     {
467         /* check the error code */
468         switch(err)
469         {
470         case 1:
471         case 2:
472             G_tcmain->log_error(0, 0, TC_SEV_ERROR,
473                                 TCERR_OBJFILE_MACRO_SYM_TOO_LONG, fname_);
474             break;
475         }
476     }
477 
478 private:
479     /* the name of the object file we're loading */
480     const char *fname_;
481 };
482 
483 /* ------------------------------------------------------------------------ */
484 /*
485  *   Load the debug records from an object file
486  */
load_debug_records_from_object_file(CVmFile * fp,const textchar_t * fname,ulong main_cs_start_ofs,ulong static_cs_start_ofs)487 void CTcGenTarg::load_debug_records_from_object_file(
488     CVmFile *fp, const textchar_t *fname,
489     ulong main_cs_start_ofs, ulong static_cs_start_ofs)
490 {
491     int first_filedesc;
492     ulong line_table_cnt;
493 
494     /*
495      *   Note the starting number of our file descriptors - in the file,
496      *   we started numbering them at zero, but if we have already loaded
497      *   other object files before this one, we'll be numbering ours after
498      *   the ones previously loaded.  So, we'll need to fix up the
499      *   references to the file descriptor indices accordingly.
500      */
501     first_filedesc = G_tok->get_next_filedesc_index();
502 
503     /* read the source file list */
504     read_sources_from_object_file(fp);
505 
506     /*
507      *   Read the line record pointers.  For each line record table, we
508      *   must fix up the line records to reflect the file descriptor
509      *   numbering system.
510      */
511     for (line_table_cnt = (ulong)fp->read_int4() ; line_table_cnt != 0 ;
512          --line_table_cnt)
513     {
514         uchar stream_id;
515         ulong ofs;
516         CTcCodeStream *cs;
517         ulong start_ofs;
518 
519         /* read the stream ID */
520         stream_id = fp->read_byte();
521 
522         /* find the appropriate code stream */
523         cs = (CTcCodeStream *)
524              CTcDataStream::get_stream_from_id(stream_id, fname);
525 
526         /* get the appropriate starting offset */
527         start_ofs = (cs == G_cs_main ? main_cs_start_ofs
528                                      : static_cs_start_ofs);
529 
530         /*
531          *   Read the next line table offset - this is the offset in the
532          *   code stream of the next debug line table.  Add our starting
533          *   offset to get the true offset.
534          */
535         ofs = (ulong)fp->read_int4() + start_ofs;
536 
537         /* update this table */
538         fix_up_debug_line_table(cs, ofs, first_filedesc);
539     }
540 
541     /* read the macro definitions */
542     CVmFileStream str(fp);
543     MyLoadMacErr err_handler(fname);
544     G_tok->load_macros_from_file(&str, &err_handler);
545 }
546 
547 /*
548  *   Fix up a debug line record table for the current object file
549  */
fix_up_debug_line_table(CTcCodeStream * cs,ulong line_table_ofs,int first_filedesc)550 void CTcGenTarg::fix_up_debug_line_table(CTcCodeStream *cs,
551                                          ulong line_table_ofs,
552                                          int first_filedesc)
553 {
554     uint cnt;
555     ulong ofs;
556 
557     /* read the number of line records in the table */
558     cnt = cs->readu2_at(line_table_ofs);
559 
560     /* adjust each entry */
561     for (ofs = line_table_ofs + 2 ; cnt != 0 ;
562          --cnt, ofs += TCT3_LINE_ENTRY_SIZE)
563     {
564         uint filedesc;
565 
566         /* read the old file descriptor ID */
567         filedesc = cs->readu2_at(ofs + 2);
568 
569         /* adjust it to the new numbering system */
570         filedesc += first_filedesc;
571 
572         /* write it back */
573         cs->write2_at(ofs + 2, filedesc);
574     }
575 }
576 
577 /* ------------------------------------------------------------------------ */
578 /*
579  *   Load a function set dependency table from the object file.  We can
580  *   add to the existing set of functions, but if we have N function sets
581  *   defined already, the first N in the file must match the ones we have
582  *   loaded exactly.
583  */
load_funcdep_from_object_file(class CVmFile * fp,const textchar_t * fname)584 void CTcGenTarg::load_funcdep_from_object_file(class CVmFile *fp,
585                                                const textchar_t *fname)
586 {
587     int cnt;
588     tc_fnset_entry *cur;
589 
590     /* read the count */
591     cnt = fp->read_int2();
592 
593     /* read the entries */
594     for (cur = fnset_head_ ; cnt != 0 ; --cnt)
595     {
596         char buf[128];
597         size_t len;
598 
599         /* read this entry */
600         len = fp->read_uint2();
601         if (len + 1 > sizeof(buf))
602         {
603             G_tcmain->log_error(0, 0, TC_SEV_ERROR,
604                                 TCERR_OBJFILE_INV_FN_OR_META, fname);
605             return;
606         }
607 
608         /* read the name and null-terminate it */
609         fp->read_bytes(buf, len);
610         buf[len] = '\0';
611 
612         /*
613          *   if we are still scanning existing entries, make sure it
614          *   matches; otherwise, add it
615          */
616         if (cur != 0)
617         {
618             const char *vsn;
619             char *ent_vsn;
620             size_t name_len;
621             size_t ent_name_len;
622 
623             /* get the version suffixes, if any */
624             vsn = lib_find_vsn_suffix(buf, '/', 0, &name_len);
625             ent_vsn = (char *)
626                       lib_find_vsn_suffix(cur->nm, '/', 0, &ent_name_len);
627 
628             /* if it doesn't match, it's an error */
629             if (name_len != ent_name_len
630                 || memcmp(cur->nm, buf, name_len) != 0)
631                 G_tcmain->log_error(0, 0, TC_SEV_ERROR,
632                                     TCERR_OBJFILE_FNSET_CONFLICT,
633                                     buf, fname);
634 
635             /*
636              *   if the new version string is higher than the old version
637              *   string, keep the new version string
638              */
639             if (vsn != 0 && ent_vsn != 0 && strcmp(vsn, ent_vsn) > 0
640                 && strlen(vsn) <= strlen(ent_vsn))
641             {
642                 /*
643                  *   the new version is newer than the version in the
644                  *   table - overwrite the table version with the new
645                  *   version, so that the table keeps the newest version
646                  *   mentioned anywhere (newer versions are upwardly
647                  *   compatible with older versions, so the code that uses
648                  *   the older version will be equally happy with the
649                  *   newer version)
650                  */
651                 strcpy(ent_vsn, vsn);
652             }
653 
654             /* move on to the next one */
655             cur = cur->nxt;
656         }
657         else
658         {
659             /* we're past the existing list - add the new function set */
660             add_fnset(buf, len);
661         }
662     }
663 }
664 
665 /*
666  *   Load a metaclass dependency table from the object file.  We can add
667  *   to the existing set of metaclasses, but if we have N metaclasses
668  *   defined already, the first N in the file must match the ones we have
669  *   loaded exactly.
670  */
load_metadep_from_object_file(class CVmFile * fp,const textchar_t * fname)671 void CTcGenTarg::load_metadep_from_object_file(class CVmFile *fp,
672                                                const textchar_t *fname)
673 {
674     int cnt;
675     tc_meta_entry *cur;
676 
677     /* read the count */
678     cnt = fp->read_int2();
679 
680     /* read the entries */
681     for (cur = meta_head_ ; cnt != 0 ; --cnt)
682     {
683         char buf[128];
684         size_t len;
685 
686         /* read this entry */
687         len = fp->read_uint2();
688         if (len + 1 > sizeof(buf))
689         {
690             G_tcmain->log_error(0, 0, TC_SEV_ERROR,
691                                 TCERR_OBJFILE_INV_FN_OR_META, fname);
692             return;
693         }
694 
695         /* read the name and null-terminate it */
696         fp->read_bytes(buf, len);
697         buf[len] = '\0';
698 
699         /*
700          *   if we are still scanning existing entries, make sure it
701          *   matches; otherwise, add it
702          */
703         if (cur != 0)
704         {
705             const char *vsn;
706             char *ent_vsn;
707             size_t name_len;
708             size_t ent_name_len;
709 
710             /* find the version suffix, if any */
711             vsn = lib_find_vsn_suffix(buf, '/', 0, &name_len);
712 
713             /* find the version suffix in this entry's name */
714             ent_vsn = (char *)
715                       lib_find_vsn_suffix(cur->nm, '/', 0, &ent_name_len);
716 
717             /* if it doesn't match the entry name, it's an error */
718             if (name_len != ent_name_len
719                 || memcmp(cur->nm, buf, name_len) != 0)
720             {
721                 /* log a mis-matched metaclass error */
722                 G_tcmain->log_error(0, 0, TC_SEV_ERROR,
723                                     TCERR_OBJFILE_META_CONFLICT, buf, fname);
724             }
725 
726             /*
727              *   if the new version string is higher than the old version
728              *   string, keep the new version string
729              */
730             if (vsn != 0 && ent_vsn != 0 && strcmp(vsn, ent_vsn) > 0
731                 && strlen(vsn) <= strlen(ent_vsn))
732             {
733                 /*
734                  *   the new version is newer than the version in the
735                  *   table - overwrite the table version with the new
736                  *   version, so that the table keeps the newest version
737                  *   mentioned anywhere (newer versions are upwardly
738                  *   compatible with older versions, so the code that uses
739                  *   the older version will be equally happy with the
740                  *   newer version)
741                  */
742                 strcpy(ent_vsn, vsn);
743             }
744 
745             /* move on to the next one */
746             cur = cur->nxt;
747         }
748         else
749         {
750             /* we're past the existing list - add the new metaclass */
751             add_meta(buf, len, 0);
752         }
753     }
754 }
755 
756 
757 /* ------------------------------------------------------------------------ */
758 /*
759  *   Write the source file list to an object file
760  */
write_sources_to_object_file(CVmFile * fp)761 void CTcGenTarg::write_sources_to_object_file(CVmFile *fp)
762 {
763     CTcTokFileDesc *desc;
764 
765     /* write the number of entries */
766     fp->write_int2(G_tok->get_filedesc_count());
767 
768     /* write the entries */
769     for (desc = G_tok->get_first_filedesc() ; desc != 0 ;
770          desc = desc->get_next())
771     {
772         size_t len;
773         const char *fname;
774 
775         /* get the filename - use the resolved local filename */
776         fname = desc->get_fname();
777 
778         /* write the length of the filename */
779         len = strlen(fname);
780         fp->write_int2(len);
781 
782         /* write the filename */
783         fp->write_bytes(fname, len);
784     }
785 }
786 
787 /*
788  *   Read a source file list from an object file
789  */
read_sources_from_object_file(CVmFile * fp)790 void CTcGenTarg::read_sources_from_object_file(CVmFile *fp)
791 {
792     uint cnt;
793     uint i;
794 
795     /* read the number of entries */
796     cnt = fp->read_uint2();
797 
798     /* read the entries */
799     for (i = 0 ; i < cnt ; ++i)
800     {
801         size_t len;
802         char fname[OSFNMAX];
803 
804         /* read the length of the entry */
805         len = fp->read_uint2();
806 
807         /* see if it fits in our buffer */
808         if (len <= sizeof(fname))
809         {
810             /* read it */
811             fp->read_bytes(fname, len);
812         }
813         else
814         {
815             /* it's too long - truncate to the buffer size */
816             fp->read_bytes(fname, sizeof(fname));
817 
818             /* skip the rest */
819             fp->set_pos(fp->get_pos() + len - sizeof(fname));
820 
821             /* note the truncated length */
822             len = sizeof(fname);
823         }
824 
825         /*
826          *   Add it to the tokenizer list.  Always create a new entry,
827          *   rather than re-using an existing entry.  When loading
828          *   multiple object files, this might result in the same file
829          *   appearing as multiple different descriptors, but it's a small
830          *   price to pay (it doesn't add too much redundant space to the
831          *   image file, and in any case the information is only retained
832          *   when we're compiling for debugging) for a big gain in
833          *   simplicity (the source references in the object file can be
834          *   fixed up simply by adding the object file's base index to all
835          *   of the reference indices).
836          */
837         G_tok->create_file_desc(fname, len);
838     }
839 }
840 
841 /* ------------------------------------------------------------------------ */
842 /*
843  *   Calculate pool layouts.  This is called at the start of the link
844  *   phase: at this point, we know the sizes of the largest constant pool
845  *   and code pool objects, so we can figure the layouts of the pools.
846  */
calc_pool_layouts(size_t * first_static_page)847 void CTcGenTarg::calc_pool_layouts(size_t *first_static_page)
848 {
849     size_t max_str;
850     size_t max_list;
851     size_t max_item;
852 
853     /*
854      *   We've parsed the entire program, so we now know the lengths of
855      *   the longest string constant and the longest list constant.  From
856      *   this, we can figure the size of our constant pool pages: since
857      *   each list or string must be contained entirely in a single page,
858      *   the minimum page size is the length of the longest string or list.
859      *
860      *   We must pick a power of two for our page size.  We don't want to
861      *   make the page size too small; each page requires a small amount
862      *   of overhead, hence the more pages for a given total constant pool
863      *   size, the more overhead.  We also don't want to make the page
864      *   size too large, because smaller page sizes will give us better
865      *   performance on small machines that will have to swap pages in and
866      *   out (the smaller a page, the less time it will take to load a
867      *   page).
868      *
869      *   Start at 2k, which is big enough that the data part will
870      *   overwhelm the per-page overhead, but small enough that it can be
871      *   loaded quickly on a small machine.  If that's at least twice the
872      *   length of the longest string or list, use it; otherwise, double
873      *   it and try again.
874      */
875 
876     /*
877      *   find the length in bytes of the longest string - we require the
878      *   length prefix in addition to the bytes of the string
879      */
880     max_str = max_str_len_ + VMB_LEN;
881 
882     /*
883      *   find the length in bytes of the longest list - we require one
884      *   data holder per element, plus the length prefix
885      */
886     max_list = (max_list_cnt_ * VMB_DATAHOLDER) + VMB_LEN;
887 
888     /* get the larger of the two - this will be our minimum size */
889     max_item = max_str;
890     if (max_list > max_item)
891         max_item = max_list;
892 
893     /*
894      *   if the maximum item size is under 16k, look for a size that will
895      *   hold twice the maximum item size; otherwise, relax this
896      *   requirement, since the pages are getting big, and look for
897      *   something that merely fits the largest element
898      */
899     if (max_item < 16*1024)
900         max_item <<= 1;
901 
902     /* calculate the constant pool layout */
903     const_layout_.calc_layout(G_ds, max_item, TRUE);
904 
905     /* calculate the main code pool layout */
906     code_layout_.calc_layout(G_cs_main, max_bytecode_len_, TRUE);
907 
908     /* note the number of pages of regular code */
909     *first_static_page = code_layout_.page_cnt_;
910 
911     /*
912      *   add the static pool into the code pool layout, since we'll
913      *   ultimately write the static code as part of the plain code pages
914      */
915     code_layout_.calc_layout(G_cs_static, max_bytecode_len_, FALSE);
916 }
917 
918 
919 /* ------------------------------------------------------------------------ */
920 /*
921  *   Write the image file
922  */
write_to_image(CVmFile * fp,uchar data_xor_mask,const char tool_data[4])923 void CTcGenTarg::write_to_image(CVmFile *fp, uchar data_xor_mask,
924                                 const char tool_data[4])
925 {
926     tc_meta_entry *meta;
927     CTcSymbol *sym;
928     unsigned long main_ofs;
929     vm_prop_id_t construct_prop = VM_INVALID_PROP;
930     vm_prop_id_t finalize_prop = VM_INVALID_PROP;
931     vm_prop_id_t objcall_prop = VM_INVALID_PROP;
932     tc_fnset_entry *fnset;
933     CVmImageWriter *image_writer;
934     int bignum_idx;
935     int int_class_idx;
936     CTcPrsExport *exp;
937     CTcDataStream *cs_list[2];
938     size_t first_static_code_page;
939 
940     /*
941      *   if we have any BigNumber data, get the BigNumber metaclass index
942      *   (or define it, if the program didn't do so itself)
943      */
944     if (G_bignum_stream->get_ofs() != 0)
945         bignum_idx = find_or_add_meta("bignumber", 9, 0);
946 
947     /* apply internal object/property ID fixups in the symbol table */
948     G_prs->apply_internal_fixups();
949 
950     /* build the grammar productions */
951     G_prs->build_grammar_productions();
952 
953     /*
954      *   Build the dictionaries.  We must wait until after applying the
955      *   internal fixups to build the dictionaries, so that we have the
956      *   final, fully-resolved form of each object's vocabulary list before
957      *   we build the dictionaries.  We must also wait until after we build
958      *   the grammar productions, because the grammar productions can add
959      *   dictionary entries for their literal token matchers.
960      */
961     G_prs->build_dictionaries();
962 
963     /* make sure the the IntrinsicClass intrinsic class is itself defined */
964     int_class_idx = find_or_add_meta("intrinsic-class", 15, 0);
965 
966     /* build the IntrinsicClass objects */
967     build_intrinsic_class_objs(G_int_class_stream);
968 
969     /* calculate the final pool layouts */
970     calc_pool_layouts(&first_static_code_page);
971 
972     /* build the source line location maps, if debugging */
973     if (G_debug)
974         build_source_line_maps();
975 
976     /* look up the "_main" symbol in the global symbol table */
977     sym = G_prs->get_global_symtab()->find("_main");
978 
979     /*
980      *   if there's no "_main" symbol, or it's not a function, it's an
981      *   error
982      */
983     if (sym == 0)
984     {
985         /* "_main" isn't defined - log an error and abort */
986         G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MAIN_NOT_DEFINED);
987         return;
988     }
989     else if (sym->get_type() != TC_SYM_FUNC)
990     {
991         /* "_main" isn't a function - log an error and abort */
992         G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MAIN_NOT_FUNC);
993         return;
994     }
995     else
996     {
997         /*
998          *   Get the "_main" symbol's code pool address - this is the
999          *   program's entrypoint.  We can ask for this information at
1000          *   this point because we don't start writing the image file
1001          *   until after the final fixup pass, which is where this address
1002          *   is finally calculated.
1003          */
1004         main_ofs = ((CTcSymFunc *)sym)->get_code_pool_addr();
1005     }
1006 
1007     /* get the constructor and finalizer property ID's */
1008     construct_prop = G_prs->get_constructor_prop();
1009     finalize_prop = G_prs->get_finalize_prop();
1010     objcall_prop = G_prs->get_objcall_prop();
1011 
1012     /* create our image writer */
1013     image_writer = new CVmImageWriter(fp);
1014 
1015     /* prepare the image file - use file format version 1 */
1016     image_writer->prepare(1, tool_data);
1017 
1018     /* write the entrypoint offset and data structure parameters */
1019     image_writer->write_entrypt(main_ofs, TCT3_METHOD_HDR_SIZE,
1020                                 TCT3_EXC_ENTRY_SIZE, TCT3_LINE_ENTRY_SIZE,
1021                                 TCT3_DBG_HDR_SIZE, TCT3_DBG_LCLSYM_HDR_SIZE,
1022                                 TCT3_DBG_FMT_VSN);
1023 
1024     /* begin writing the symbolic items */
1025     image_writer->begin_sym_block();
1026 
1027     /* run through the list of exports in the parser */
1028     for (exp = G_prs->get_exp_head() ; exp != 0 ; exp = exp->get_next())
1029     {
1030         CTcPrsExport *exp2;
1031         int dup_err_cnt;
1032 
1033         /*
1034          *   if this one's external name is null, it means that we've
1035          *   previously encountered it as a duplicate and marked it as such
1036          *   - in this case, simply skip it
1037          */
1038         if (exp->get_ext_name() == 0)
1039             continue;
1040 
1041         /* make sure it's not one of our special ones */
1042         if (exp->ext_name_matches("LastProp")
1043             || exp->ext_name_matches("Constructor")
1044             || exp->ext_name_matches("Destructor")
1045             || exp->ext_name_matches("ObjectCallProp"))
1046         {
1047             /* it's a reserved export - flag an error */
1048             G_tcmain->log_error(0, 0, TC_SEV_ERROR,
1049                                 TCERR_RESERVED_EXPORT,
1050                                 (int)exp->get_ext_len(),
1051                                 exp->get_ext_name());
1052         }
1053 
1054 
1055         /* look up the symbol, defining as a property if undefined */
1056         sym = G_prs->get_global_symtab()
1057               ->find_or_def_prop(exp->get_sym(), exp->get_sym_len(), FALSE);
1058 
1059         /*
1060          *   Scan the rest of the export list for duplicates.  If we find
1061          *   the symbol external name exported with a different value, it's
1062          *   an error.
1063          */
1064         for (dup_err_cnt = 0, exp2 = exp->get_next() ; exp2 != 0 ;
1065              exp2 = exp2->get_next())
1066         {
1067             /* if this one has already been marked as a duplicate, skip it */
1068             if (exp2->get_ext_name() == 0)
1069                 continue;
1070 
1071             /* check for a match of the external name */
1072             if (exp->ext_name_matches(exp2))
1073             {
1074                 /*
1075                  *   This one matches, so it's a redundant export for the
1076                  *   same name.  If it's being exported as the same internal
1077                  *   symbol as the other one, this is fine; otherwise it's
1078                  *   an error, since the same external name can't be given
1079                  *   two different meanings.
1080                  */
1081                 if (!exp->sym_matches(exp2))
1082                 {
1083                     /*
1084                      *   It doesn't match - log an error.  If we've already
1085                      *   logged an error, show a continuation error;
1086                      *   otherwise show the first error for the symbol.
1087                      */
1088                     ++dup_err_cnt;
1089                     if (dup_err_cnt == 1)
1090                     {
1091                         /* it's the first error - show the long form */
1092                         G_tcmain->log_error(0, 0, TC_SEV_ERROR,
1093                                             TCERR_DUP_EXPORT,
1094                                             (int)exp->get_ext_len(),
1095                                             exp->get_ext_name(),
1096                                             (int)exp->get_sym_len(),
1097                                             exp->get_sym(),
1098                                             (int)exp2->get_sym_len(),
1099                                             exp2->get_sym());
1100                     }
1101                     else
1102                     {
1103                         /* it's a follow-up error */
1104                         G_tcmain->log_error(0, 0, TC_SEV_ERROR,
1105                                             TCERR_DUP_EXPORT_AGAIN,
1106                                             (int)exp->get_ext_len(),
1107                                             exp->get_ext_name(),
1108                                             (int)exp2->get_sym_len(),
1109                                             exp2->get_sym());
1110                     }
1111                 }
1112 
1113                 /*
1114                  *   Regardless of whether this one matches or not, remove
1115                  *   it from the list by setting its external name to null -
1116                  *   we only want to include each symbol in the export list
1117                  *   once.
1118                  */
1119                 exp2->set_extern_name(0, 0);
1120             }
1121         }
1122 
1123         /* write it out according to its type */
1124         switch(sym->get_type())
1125         {
1126         case TC_SYM_OBJ:
1127             /* write the object symbol */
1128             image_writer->write_sym_item_objid(
1129                 exp->get_ext_name(), exp->get_ext_len(),
1130                 ((CTcSymObj *)sym)->get_obj_id());
1131             break;
1132 
1133         case TC_SYM_PROP:
1134             /* write the property symbol */
1135             image_writer->write_sym_item_propid(
1136                 exp->get_ext_name(), exp->get_ext_len(),
1137                 ((CTcSymProp *)sym)->get_prop());
1138             break;
1139 
1140         case TC_SYM_FUNC:
1141             /* write the function symbol */
1142             image_writer->write_sym_item_func(
1143                 exp->get_ext_name(), exp->get_ext_len(),
1144                 ((CTcSymFunc *)sym)->get_code_pool_addr());
1145             break;
1146 
1147         default:
1148             /* can't export other types */
1149             G_tcmain->log_error(0, 0, TC_SEV_ERROR,
1150                                 TCERR_INVALID_TYPE_FOR_EXPORT,
1151                                 (int)exp->get_sym_len(), exp->get_sym());
1152             break;
1153         }
1154     }
1155 
1156     /*
1157      *   write the last property ID - this is a special synthetic export
1158      *   that we provide automatically
1159      */
1160     image_writer->write_sym_item_propid("LastProp", next_prop_);
1161 
1162     /* write our Constructor and Destructor property ID's */
1163     if (construct_prop != VM_INVALID_PROP)
1164         image_writer->write_sym_item_propid("Constructor", construct_prop);
1165     if (finalize_prop != VM_INVALID_PROP)
1166         image_writer->write_sym_item_propid("Destructor", finalize_prop);
1167 
1168     /*
1169      *   write the special property ID for calling properties of anonymous
1170      *   function objects
1171      */
1172     if (objcall_prop != VM_INVALID_PROP)
1173         image_writer->write_sym_item_propid("ObjectCallProp", objcall_prop);
1174 
1175     /* done with the symbolic names */
1176     image_writer->end_sym_block();
1177 
1178     /* write the function-set dependency table */
1179     image_writer->begin_func_dep(fnset_cnt_);
1180     for (fnset = fnset_head_ ; fnset != 0 ; fnset = fnset->nxt)
1181         image_writer->write_func_dep_item(fnset->nm);
1182     image_writer->end_func_dep();
1183 
1184     /* start the metaclass dependency table */
1185     image_writer->begin_meta_dep(meta_cnt_);
1186 
1187     /* write the metaclass dependency items */
1188     for (meta = meta_head_ ; meta != 0 ; meta = meta->nxt)
1189     {
1190         /* write the dependency item */
1191         image_writer->write_meta_dep_item(meta->nm);
1192 
1193         /* if there's an associated symbol, write the property list */
1194         if (meta->sym != 0)
1195         {
1196             CTcSymMetaProp *prop;
1197 
1198             /* scan the list of properties and write each one */
1199             for (prop = meta->sym->get_prop_head() ; prop != 0 ;
1200                  prop = prop->nxt_)
1201             {
1202                 /* write this item's property */
1203                 image_writer->write_meta_item_prop(prop->prop_->get_prop());
1204             }
1205         }
1206     }
1207 
1208     /* end the metaclass dependency table */
1209     image_writer->end_meta_dep();
1210 
1211     /* write the code pool streams (don't bother masking the code bytes) */
1212     cs_list[0] = G_cs_main;
1213     cs_list[1] = G_cs_static;
1214     code_layout_.write_to_image(cs_list, 2, image_writer, 1, 0);
1215 
1216     /*
1217      *   write the constant pool (applying the constant pool data mask to
1218      *   obscure any text strings in the data)
1219      */
1220     const_layout_.write_to_image(&G_ds, 1, image_writer, 2, data_xor_mask);
1221 
1222     /* write the "TADS object" data */
1223     write_tads_objects_to_image(G_os, image_writer, TCT3_METAID_TADSOBJ);
1224 
1225     /* write the intrinsic class modifier object data */
1226     write_tads_objects_to_image(G_icmod_stream, image_writer,
1227                                 TCT3_METAID_ICMOD);
1228 
1229     /* write the dictionary data - this is a stream of dictionary objects */
1230     write_nontads_objs_to_image(G_dict_stream, image_writer,
1231                                 TCT3_METAID_DICT, TRUE);
1232 
1233     /* write the grammar data - this is a stream of production objects */
1234     write_nontads_objs_to_image(G_gramprod_stream, image_writer,
1235                                 TCT3_METAID_GRAMPROD, TRUE);
1236 
1237     /* if we have any BigNumber data, write it out */
1238     if (G_bignum_stream->get_ofs() != 0)
1239         write_nontads_objs_to_image(G_bignum_stream,
1240                                     image_writer, bignum_idx, FALSE);
1241 
1242     /* if we have any IntrinsicClass data, write it out */
1243     if (G_int_class_stream->get_ofs() != 0)
1244         write_nontads_objs_to_image(G_int_class_stream, image_writer,
1245                                     int_class_idx, FALSE);
1246 
1247     /* write the static initializer list */
1248     write_static_init_list(image_writer,
1249                            first_static_code_page * code_layout_.page_size_);
1250 
1251     /* write debug information if desired */
1252     if (G_debug)
1253     {
1254         /* write the source file table */
1255         write_sources_to_image(image_writer);
1256 
1257         /* write the global symbol table to the image file */
1258         write_global_symbols_to_image(image_writer);
1259 
1260         /* write the method header list */
1261         write_method_list_to_image(image_writer);
1262 
1263         /* write the macro records */
1264         write_macros_to_image(image_writer);
1265     }
1266 
1267     /* finish the image file */
1268     image_writer->finish();
1269 
1270     /* delete our image writer */
1271     delete image_writer;
1272     image_writer = 0;
1273 }
1274 
1275 /* ------------------------------------------------------------------------ */
1276 /*
1277  *   Write the static initializer ID list
1278  */
write_static_init_list(CVmImageWriter * image_writer,ulong main_cs_size)1279 void CTcGenTarg::write_static_init_list(CVmImageWriter *image_writer,
1280                                         ulong main_cs_size)
1281 {
1282     ulong rem;
1283     ulong ofs;
1284     ulong init_cnt;
1285 
1286     /*
1287      *   calculate the number of initializers - this is simply the size of
1288      *   the stream divided by the size of each record (4 bytes for object
1289      *   ID, 2 bytes for property ID)
1290      */
1291     init_cnt = G_static_init_id_stream->get_ofs() / 6;
1292 
1293     /* start the block */
1294     image_writer->begin_sini_block(main_cs_size, init_cnt);
1295 
1296     /* write the bytes */
1297     for (ofs = 0, rem = G_static_init_id_stream->get_ofs() ; rem != 0 ; )
1298     {
1299         const char *ptr;
1300         ulong cur;
1301 
1302         /* get the next chunk */
1303         ptr = G_static_init_id_stream->get_block_ptr(ofs, rem, &cur);
1304 
1305         /* write this chunk */
1306         image_writer->write_bytes(ptr, cur);
1307 
1308         /* advance past this chunk */
1309         ofs += cur;
1310         rem -= cur;
1311     }
1312 
1313     /* end the block */
1314     image_writer->end_sini_block();
1315 }
1316 
1317 /* ------------------------------------------------------------------------ */
1318 /*
1319  *   Build the IntrinsicClass objects
1320  */
build_intrinsic_class_objs(CTcDataStream * str)1321 void CTcGenTarg::build_intrinsic_class_objs(CTcDataStream *str)
1322 {
1323     tc_meta_entry *meta;
1324     uint idx;
1325 
1326     /*
1327      *   run through the dependency table, and create an IntrinsicClass
1328      *   object for each entry
1329      */
1330     for (idx = 0, meta = meta_head_ ; meta != 0 ; meta = meta->nxt, ++idx)
1331     {
1332         /*
1333          *   if we have a symbol for this class, add the object to the
1334          *   intrinsic class stream
1335          */
1336         if (meta->sym != 0)
1337         {
1338             /* write the OBJS header */
1339             str->write4(meta->sym->get_class_obj());
1340             str->write2(8);
1341 
1342             /*
1343              *   write the data - the data length (8), followed by the
1344              *   intrinsic class index that this object is associated
1345              *   with, followed by the modifier object
1346              */
1347             str->write2(8);
1348             str->write2(idx);
1349             str->write4(meta->sym->get_mod_obj() == 0
1350                         ? VM_INVALID_OBJ
1351                         : meta->sym->get_mod_obj()->get_obj_id());
1352 
1353             /*
1354              *   fix up the inheritance chain in the modifier objects, if
1355              *   necessary
1356              */
1357             meta->sym->fix_mod_obj_sc_list();
1358         }
1359     }
1360 }
1361 
1362 /* ------------------------------------------------------------------------ */
1363 /*
1364  *   Build the source file line maps.  These maps provide listings from
1365  *   the source location to the executable location, so the debugger can
1366  *   do things such as set a breakpoint at a given source file location.
1367  */
build_source_line_maps()1368 void CTcGenTarg::build_source_line_maps()
1369 {
1370     CTcStreamAnchor *anchor;
1371 
1372     /* go through the list of anchors in the code stream */
1373     for (anchor = G_cs->get_first_anchor() ; anchor != 0 ;
1374          anchor = anchor->nxt_)
1375     {
1376         ulong start_ofs;
1377         ulong start_addr;
1378         ulong dbg_ofs;
1379         uint cnt;
1380         ulong ofs;
1381 
1382         /* get the anchor's stream offset */
1383         start_ofs = anchor->get_ofs();
1384 
1385         /* get the anchor's absolute address in the image file */
1386         start_addr = anchor->get_addr();
1387 
1388         /* read the debug table offset from the method header */
1389         dbg_ofs = start_ofs + G_cs->readu2_at(start_ofs + 8);
1390 
1391         /* if there's no debug table for this method, go on to the next */
1392         if (dbg_ofs == start_ofs)
1393             continue;
1394 
1395         /* read the number of line entries */
1396         cnt = G_cs->readu2_at(dbg_ofs + TCT3_DBG_HDR_SIZE);
1397 
1398         /* go through the individual line entries */
1399         for (ofs = dbg_ofs + TCT3_DBG_HDR_SIZE + 2 ; cnt != 0 ;
1400              --cnt, ofs += TCT3_LINE_ENTRY_SIZE)
1401         {
1402             uint file_id;
1403             ulong linenum;
1404             uint method_ofs;
1405             ulong line_addr;
1406             CTcTokFileDesc *file_desc;
1407 
1408             /*
1409              *   get the file position, and the byte-code offset from the
1410              *   start of the method of the executable code for the line
1411              */
1412             method_ofs = G_cs->readu2_at(ofs);
1413             file_id = G_cs->readu2_at(ofs + 2);
1414             linenum = G_cs->read4_at(ofs + 4);
1415 
1416             /* calculate the absolute address of the line in the image file */
1417             line_addr = start_addr + method_ofs;
1418 
1419             /* find the given file descriptor */
1420             file_desc = G_tok->get_filedesc(file_id);
1421 
1422             /*
1423              *   get the original file descriptor, since we always want to
1424              *   add to the original, not to the duplicates, if the file
1425              *   appears more than once (because this is a one-way mapping
1426              *   from file to byte-code location - we thus require a
1427              *   single index)
1428              */
1429             if (file_desc->get_orig() != 0)
1430                 file_desc = file_desc->get_orig();
1431 
1432             /* add this line to the file descriptor */
1433             file_desc->add_source_line(linenum, line_addr);
1434         }
1435     }
1436 }
1437 
1438 
1439 /* ------------------------------------------------------------------------ */
1440 /*
1441  *   Callback to write enumerated source lines to an image file
1442  */
write_source_lines_cb(void * ctx,ulong linenum,ulong code_addr)1443 static void write_source_lines_cb(void *ctx, ulong linenum, ulong code_addr)
1444 {
1445     CVmImageWriter *image_writer;
1446 
1447     /* get the image writer */
1448     image_writer = (CVmImageWriter *)ctx;
1449 
1450     /* write the data */
1451     image_writer->write_srcf_line_entry(linenum, code_addr);
1452 }
1453 
1454 /*
1455  *   Write the list of source file descriptors to an image file
1456  */
write_sources_to_image(CVmImageWriter * image_writer)1457 void CTcGenTarg::write_sources_to_image(CVmImageWriter *image_writer)
1458 {
1459     CTcTokFileDesc *desc;
1460 
1461     /* write the block prefix */
1462     image_writer->begin_srcf_block(G_tok->get_filedesc_count());
1463 
1464     /* write the entries */
1465     for (desc = G_tok->get_first_filedesc() ; desc != 0 ;
1466          desc = desc->get_next())
1467     {
1468         const char *fname;
1469 
1470         /*
1471          *   Get the filename.  Use the fully resolved local filename, so
1472          *   that the debugger can correlate the resolved file back to the
1473          *   project configuration.  This ties the debug records to the local
1474          *   directory structure, but the only drawback of this is that the
1475          *   program must be recompiled wherever it's to be used with the
1476          *   debugger.
1477          */
1478         fname = desc->get_fname();
1479 
1480         /*
1481          *   if we're in test reporting mode, write only the root name, not
1482          *   the full name - this insulates test logs from the details of
1483          *   local pathname conventions and the local directory structure,
1484          *   allowing for more portable test logs
1485          */
1486         if (G_tcmain->get_test_report_mode())
1487             fname = os_get_root_name((char *)fname);
1488 
1489         /* begin this entry */
1490         image_writer->begin_srcf_entry(desc->get_orig_index(), fname);
1491 
1492         /* write the source lines */
1493         desc->enum_source_lines(write_source_lines_cb, image_writer);
1494 
1495         /* end this entry */
1496         image_writer->end_srcf_entry();
1497     }
1498 
1499     /* end the block */
1500     image_writer->end_srcf_block();
1501 }
1502 
1503 /*
1504  *   Write the method header list to the image file
1505  */
write_method_list_to_image(CVmImageWriter * image_writer)1506 void CTcGenTarg::write_method_list_to_image(CVmImageWriter *image_writer)
1507 {
1508     CTcStreamAnchor *anchor;
1509 
1510     /* begin the method header list block in the image file */
1511     image_writer->begin_mhls_block();
1512 
1513     /* go through the list of anchors in the code stream */
1514     for (anchor = G_cs->get_first_anchor() ; anchor != 0 ;
1515          anchor = anchor->nxt_)
1516     {
1517         /* write this entry's code pool address */
1518         image_writer->write_mhls_entry(anchor->get_addr());
1519     }
1520 
1521     /* end the block */
1522     image_writer->end_mhls_block();
1523 }
1524 
1525 /*
1526  *   Write the preprocessor macros to the image file, for debugger use
1527  */
write_macros_to_image(CVmImageWriter * image_writer)1528 void CTcGenTarg::write_macros_to_image(CVmImageWriter *image_writer)
1529 {
1530     /* begin the macro block */
1531     image_writer->begin_macr_block();
1532 
1533     /*
1534      *   ask the tokenizer to dump the data directly to the file underlying
1535      *   the image writer
1536      */
1537     G_tok->write_macros_to_file_for_debug(image_writer->get_fp());
1538 
1539     /* end the macro block */
1540     image_writer->end_macr_block();
1541 }
1542 
1543 /* ------------------------------------------------------------------------ */
1544 /*
1545  *   Callback context for global symbol table writer
1546  */
1547 struct write_sym_to_image_cb
1548 {
1549     /* number of symbols written */
1550     ulong count;
1551 
1552     /* the image writer */
1553     CVmImageWriter *image_writer;
1554 };
1555 
1556 /*
1557  *   Callback for writing the global symbol table to an object file
1558  */
write_sym_to_image(void * ctx0,CTcSymbol * sym)1559 static void write_sym_to_image(void *ctx0, CTcSymbol *sym)
1560 {
1561     write_sym_to_image_cb *ctx;
1562 
1563     /* cast the context */
1564     ctx = (write_sym_to_image_cb *)ctx0;
1565 
1566     /*
1567      *   If the symbol's name starts with a period, don't write it - the
1568      *   compiler constructs certain private symbol names for its own
1569      *   internal use, and marks them as such by starting the name with a
1570      *   period.  These symbols cannot be used to evaluate expressions, so
1571      *   they're of no use in teh global symbol table in the image file.
1572      */
1573     if (sym->get_sym()[0] == '.')
1574         return;
1575 
1576     /* ask the symbol to do the work */
1577     if (sym->write_to_image_file_global(ctx->image_writer))
1578     {
1579         /* we wrote the symbol - count it */
1580         ++(ctx->count);
1581     }
1582 }
1583 
1584 /*
1585  *   Write the global symbol table to an object file
1586  */
write_global_symbols_to_image(CVmImageWriter * image_writer)1587 void CTcGenTarg::write_global_symbols_to_image(CVmImageWriter *image_writer)
1588 {
1589     write_sym_to_image_cb ctx;
1590 
1591     /* set up the callback context */
1592     ctx.count = 0;
1593     ctx.image_writer = image_writer;
1594 
1595     /* start the block */
1596     image_writer->begin_gsym_block();
1597 
1598     /* ask the symbol table to enumerate itself through our symbol writer */
1599     G_prs->get_global_symtab()->enum_entries(&write_sym_to_image, &ctx);
1600 
1601     /* end the block */
1602     image_writer->end_gsym_block(ctx.count);
1603 }
1604 
1605 /* ------------------------------------------------------------------------ */
1606 /*
1607  *   Look up a property
1608  */
look_up_prop(const char * propname,int required,int err_if_undef,int err_if_not_prop)1609 vm_prop_id_t CTcGenTarg::look_up_prop(const char *propname, int required,
1610                                       int err_if_undef, int err_if_not_prop)
1611 {
1612     CTcSymbol *sym;
1613 
1614     /* look up the symbol */
1615     sym = G_prs->get_global_symtab()->find(propname);
1616 
1617     /* check to see if it's defined and of the proper type */
1618     if (sym == 0)
1619     {
1620         /* log the 'undefined' error */
1621         G_tcmain->log_error(0, 0, required ? TC_SEV_ERROR : TC_SEV_PEDANTIC,
1622                             err_if_undef);
1623     }
1624     else if (sym->get_type() != TC_SYM_PROP)
1625     {
1626         /* log the 'not a property' error */
1627         G_tcmain->log_error(0, 0, required ? TC_SEV_ERROR : TC_SEV_PEDANTIC,
1628                             err_if_not_prop);
1629     }
1630     else
1631     {
1632         /* return the property ID */
1633         return ((CTcSymProp *)sym)->get_prop();
1634     }
1635 
1636     /* if we got here, we didn't find a valid property */
1637     return VM_INVALID_PROP;
1638 }
1639 
1640 
1641 /* ------------------------------------------------------------------------ */
1642 /*
1643  *   Write a TADS object stream to the image file.  We'll write blocks of
1644  *   size up to somewhat less than 64k, to ensure that the file is usable on
1645  *   16-bit machines.
1646  */
write_tads_objects_to_image(CTcDataStream * os,CVmImageWriter * image_writer,int meta_idx)1647 void CTcGenTarg::write_tads_objects_to_image(CTcDataStream *os,
1648                                              CVmImageWriter *image_writer,
1649                                              int meta_idx)
1650 {
1651     /* write the persistent (non-transient) objects */
1652     write_tads_objects_to_image(os, image_writer, meta_idx, FALSE);
1653 
1654     /* write the transient objects */
1655     write_tads_objects_to_image(os, image_writer, meta_idx, TRUE);
1656 }
1657 
1658 /*
1659  *   Write the TADS object stream to the image file, writing only persistent
1660  *   or transient objects.
1661  */
write_tads_objects_to_image(CTcDataStream * os,CVmImageWriter * image_writer,int meta_idx,int trans)1662 void CTcGenTarg::write_tads_objects_to_image(CTcDataStream *os,
1663                                              CVmImageWriter *image_writer,
1664                                              int meta_idx, int trans)
1665 {
1666     ulong start_ofs;
1667 
1668     /* keep going until we've written the whole file */
1669     for (start_ofs = 0 ; start_ofs < os->get_ofs() ; )
1670     {
1671         ulong ofs;
1672         uint siz;
1673         uint cnt;
1674         uint block_size;
1675 
1676         /*
1677          *   Scan the stream.  Each entry in the stream is a standard
1678          *   object record, which means that it starts with the object ID
1679          *   (UINT4) and the length (UINT2) of the metaclass-specific
1680          *   data, which is then followed by the metaclass data.  Skip as
1681          *   many objects as we can while staying within our approximately
1682          *   64k limit.
1683          */
1684         for (block_size = 0, ofs = start_ofs, cnt = 0 ; ; )
1685         {
1686             uint flags;
1687             ulong rem_len;
1688             size_t orig_prop_cnt;
1689             size_t write_prop_cnt;
1690             size_t write_size;
1691             ulong next_ofs;
1692             ulong orig_ofs;
1693 
1694             /* if we've reached the end of the stream, we're done */
1695             if (ofs >= os->get_ofs())
1696                 break;
1697 
1698             /* remember the starting offset */
1699             orig_ofs = ofs;
1700 
1701             /* read our internal flags */
1702             flags = os->readu2_at(ofs + TCT3_OBJ_INTERNHDR_FLAGS_OFS);
1703 
1704             /*
1705              *   get the size of this block - this is the
1706              *   metaclass-specific data size at offset 4 in the T3
1707              *   metaclass header, plus the size of the T3 metaclass
1708              *   header, plus the size of our internal header
1709              */
1710             siz = TCT3_OBJ_INTERNHDR_SIZE
1711                   + TCT3_META_HEADER_SIZE
1712                   + os->readu2_at(ofs + TCT3_META_HEADER_OFS + 4);
1713 
1714             /*
1715              *   Calculate the offset of the next block.  Note that this is
1716              *   the current offset plus the original block size; the amount
1717              *   of data we end up writing might be less than the original
1718              *   block size because we might have deleted property slots
1719              *   when we sorted and compressed the property table.
1720              */
1721             next_ofs = ofs + siz;
1722 
1723             /* if this object was deleted, skip it */
1724             if ((flags & TCT3_OBJ_REPLACED) != 0)
1725             {
1726                 ofs = next_ofs;
1727                 continue;
1728             }
1729 
1730             /*
1731              *   if this object is of the wrong persistent/transient type,
1732              *   skip it
1733              */
1734             if (((flags & TCT3_OBJ_TRANSIENT) != 0) != (trans != 0))
1735             {
1736                 ofs = next_ofs;
1737                 continue;
1738             }
1739 
1740             /*
1741              *   if this would push us over the limit, stop here and start a
1742              *   new block
1743              */
1744             if (block_size + siz > 64000L)
1745                 break;
1746 
1747             /*
1748              *   We must sort the property table, in order of ascending
1749              *   property ID, before we write the image file.  We had to
1750              *   wait until now to do this, because the final property ID
1751              *   assignments aren't made until link time.
1752              */
1753             write_prop_cnt = sort_object_prop_table(os, ofs);
1754 
1755             /* note the original property count */
1756             orig_prop_cnt = CTPNStmObject::get_stream_prop_cnt(os, ofs);
1757 
1758             /*
1759              *   Then temporarily pdate the property count in the stream, in
1760              *   case we changed it in the sorting process.
1761              *
1762              *   Calculate the new size of the data to write.  Note that we
1763              *   must add in the size of the T3 metaclass header, since this
1764              *   isn't reflected in the data size.
1765              */
1766             write_size =
1767                 CTPNStmObject::set_stream_prop_cnt(os, ofs, write_prop_cnt)
1768                 + TCT3_META_HEADER_SIZE;
1769 
1770             /*
1771              *   if this is the first object in this block, write the
1772              *   block header
1773              */
1774             if (cnt == 0)
1775                 image_writer->begin_objs_block(meta_idx, FALSE, trans);
1776 
1777             /*
1778              *   skip past our internal header - we don't want to write
1779              *   our internal header to the image file, since this was
1780              *   purely for our own use in the compiler and linker
1781              */
1782             ofs += TCT3_OBJ_INTERNHDR_SIZE;
1783 
1784             /*
1785              *   write the object data; write the size returned from
1786              *   sorting the property table, which might be different than
1787              *   the original block data size in the stream, because we
1788              *   might have compressed the property table
1789              */
1790             for (rem_len = write_size ; rem_len != 0 ; )
1791             {
1792                 const char *p;
1793                 ulong avail_len;
1794 
1795                 /* get the next block */
1796                 p = os->get_block_ptr(ofs, rem_len, &avail_len);
1797 
1798                 /* write it out */
1799                 image_writer->write_objs_bytes(p, avail_len);
1800 
1801                 /* move past this block */
1802                 ofs += avail_len;
1803                 rem_len -= avail_len;
1804             }
1805 
1806             /* count the object */
1807             ++cnt;
1808 
1809             /* restore the original stream property count */
1810             CTPNStmObject::set_stream_prop_cnt(os, orig_ofs, orig_prop_cnt);
1811 
1812             /* move on to the next block */
1813             ofs = next_ofs;
1814         }
1815 
1816         /* if we wrote any objects, end the block */
1817         if (cnt != 0)
1818             image_writer->end_objs_block(cnt);
1819 
1820         /* move on to the next block */
1821         start_ofs = ofs;
1822     }
1823 }
1824 
1825 /* ------------------------------------------------------------------------ */
1826 /*
1827  *   Write an object stream of non-TADS objects to the image file
1828  */
write_nontads_objs_to_image(CTcDataStream * os,CVmImageWriter * image_writer,int meta_idx,int large_objs)1829 void CTcGenTarg::write_nontads_objs_to_image(CTcDataStream *os,
1830                                              CVmImageWriter *image_writer,
1831                                              int meta_idx, int large_objs)
1832 {
1833     ulong start_ofs;
1834 
1835     /* keep going until we've written the whole file */
1836     for (start_ofs = 0 ; start_ofs < os->get_ofs() ; )
1837     {
1838         ulong ofs;
1839         uint siz;
1840         uint cnt;
1841         uint block_size;
1842 
1843         /*
1844          *   Scan the stream.  Each entry in the stream is either a small or
1845          *   large object record,, which means that it starts with the
1846          *   object ID (UINT4) and the length (UINT2 for small, UINT4 for
1847          *   large) of the metaclass-specific data, which is then followed
1848          *   by the metaclass data.
1849          *
1850          *   Include as many objects as we can while staying within our
1851          *   approximately 64k limit, if this is a small-format block; fill
1852          *   the block without limit if this is a large-format block.
1853          */
1854         for (block_size = 0, ofs = start_ofs, cnt = 0 ; ; )
1855         {
1856             ulong rem_len;
1857             ulong next_ofs;
1858 
1859             /* if we've reached the end of the stream, we're done */
1860             if (ofs >= os->get_ofs())
1861                 break;
1862 
1863             /*
1864              *   get the size of this block - this is the
1865              *   metaclass-specific data size at offset 4 in the T3
1866              *   metaclass header, plus the size of the T3 metaclass
1867              *   header
1868              */
1869             if (large_objs)
1870             {
1871                 /*
1872                  *   Get the 32-bit size value.  Note that we don't worry
1873                  *   about limiting the overall block size to 64k when we're
1874                  *   writing a "large" object block.
1875                  */
1876                 siz = (ulong)os->read4_at(ofs + 4)
1877                       + TCT3_LARGE_META_HEADER_SIZE;
1878             }
1879             else
1880             {
1881                 /* get the 16-bit size value */
1882                 siz = (ulong)os->read2_at(ofs + 4)
1883                       + TCT3_META_HEADER_SIZE;
1884 
1885                 /*
1886                  *   Since this is a small-object block, limit the aggregate
1887                  *   size of the entire block to 64k.  So, if this block
1888                  *   would push us over the 64k aggregate for the block,
1889                  *   start a new OBJS block with this object.
1890                  */
1891                 if (cnt != 0 && block_size + siz > 64000L)
1892                     break;
1893             }
1894 
1895             /*
1896              *   if this is the first object in this block, write the
1897              *   block header - the dictionary uses large object headers,
1898              *   so note that
1899              */
1900             if (cnt == 0)
1901                 image_writer->begin_objs_block(meta_idx, large_objs, FALSE);
1902 
1903             /* calculate the offset of the next block */
1904             next_ofs = ofs + siz;
1905 
1906             /* write the object data */
1907             for (rem_len = siz ; rem_len != 0 ; )
1908             {
1909                 const char *p;
1910                 ulong avail_len;
1911 
1912                 /* get the next block */
1913                 p = os->get_block_ptr(ofs, rem_len, &avail_len);
1914 
1915                 /* write it out */
1916                 image_writer->write_objs_bytes(p, avail_len);
1917 
1918                 /* move past this block */
1919                 ofs += avail_len;
1920                 rem_len -= avail_len;
1921             }
1922 
1923             /* count the object */
1924             ++cnt;
1925 
1926             /* move on to the next block */
1927             ofs = next_ofs;
1928         }
1929 
1930         /* if we wrote any objects, end the block */
1931         if (cnt != 0)
1932             image_writer->end_objs_block(cnt);
1933 
1934         /* move on to the next block */
1935         start_ofs = ofs;
1936     }
1937 }
1938 
1939 
1940 /* ------------------------------------------------------------------------ */
1941 /*
1942  *   Property comparison callback function for qsort() when invoked from
1943  *   sort_object_prop_table()
1944  */
prop_compare(const void * p1,const void * p2)1945 static int prop_compare(const void *p1, const void *p2)
1946 {
1947     uint id1, id2;
1948 
1949     /* get the ID's */
1950     id1 = osrp2(p1);
1951     id2 = osrp2(p2);
1952 
1953     /* compare them and return the result */
1954     return (id1 < id2 ? -1 : id1 == id2 ? 0 : 1);
1955 }
1956 
1957 /*
1958  *   Sort an object's property table.  This puts the property table into
1959  *   order of ascending property ID, and deletes any unused properties from
1960  *   the table.
1961  *
1962  *   Note that we do NOT update the stream to indicate the reduced number of
1963  *   properties if we delete any properties.  Instead, we simply return the
1964  *   new number of properties.
1965  */
sort_object_prop_table(CTcDataStream * os,ulong start_ofs)1966 size_t CTcGenTarg::sort_object_prop_table(CTcDataStream *os, ulong start_ofs)
1967 {
1968     uint prop_table_size;
1969     ulong orig_prop_cnt;
1970     uint prop_cnt;
1971     ulong prop_ofs;
1972     size_t src, dst;
1973 
1974     /* read the number of properties from the header */
1975     prop_cnt = CTPNStmObject::get_stream_prop_cnt(os, start_ofs);
1976 
1977     /* remember the original property count, in case we delete unused slots */
1978     orig_prop_cnt = prop_cnt;
1979 
1980     /* calculate the property table size */
1981     prop_table_size = prop_cnt * TCT3_TADSOBJ_PROP_SIZE;
1982 
1983     /* get the offset of the first property */
1984     prop_ofs = CTPNStmObject::get_stream_first_prop_ofs(os, start_ofs);
1985 
1986     /* reallocate the sort buffer if necessary */
1987     if (prop_table_size > sort_buf_size_)
1988     {
1989         /* increase the sort buffer size to the next 4k increment */
1990         sort_buf_size_ = (prop_table_size + 4095) & ~4096;
1991 
1992         /* reallocate the buffer */
1993         sort_buf_ = (char *)t3realloc(sort_buf_, sort_buf_size_);
1994         if (sort_buf_ == 0 || sort_buf_size_ < prop_table_size)
1995             G_tok->throw_internal_error(TCERR_CODEGEN_NO_MEM);
1996     }
1997 
1998     /* extract the table into our buffer */
1999     os->copy_to_buf(sort_buf_, prop_ofs, prop_table_size);
2000 
2001     /*
2002      *   Compress the table by removing any properties that have been
2003      *   marked as deleted -- if we had any 'modify + replace' properties
2004      *   that we resolved at link time, we will have marked those
2005      *   properties for deletion by setting their property ID's to zero in
2006      *   the table.  Scan the table for any such properties and remove
2007      *   them now.
2008      */
2009     for (src = dst = 0, prop_cnt = 0 ; src < prop_table_size ;
2010          src += TCT3_TADSOBJ_PROP_SIZE)
2011     {
2012         /* if this property isn't marked for deletion, keep it */
2013         if (osrp2(sort_buf_ + src) != VM_INVALID_PROP)
2014         {
2015             /*
2016              *   we're keeping it - if we can move it to a lower table
2017              *   position, copy the data to the new position, otherwise
2018              *   leave it alone
2019              */
2020             if (src != dst)
2021                 memcpy(sort_buf_ + dst, sort_buf_ + src,
2022                        TCT3_TADSOBJ_PROP_SIZE);
2023 
2024             /*
2025              *   advance the destination pointer past this slot, since
2026              *   we're going to keep the data in the slot
2027              */
2028             dst += TCT3_TADSOBJ_PROP_SIZE;
2029 
2030             /* count this property, since we're keeping it */
2031             ++prop_cnt;
2032         }
2033     }
2034 
2035     /* sort the table */
2036     qsort(sort_buf_, prop_cnt, TCT3_TADSOBJ_PROP_SIZE, &prop_compare);
2037 
2038     /* add back any unused slots after all of the sorted slots */
2039     for ( ; dst < prop_table_size ; dst += TCT3_TADSOBJ_PROP_SIZE)
2040         oswp2(sort_buf_ + dst, VM_INVALID_PROP);
2041 
2042     /* put the sorted table back in the buffer */
2043     os->write_at(prop_ofs, sort_buf_, prop_table_size);
2044 
2045     /* return the (possibly reduced) number of properties */
2046     return prop_cnt;
2047 }
2048 
2049 
2050 /*
2051  *   callback context for enumerating a dictionary
2052  */
2053 struct enum_dict_ctx
2054 {
2055     /* number of entries written so far */
2056     uint cnt;
2057 };
2058 
2059 /*
2060  *   Generate code for a dictionary object
2061  */
gen_code_for_dict(CTcDictEntry * dict)2062 void CTcGenTarg::gen_code_for_dict(CTcDictEntry *dict)
2063 {
2064     long size_ofs;
2065     long entry_cnt_ofs;
2066     long end_ofs;
2067     enum_dict_ctx ctx;
2068 
2069     /*
2070      *   Write the OBJS header - object ID plus byte count for
2071      *   metaclass-specific data (use a placeholder length for now)
2072      */
2073     G_dict_stream->write4(dict->get_sym()->get_obj_id());
2074     size_ofs = G_dict_stream->get_ofs();
2075     G_dict_stream->write4(0);
2076 
2077     /*
2078      *   Write the metaclass-specific data for the 'dictionary' metaclass
2079      */
2080 
2081     /* write a nil comparator object initially */
2082     G_dict_stream->write4(0);
2083 
2084     /* write a placeholder for the entry count */
2085     entry_cnt_ofs = G_dict_stream->get_ofs();
2086     G_dict_stream->write2(0);
2087 
2088     /* write the dictionary entries */
2089     ctx.cnt = 0;
2090     dict->get_hash_table()->enum_entries(&enum_dict_gen_cb, &ctx);
2091 
2092     /* remember the ending offset of the table */
2093     end_ofs = G_dict_stream->get_ofs();
2094 
2095     /* go back and fix up the total size of the object data */
2096     G_dict_stream->write4_at(size_ofs, end_ofs - size_ofs - 4);
2097 
2098     /* fix up the dictionary entry count */
2099     G_dict_stream->write2_at(entry_cnt_ofs, ctx.cnt);
2100 }
2101 
2102 /*
2103  *   Callback - enumerate dictionary entries for code generation
2104  */
enum_dict_gen_cb(void * ctx0,CVmHashEntry * entry0)2105 void CTcGenTarg::enum_dict_gen_cb(void *ctx0, CVmHashEntry *entry0)
2106 {
2107     enum_dict_ctx *ctx = (enum_dict_ctx *)ctx0;
2108     CVmHashEntryPrsDict *entry = (CVmHashEntryPrsDict *)entry0;
2109     char buf[255];
2110     size_t len;
2111     char *p;
2112     size_t rem;
2113     uint cnt;
2114     CTcPrsDictItem *item;
2115 
2116     /* count this entry */
2117     ++(ctx->cnt);
2118 
2119     /* limit the key length to 255 bytes */
2120     len = entry->getlen();
2121     if (len > 255)
2122         len = 255;
2123 
2124     /* copy the entry to our buffer */
2125     memcpy(buf, entry->getstr(), len);
2126 
2127     /* apply the XOR obfuscation to the key text */
2128     for (p = buf, rem = len ; rem != 0 ; ++p, --rem)
2129         *p ^= 0xBD;
2130 
2131     /* write the length of the key followed by the key string */
2132     G_dict_stream->write((uchar)len);
2133     G_dict_stream->write(buf, len);
2134 
2135     /* count the items in this entry */
2136     for (cnt = 0, item = entry->get_list() ; item != 0 ;
2137          ++cnt, item = item->nxt_) ;
2138 
2139     /* write the number of entries */
2140     G_dict_stream->write2(cnt);
2141 
2142     /* write the entries */
2143     for (item = entry->get_list() ; item != 0 ; item = item->nxt_)
2144     {
2145         /* write the object ID and property ID of this entry */
2146         G_dict_stream->write4(item->obj_);
2147         G_dict_stream->write2(item->prop_);
2148     }
2149 }
2150 
2151 /*
2152  *   Generate code for a grammar production
2153  */
gen_code_for_gramprod(CTcGramProdEntry * prod)2154 void CTcGenTarg::gen_code_for_gramprod(CTcGramProdEntry *prod)
2155 {
2156     long size_ofs;
2157     long end_ofs;
2158     uint cnt;
2159     CTcGramProdAlt *alt;
2160     CTcDataStream *str = G_gramprod_stream;
2161 
2162     /*
2163      *   write the OBJS header - object ID plus byte count for
2164      *   metaclass-specific data (use a placeholder length for now)
2165      */
2166     str->write4(prod->get_prod_sym()->get_obj_id());
2167     size_ofs = str->get_ofs();
2168     str->write4(0);
2169 
2170     /*
2171      *   Write the metaclass-specific data for the 'grammar-production'
2172      *   metaclass
2173      */
2174 
2175     /* count the alternatives */
2176     for (cnt = 0, alt = prod->get_alt_head() ; alt != 0 ;
2177          ++cnt, alt = alt->get_next()) ;
2178 
2179     /*
2180      *   If this production has no alternatives and was not explicitly
2181      *   declared, flag an error indicating that the production is
2182      *   undeclared.  We treat this as an error because there's a good chance
2183      *   that the an alternative referring to the production misspelled the
2184      *   name.  If the production was explicitly declared, then we have
2185      *   sufficient confirmation that the name is correct, so no error is
2186      *   indicated.
2187      */
2188     if (cnt == 0 && !prod->is_declared())
2189         G_tcmain->log_error(0, 0, TC_SEV_ERROR,
2190                             TCERR_GRAMPROD_HAS_NO_ALTS,
2191                             (int)prod->get_prod_sym()->get_sym_len(),
2192                             prod->get_prod_sym()->get_sym());
2193 
2194     /*
2195      *   The count has to fit in 16 bits; it's surprisingly easy to exceed
2196      *   this by using the power of permutation (with nested '|'
2197      *   alternatives), so check for overflow and flag an error.  Even though
2198      *   it's not hard to exceed the limit, it's not desirable to create so
2199      *   many permutations, so the limit isn't really in need of being
2200      *   raised; it's better to rewrite a rule with a huge number of
2201      *   permutations using sub-productions.
2202      */
2203     if (cnt > 65535)
2204         G_tcmain->log_error(0, 0, TC_SEV_ERROR,
2205                             TCERR_GRAMPROD_TOO_MANY_ALTS,
2206                             (int)prod->get_prod_sym()->get_sym_len(),
2207                             prod->get_prod_sym()->get_sym());
2208 
2209     /* write the number of alternatives */
2210     str->write2(cnt);
2211 
2212     /* write the alternatives */
2213     for (alt = prod->get_alt_head() ; alt != 0 ; alt = alt->get_next())
2214     {
2215         CTcGramProdTok *tok;
2216 
2217         /* write the score and badness for the alternative */
2218         str->write2(alt->get_score());
2219         str->write2(alt->get_badness());
2220 
2221         /* write the processor object ID for this alternative */
2222         str->write4(alt->get_processor_obj()->get_obj_id());
2223 
2224         /* count the tokens in this alternative */
2225         for (cnt = 0, tok = alt->get_tok_head() ; tok != 0 ;
2226              ++cnt, tok = tok->get_next()) ;
2227 
2228         /* write the token count */
2229         str->write2(cnt);
2230 
2231         /* write the tokens */
2232         for (tok = alt->get_tok_head() ; tok != 0 ; tok = tok->get_next())
2233         {
2234             size_t idx;
2235 
2236             /* write the property association */
2237             str->write2((uint)tok->get_prop_assoc());
2238 
2239             /* write the token data */
2240             switch(tok->get_type())
2241             {
2242             case TCGRAM_PROD:
2243                 /* write the type */
2244                 str->write((uchar)VMGRAM_MATCH_PROD);
2245 
2246                 /* write the sub-production object ID */
2247                 str->write4((ulong)tok->getval_prod()->get_obj_id());
2248                 break;
2249 
2250             case TCGRAM_PART_OF_SPEECH:
2251                 /* write the type */
2252                 str->write((uchar)VMGRAM_MATCH_SPEECH);
2253 
2254                 /* write the part-of-speech property */
2255                 str->write2((uint)tok->getval_part_of_speech());
2256                 break;
2257 
2258             case TCGRAM_PART_OF_SPEECH_LIST:
2259                 /* write the type */
2260                 str->write((uchar)VMGRAM_MATCH_NSPEECH);
2261 
2262                 /* write the number of elements in the property list */
2263                 str->write2((uint)tok->getval_part_list_len());
2264 
2265                 /* write each element */
2266                 for (idx = 0 ; idx < tok->getval_part_list_len() ; ++idx)
2267                     str->write2((uint)tok->getval_part_list_ele(idx));
2268 
2269                 /* done */
2270                 break;
2271 
2272             case TCGRAM_LITERAL:
2273                 /* write the type */
2274                 str->write((uchar)VMGRAM_MATCH_LITERAL);
2275 
2276                 /* write the string length prefix */
2277                 str->write2(tok->getval_literal_len());
2278 
2279                 /* write the string text */
2280                 str->write(tok->getval_literal_txt(),
2281                            tok->getval_literal_len());
2282 
2283                 /*
2284                  *   add the word to the dictionary that was active when the
2285                  *   alternative was defined
2286                  */
2287                 if (alt->get_dict() != 0)
2288                 {
2289                     /*
2290                      *   there's a dictionary - add the word, associating it
2291                      *   with the production object and with the parser's
2292                      *   miscVocab property
2293                      */
2294                     alt->get_dict()->add_word(
2295                         tok->getval_literal_txt(), tok->getval_literal_len(),
2296                         FALSE, prod->get_prod_sym()->get_obj_id(),
2297                         G_prs->get_miscvocab_prop());
2298                 }
2299                 break;
2300 
2301             case TCGRAM_TOKEN_TYPE:
2302                 /* write the type */
2303                 str->write((uchar)VMGRAM_MATCH_TOKTYPE);
2304 
2305                 /* write the enum ID of the token */
2306                 str->write4(tok->getval_token_type());
2307                 break;
2308 
2309             case TCGRAM_STAR:
2310                 /* write the type - there's no additional data */
2311                 str->write((uchar)VMGRAM_MATCH_STAR);
2312                 break;
2313 
2314             default:
2315                 assert(FALSE);
2316                 break;
2317             }
2318         }
2319     }
2320 
2321     /* remember the ending offset of the object data */
2322     end_ofs = str->get_ofs();
2323 
2324     /* go back and fix up the total size of the object data */
2325     str->write4_at(size_ofs, end_ofs - size_ofs - 4);
2326 }
2327 
2328 
2329 /* ------------------------------------------------------------------------ */
2330 /*
2331  *   Data Stream Layout Manager
2332  */
2333 
2334 /*
2335  *   calculate the size of the pool pages, given the size of the largest
2336  *   single item
2337  */
calc_layout(CTcDataStream * ds,ulong max_len,int is_first)2338 void CTcStreamLayout::calc_layout(CTcDataStream *ds, ulong max_len,
2339                                   int is_first)
2340 {
2341     ulong rem;
2342     ulong free_ofs;
2343     CTcStreamAnchor *anchor;
2344 
2345     /* if this is the first page, handle some things specially */
2346     if (is_first)
2347     {
2348         ulong pgsiz;
2349 
2350         /*
2351          *   Starting at 2k, look for a page size that will fit the
2352          *   desired minimum size.
2353          */
2354         for (pgsiz = 2048 ; pgsiz < max_len ; pgsiz <<= 1) ;
2355 
2356         /* remember our selected page size */
2357         page_size_ = pgsiz;
2358 
2359         /* start at the bottom of the first page */
2360         rem = pgsiz;
2361         free_ofs = 0;
2362         page_cnt_ = 1;
2363     }
2364     else
2365     {
2366         /*
2367          *   this isn't the first page - if there are no anchors, don't
2368          *   bother adding anything
2369          */
2370         if (ds->get_first_anchor() == 0)
2371             return;
2372 
2373         /*
2374          *   start at the end of the last existing page - this will ensure
2375          *   that everything added from the new stream will go onto a
2376          *   brand new page after everything from the previous stream
2377          */
2378         rem = 0;
2379         free_ofs = page_size_ * page_cnt_;
2380     }
2381 
2382     /*
2383      *   Run through the list of stream anchors and calculate the layout.
2384      *   For each item, assign its final pool address and apply its
2385      *   fixups.
2386      */
2387     for (anchor = ds->get_first_anchor() ; anchor != 0 ;
2388          anchor = anchor->nxt_)
2389     {
2390         ulong len;
2391 
2392         /*
2393          *   if this anchor has been marked as replaced, don't include it
2394          *   in our calculations, because we don't want to include this
2395          *   block in the image file
2396          */
2397         if (anchor->is_replaced())
2398             continue;
2399 
2400         /*
2401          *   if this item fits on the current page, assign it the next
2402          *   sequential address; otherwise, go to the next page
2403          *
2404          *   if this anchor is at the dividing point, put it on the next
2405          *   page, unless we just started a new page
2406          */
2407         len = anchor->get_len(ds);
2408         if (len > rem)
2409         {
2410             /*
2411              *   we must start the next page - skip to the next page by
2412              *   moving past the remaining free space on this page
2413              */
2414             free_ofs += rem;
2415 
2416             /* count the new page */
2417             ++page_cnt_;
2418 
2419             /* the whole next page is available to us now */
2420             rem = page_size_;
2421         }
2422 
2423         /*
2424          *   set the anchor's final address, which will apply fixups for
2425          *   the object's fixup list
2426          */
2427         anchor->set_addr(free_ofs);
2428 
2429         /* advance past this block */
2430         free_ofs += len;
2431         rem -= len;
2432     }
2433 
2434     /* if there's no data at all, we have zero pages */
2435     if (free_ofs == 0)
2436         page_cnt_ = 0;
2437 }
2438 
2439 
2440 /*
2441  *   Write our stream to an image file
2442  */
write_to_image(CTcDataStream ** ds_arr,size_t ds_cnt,CVmImageWriter * image_writer,int pool_id,uchar xor_mask)2443 void CTcStreamLayout::write_to_image(CTcDataStream **ds_arr, size_t ds_cnt,
2444                                      CVmImageWriter *image_writer,
2445                                      int pool_id, uchar xor_mask)
2446 {
2447     CTcStreamAnchor *anchor;
2448     ulong free_ofs;
2449     ulong next_page_start;
2450     int pgnum;
2451 
2452     /* write the constant pool definition block - the pool's ID is 2 */
2453     image_writer->write_pool_def(pool_id, page_cnt_, page_size_, TRUE);
2454 
2455     /*
2456      *   start out before the first page - the next page starts with the
2457      *   item at offset zero
2458      */
2459     pgnum = 0;
2460     next_page_start = 0;
2461 
2462     /* run through each stream */
2463     for ( ; ds_cnt != 0 ; ++ds_arr, --ds_cnt)
2464     {
2465         CTcDataStream *ds;
2466 
2467         /* get the current stream */
2468         ds = *ds_arr;
2469 
2470         /* run through the anchor list for this stream */
2471         for (anchor = ds->get_first_anchor() ; anchor != 0 ;
2472              anchor = anchor->nxt_)
2473         {
2474             ulong len;
2475             ulong stream_ofs;
2476             ulong addr;
2477 
2478             /*
2479              *   if this anchor is marked as replaced, skip it entirely -
2480              *   we omit replaced blocks from the image file, because
2481              *   they're completely unreachable
2482              */
2483             if (anchor->is_replaced())
2484                 continue;
2485 
2486             /*
2487              *   if this item's assigned address is on the next page, move
2488              *   to the next page
2489              */
2490             len = anchor->get_len(ds);
2491             addr = anchor->get_addr();
2492             if (addr == next_page_start)
2493             {
2494                 /* if this isn't the first page, close the previous page */
2495                 if (pgnum != 0)
2496                     image_writer->end_pool_page();
2497 
2498                 /* start the new page */
2499                 image_writer->begin_pool_page(pool_id, pgnum, TRUE, xor_mask);
2500 
2501                 /* this item is at the start of the new page */
2502                 free_ofs = next_page_start;
2503 
2504                 /* count the new page */
2505                 ++pgnum;
2506 
2507                 /* calculate the address of the start of the next page */
2508                 next_page_start += page_size_;
2509             }
2510 
2511             /* advance past this block */
2512             free_ofs += len;
2513 
2514             /*
2515              *   write the data from the stream to the image file - we
2516              *   must iterate over the chunks the code stream returns,
2517              *   since it might not be able to return the entire block in
2518              *   a single operation
2519              */
2520             for (stream_ofs = anchor->get_ofs() ; len != 0 ; )
2521             {
2522                 ulong cur;
2523                 const char *ptr;
2524 
2525                 /* get the pointer to this chunk */
2526                 ptr = ds->get_block_ptr(stream_ofs, len, &cur);
2527 
2528                 /* write this chunk */
2529                 image_writer->write_pool_page_bytes(ptr, cur, xor_mask);
2530 
2531                 /* advance our pointers past this chunk */
2532                 len -= cur;
2533                 stream_ofs += cur;
2534             }
2535         }
2536     }
2537 
2538     /* if we started a page, end it */
2539     if (pgnum != 0)
2540         image_writer->end_pool_page();
2541 }
2542 
2543 /* ------------------------------------------------------------------------ */
2544 /*
2545  *   Object Symbol subclass - image-file functions
2546  */
2547 
2548 /*
2549  *   mark the compiled data for the object as a 'class' object
2550  */
mark_compiled_as_class()2551 void CTcSymObj::mark_compiled_as_class()
2552 {
2553     uint flags;
2554     CTcDataStream *str;
2555 
2556     /* get the appropriate stream for generating the data */
2557     str = get_stream();
2558 
2559     /* get my original object flags */
2560     flags = CTPNStmObject::get_stream_obj_flags(str, stream_ofs_);
2561 
2562     /* add in the 'class' flag */
2563     flags |= TCT3_OBJFLG_CLASS;
2564 
2565     /* set the updated flags */
2566     CTPNStmObject::set_stream_obj_flags(str, stream_ofs_, flags);
2567 }
2568 
2569 /*
2570  *   Delete a property from our modified base classes
2571  */
delete_prop_from_mod_base(tctarg_prop_id_t prop_id)2572 void CTcSymObj::delete_prop_from_mod_base(tctarg_prop_id_t prop_id)
2573 {
2574     uint prop_cnt;
2575     uint i;
2576     CTcDataStream *str;
2577 
2578     /* get the correct data stream */
2579     str = get_stream();
2580 
2581     /* get the number of properties in the object */
2582     prop_cnt = CTPNStmObject::get_stream_prop_cnt(str, stream_ofs_);
2583 
2584     /* find the property in our property table */
2585     for (i = 0 ; i < prop_cnt ; ++i)
2586     {
2587         /* if this property ID matches, delete it */
2588         if (CTPNStmObject::get_stream_prop_id(str, stream_ofs_, i)
2589             == prop_id)
2590         {
2591             /* delete the object by setting its ID to 'invalid' */
2592             CTPNStmObject::set_stream_prop_id(str, stream_ofs_, i,
2593                                               VM_INVALID_PROP);
2594 
2595             /*
2596              *   there's no need to look any further - a property can
2597              *   occur only once in an object
2598              */
2599             break;
2600         }
2601     }
2602 }
2603 
2604 /*
2605  *   Build the dictionary
2606  */
build_dictionary()2607 void CTcSymObj::build_dictionary()
2608 {
2609     uint prop_cnt;
2610     uint i;
2611 
2612     /*
2613      *   Inherit the default handling - this will explicitly add all
2614      *   superclass dictionary data into my own internal dictionary list,
2615      *   so that we don't have to worry at all about superclasses here.
2616      *   This will also add our words to my associated dictionary object.
2617      */
2618     CTcSymObjBase::build_dictionary();
2619 
2620     /* if I'm not a regular tads object, there's nothing to do here */
2621     if (metaclass_ != TC_META_TADSOBJ)
2622         return;
2623 
2624     /*
2625      *   Examine my properties.  Each time we find a property whose value
2626      *   is set to vocab-list, replace it with an actual list of strings
2627      *   for my vocabulary words associated with the property.
2628      */
2629 
2630     /* get the number of properties in the object */
2631     prop_cnt = CTPNStmObject::get_stream_prop_cnt(G_os, stream_ofs_);
2632 
2633     /* find the property in our property table */
2634     for (i = 0 ; i < prop_cnt ; ++i)
2635     {
2636         CTcConstVal val;
2637         vm_datatype_t prop_type;
2638 
2639         /* get this property value */
2640         prop_type = CTPNStmObject::get_stream_prop_type(G_os, stream_ofs_, i);
2641 
2642         /*
2643          *   if it's a vocabulary list placeholder, replace it with the
2644          *   actual list of vocabulary strings
2645          */
2646         if (prop_type == VM_VOCAB_LIST)
2647         {
2648             vm_prop_id_t prop_id;
2649             CTcVocabEntry *entry;
2650             CTPNList *lst;
2651             ulong prop_val_ofs;
2652 
2653             /* get the property ID */
2654             prop_id = CTPNStmObject::get_stream_prop_id(G_os, stream_ofs_, i);
2655 
2656             /* get the value offset of this property */
2657             prop_val_ofs = CTPNStmObject::
2658                            get_stream_prop_val_ofs(G_os, stream_ofs_, i);
2659 
2660             /* create a list */
2661             lst = new CTPNList();
2662 
2663             /*
2664              *   scan my internal vocabulary list and add the entries
2665              *   associated with this property
2666              */
2667             for (entry = vocab_ ; entry != 0 ; entry = entry->nxt_)
2668             {
2669                 /* if this one matches our property, add it */
2670                 if (entry->prop_ == prop_id)
2671                 {
2672                     CTcConstVal str_val;
2673                     CTcPrsNode *ele;
2674 
2675                     /* create a string element */
2676                     str_val.set_sstr(entry->txt_, entry->len_);
2677                     ele = new CTPNConst(&str_val);
2678 
2679                     /* add it to the list */
2680                     lst->add_element(ele);
2681                 }
2682             }
2683 
2684             /*
2685              *   Overwrite the original property value with the new list.
2686              *   If the list is empty, this object doesn't define or
2687              *   inherit any vocabulary of this property at all, so we can
2688              *   clear the property entirely.
2689              */
2690             if (lst->get_count() == 0)
2691             {
2692                 /*
2693                  *   delete the property from the object by setting its
2694                  *   property ID to 'invalid'
2695                  */
2696                 CTPNStmObject::
2697                     set_stream_prop_id(G_os, stream_ofs_, i, VM_INVALID_PROP);
2698             }
2699             else
2700             {
2701                 /* write the list value to the property */
2702                 val.set_list(lst);
2703                 G_cg->write_const_as_dh(G_os, prop_val_ofs, &val);
2704             }
2705         }
2706     }
2707 }
2708 
2709 
2710 /* ------------------------------------------------------------------------ */
2711 /*
2712  *   Symbol table entry routines for writing a symbol to the global symbol
2713  *   table in the debug records in the image file
2714  */
2715 
2716 /*
2717  *   write the symbol to an image file's global symbol table
2718  */
write_to_image_file_global(class CVmImageWriter * image_writer)2719 int CTcSymFunc::write_to_image_file_global(class CVmImageWriter *image_writer)
2720 {
2721     char buf[128];
2722 
2723     /* build our extra data buffer */
2724     oswp4(buf, get_code_pool_addr());
2725     oswp2(buf + 4, get_argc());
2726     buf[6] = (is_varargs() != 0);
2727     buf[7] = (has_retval() != 0);
2728 
2729     /* write the data */
2730     image_writer->write_gsym_entry(get_sym(), get_sym_len(),
2731                                    (int)TC_SYM_FUNC, buf, 8);
2732 
2733     /* we wrote the symbol */
2734     return TRUE;
2735 }
2736 
2737 /*
2738  *   write the symbol to an image file's global symbol table
2739  */
write_to_image_file_global(class CVmImageWriter * image_writer)2740 int CTcSymObj::write_to_image_file_global(class CVmImageWriter *image_writer)
2741 {
2742     char buf[128];
2743 
2744     /* store our object ID in the extra data buffer */
2745     oswp4(buf, obj_id_);
2746 
2747     /* add our modifying object ID, if we have a modifiying object */
2748     if (get_modifying_sym() != 0)
2749         oswp4(buf + 4, get_modifying_sym()->get_obj_id());
2750     else
2751         oswp4(buf + 4, 0);
2752 
2753     /* write the data */
2754     image_writer->write_gsym_entry(get_sym(), get_sym_len(),
2755                                    (int)TC_SYM_OBJ, buf, 8);
2756 
2757     /* we wrote the symbol */
2758     return TRUE;
2759 }
2760 
2761 /*
2762  *   write the symbol to an image file's global symbol table
2763  */
write_to_image_file_global(class CVmImageWriter * image_writer)2764 int CTcSymProp::write_to_image_file_global(class CVmImageWriter *image_writer)
2765 {
2766     char buf[128];
2767 
2768     /* build our extra data buffer */
2769     oswp2(buf, (uint)get_prop());
2770 
2771     /* write the data */
2772     image_writer->write_gsym_entry(get_sym(), get_sym_len(),
2773                                    (int)TC_SYM_PROP, buf, 2);
2774 
2775     /* we wrote the symbol */
2776     return TRUE;
2777 }
2778 
2779 /*
2780  *   write the symbol to an image file's global symbol table
2781  */
write_to_image_file_global(class CVmImageWriter * image_writer)2782 int CTcSymEnum::write_to_image_file_global(class CVmImageWriter *image_writer)
2783 {
2784     char buf[128];
2785 
2786     /* build our extra data buffer */
2787     oswp4(buf, get_enum_id());
2788 
2789     /* build our flags */
2790     buf[4] = 0;
2791     if (is_token_)
2792         buf[4] |= 1;
2793 
2794     /* write the data */
2795     image_writer->write_gsym_entry(get_sym(), get_sym_len(),
2796                                    (int)TC_SYM_ENUM, buf, 5);
2797 
2798     /* we wrote the symbol */
2799     return TRUE;
2800 }
2801 
2802 /*
2803  *   write the symbol to an image file's global symbol table
2804  */
2805 int CTcSymMetaclass::
write_to_image_file_global(class CVmImageWriter * image_writer)2806    write_to_image_file_global(class CVmImageWriter *image_writer)
2807 {
2808     char buf[128];
2809 
2810     /* build our extra data buffer */
2811     oswp2(buf, meta_idx_);
2812     oswp4(buf + 2, class_obj_);
2813 
2814     /* write the data */
2815     image_writer->write_gsym_entry(get_sym(), get_sym_len(),
2816                                    (int)TC_SYM_METACLASS, buf, 6);
2817 
2818     /* we wrote the symbol */
2819     return TRUE;
2820 }
2821 
2822 /*
2823  *   Fix up the inheritance chain in the modifier objects
2824  */
fix_mod_obj_sc_list()2825 void CTcSymMetaclass::fix_mod_obj_sc_list()
2826 {
2827     CTcSymObj *obj;
2828     CTcSymObj *obj_base;
2829 
2830     /*
2831      *   go through our chain of modifier objects, and make sure the
2832      *   stream data for each object points to its correct superclass
2833      */
2834     for (obj = mod_obj_ ; obj != 0 ; obj = obj_base)
2835     {
2836         CTcDataStream *str;
2837 
2838         /* get the correct data stream */
2839         str = obj->get_stream();
2840 
2841         /* get the base object for this symbol */
2842         obj_base = obj->get_mod_base_sym();
2843 
2844         /*
2845          *   if there's no base object, there's no superclass entry to
2846          *   adjust for this object
2847          */
2848         if (obj_base == 0)
2849             break;
2850 
2851         /*
2852          *   set the superclass in this object to point to this base
2853          *   object
2854          */
2855         CTPNStmObject::set_stream_sc(str, obj->get_stream_ofs(),
2856                                      0, obj_base->get_obj_id());
2857     }
2858 }
2859 
2860 /*
2861  *   write the symbol to an image file's global symbol table
2862  */
write_to_image_file_global(class CVmImageWriter * image_writer)2863 int CTcSymBif::write_to_image_file_global(class CVmImageWriter *image_writer)
2864 {
2865     char buf[128];
2866 
2867     /* build our extra data buffer */
2868     oswp2(buf, get_func_idx());
2869     oswp2(buf + 2, get_func_set_id());
2870     buf[4] = (has_retval() != 0);
2871     oswp2(buf + 5, get_min_argc());
2872     oswp2(buf + 7, get_max_argc());
2873     buf[9] = (is_varargs() != 0);
2874 
2875     /* write the data */
2876     image_writer->write_gsym_entry(get_sym(), get_sym_len(),
2877                                    (int)TC_SYM_BIF, buf, 10);
2878 
2879     /* we wrote the symbol */
2880     return TRUE;
2881 }
2882 
2883 /*
2884  *   write the symbol to an image file's global symbol table
2885  */
write_to_image_file_global(class CVmImageWriter * iw)2886 int CTcSymExtfn::write_to_image_file_global(class CVmImageWriter *iw)
2887 {
2888     //$$$ to be implemented
2889     assert(FALSE);
2890     return FALSE;
2891 }
2892 
2893