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