3
"$Header: d:/cvsroot/tads/tads3/TCT3IMG.CPP,v 1.1 1999/07/11 00:46:57 MJRoberts Exp $";
7
* Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved.
9
* Please see the accompanying license file, LICENSE.TXT, for information
10
* on using and copying this software.
14
tct3.cpp - TADS 3 Compiler - T3 VM Code Generator - image writing functions
16
Image writing routines for the T3-specific code generator
20
05/08/99 MJRoberts - Creation
41
/* ------------------------------------------------------------------------ */
43
* Object file signature. The numerical suffix in the first part is the
44
* format version number: whenever we make an incompatible change to the
45
* format, we'll increment this number so that the linker will recognize an
46
* incompatible file format and require a full rebuild.
48
static const char obj_file_sig[] = "TADS3.Object.000E\n\r\032";
51
/* ------------------------------------------------------------------------ */
53
* Write an object file. The object file contains the raw byte streams
54
* with the generated code; the fixup lists for the streams; the global
55
* symbol table; and the function and metaclass dependency lists.
57
void CTcGenTarg::write_to_object_file(CVmFile *fp, CTcMake *)
61
/* write the signature */
62
fp->write_bytes(obj_file_sig, sizeof(obj_file_sig) - 1);
64
/* compute the object file flags */
67
flags |= TCT3_OBJHDR_DEBUG;
70
fp->write_int4(flags);
72
/* write the constant and code pool indivisible object maxima */
73
fp->write_int4(max_str_len_);
74
fp->write_int4(max_list_cnt_);
75
fp->write_int4(max_bytecode_len_);
78
* Write the maximum object and property ID's. When we load this
79
* object file, we'll need to generate a translated ID number for
80
* each object ID and for each property ID, to translate from the
81
* numbering system in the object file to the final image file
82
* numbering system. It is helpful if we know early on how many of
83
* each there are, so that we can allocate table space accordingly.
85
fp->write_int4(next_obj_);
86
fp->write_int4(next_prop_);
87
fp->write_int4(G_prs->get_enum_count());
89
/* write the function set dependency table */
90
write_funcdep_to_object_file(fp);
93
* write the metaclass dependency table - note that we must do this
94
* before writing the global symbol table, because upon loading the
95
* object file, we must have the dependency table loaded before we
96
* can load the symbols (so that any metaclass symbols can be
97
* resolved to their dependency table indices)
99
write_metadep_to_object_file(fp);
101
/* write the global symbol table */
102
G_prs->write_to_object_file(fp);
104
/* write the main code stream and its fixup list */
105
G_cs_main->write_to_object_file(fp);
107
/* write the static code stream and its fixup list */
108
G_cs_static->write_to_object_file(fp);
110
/* write the data stream and its fixup list */
111
G_ds->write_to_object_file(fp);
113
/* write the object stream and its fixup list */
114
G_os->write_to_object_file(fp);
116
/* write the intrinsic class modifier stream */
117
G_icmod_stream->write_to_object_file(fp);
119
/* write the BigNumber stream and its fixup list */
120
G_bignum_stream->write_to_object_file(fp);
122
/* write the static initializer ID stream */
123
G_static_init_id_stream->write_to_object_file(fp);
125
/* write the object ID fixup list */
126
CTcIdFixup::write_to_object_file(fp, G_objfixup);
128
/* write the property ID fixup list */
129
CTcIdFixup::write_to_object_file(fp, G_propfixup);
131
/* write the enumerator ID fixup list */
132
CTcIdFixup::write_to_object_file(fp, G_enumfixup);
134
/* write debugging information if we're compiling for the debugger */
137
tct3_debug_line_page *pg;
139
/* write the source file list */
140
write_sources_to_object_file(fp);
143
* Write the pointers to the debug line records in the code
144
* stream, so that we can fix up the line records on re-loading
145
* the object file (they'll need to be adjusted for the new
146
* numbering system for the source file descriptors). First,
147
* write the total number of pointers.
149
fp->write_int4(debug_line_cnt_);
151
/* now write the pointers, one page at a time */
152
for (pg = debug_line_head_ ; pg != 0 ; pg = pg->nxt)
157
* if this is the last entry, it might only be partially
158
* full; otherwise, it's completely full, because we always
159
* fill a page before allocating a new one
162
pgcnt = (size_t)(debug_line_cnt_ % TCT3_DEBUG_LINE_PAGE_SIZE);
164
pgcnt = TCT3_DEBUG_LINE_PAGE_SIZE;
167
* Write the data - we prepared it in portable format, so we
168
* can just copy it directly to the file. Note that each
169
* entry is four bytes.
171
fp->write_bytes((char *)pg->line_ofs,
172
pgcnt * TCT3_DEBUG_LINE_REC_SIZE);
175
/* write the #define symbols */
176
G_tok->write_macros_to_file_for_debug(fp);
180
/* ------------------------------------------------------------------------ */
182
* Write the function-set dependency table to an object file
184
void CTcGenTarg::write_funcdep_to_object_file(CVmFile *fp)
188
/* write the count */
189
fp->write_int2(fnset_cnt_);
191
/* write the entries */
192
for (cur = fnset_head_ ; cur != 0 ; cur = cur->nxt)
196
len = strlen(cur->nm);
198
fp->write_bytes(cur->nm, len);
203
* Write the metaclass dependency table to an object file
205
void CTcGenTarg::write_metadep_to_object_file(CVmFile *fp)
209
/* write the count */
210
fp->write_int2(meta_cnt_);
212
/* write the entries */
213
for (cur = meta_head_ ; cur != 0 ; cur = cur->nxt)
217
len = strlen(cur->nm);
219
fp->write_bytes(cur->nm, len);
224
/* ------------------------------------------------------------------------ */
226
* Load an object file. We'll read the file, load its data into memory
227
* (creating global symbol table entries and writing to the code and
228
* data streams), fix up the fixups to the new base offsets in the
229
* streams, and translate object and property ID values from the object
230
* file numbering system to our in-memory numbering system (which will
231
* usually differ after more than one object file is loaded, because the
232
* numbering systems in the different files must be reconciled).
234
* Returns zero on success; logs errors and returns non-zero on error.
236
int CTcGenTarg::load_object_file(CVmFile *fp, const textchar_t *fname)
242
vm_obj_id_t *obj_xlat = 0;
243
vm_prop_id_t *prop_xlat = 0;
244
ulong *enum_xlat = 0;
248
ulong main_cs_start_ofs;
249
ulong static_cs_start_ofs;
252
* Before loading anything from the file, go through all of the
253
* streams and set their object file base offset. All stream
254
* offsets that we read from the object file will be relative to the
255
* these values, since the object file stream data will be loaded in
256
* after any data currently in the streams.
258
G_cs_main->set_object_file_start_ofs();
259
G_cs_static->set_object_file_start_ofs();
260
G_ds->set_object_file_start_ofs();
261
G_os->set_object_file_start_ofs();
262
G_icmod_stream->set_object_file_start_ofs();
263
G_bignum_stream->set_object_file_start_ofs();
264
G_static_init_id_stream->set_object_file_start_ofs();
267
* note the main code stream's start offset, since we'll need this
268
* later in order to process the debug line records; likewise, note
269
* the static stream's start offset
271
main_cs_start_ofs = G_cs_main->get_ofs();
272
static_cs_start_ofs = G_cs_static->get_ofs();
274
/* read the signature, and make sure it matches */
275
fp->read_bytes(buf, sizeof(obj_file_sig) - 1);
276
if (memcmp(buf, obj_file_sig, sizeof(obj_file_sig) - 1) != 0)
278
G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_INV_SIG);
282
/* read the file header flags */
283
hdr_flags = fp->read_uint4();
286
* If we're linking with debugging information, but this object file
287
* wasn't compiled with debugging information, we won't be able to
288
* produce a complete debuggable image - log an error to that
291
if (G_debug && (hdr_flags & TCT3_OBJHDR_DEBUG) == 0)
292
G_tcmain->log_error(0, 0, TC_SEV_ERROR,
293
TCERR_OBJFILE_NO_DBG, fname);
296
* Read the constant and code pool indivisible object maxima. Note
297
* each maximum that exceeds the current maximum, since we must keep
298
* track of the largest indivisible object of each type in the
302
/* read and note the maximum string constant length */
303
siz = fp->read_uint4();
304
if (siz > max_str_len_)
307
/* read and note the maximum list size */
308
siz = fp->read_uint4();
309
if (siz > max_list_cnt_)
312
/* read and note the maximum code pool object size */
313
siz = fp->read_uint4();
314
if (siz > max_bytecode_len_)
315
max_bytecode_len_ = siz;
318
* read the object, property, and enumerator ID counts from the file
319
* - these give the highest ID values that are assigned anywhere in
320
* the object file's numbering system
322
obj_cnt = fp->read_uint4();
323
prop_cnt = fp->read_uint4();
324
enum_cnt = fp->read_uint4();
327
* Allocate object and property ID translation tables. These are
328
* simply arrays of ID's. Each element of an array gives the global
329
* ID number assigned to the object whose local ID is the array
330
* index. So, obj_xlat[local_id] = global_id. We need one element
331
* in the object ID translation array for each local ID in the
332
* object file, which is obj_cnt; likewise for properties and
335
* We're being a bit lazy here by using flat arrays. This could be
336
* a problem for very large object files on 16-bit machines: if a
337
* single object file has more than 16k object ID's (which means
338
* that it defines and imports more than 16k unique objects), or
339
* more than 32k property ID's, we'll go over the 64k allocation
340
* limit on these machines. This seems unlikely to become a problem
341
* in practice, but to ensure a graceful failure in such cases,
342
* check the allocation requirement to make sure we don't go over
343
* the present platform's architectural limits.
345
if (obj_cnt * sizeof(obj_xlat[0]) > OSMALMAX
346
|| prop_cnt * sizeof(prop_xlat[0]) > OSMALMAX
347
|| enum_cnt * sizeof(enum_xlat[0]) > OSMALMAX)
349
G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_TOO_MANY_IDS);
353
/* allocate the translation arrays */
354
obj_xlat = (vm_obj_id_t *)
355
t3malloc((size_t)(obj_cnt * sizeof(obj_xlat[0])));
356
prop_xlat = (vm_prop_id_t *)
357
t3malloc((size_t)(prop_cnt * sizeof(prop_xlat[0])));
358
enum_xlat = (ulong *)
359
t3malloc((size_t)(enum_cnt * sizeof(enum_xlat[0])));
361
/* check to make sure we got the memory */
362
if (obj_xlat == 0 || prop_xlat == 0 || enum_xlat == 0)
364
/* log an error and return failure */
365
G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_OUT_OF_MEM);
371
* Clear out the translation arrays initially. We should, in the
372
* course of loading the symbol table, assign a translation value
373
* for every entry. If anything is left at zero (which is invalid
374
* as an object or property ID), something must be wrong.
376
memset(obj_xlat, 0, (size_t)(obj_cnt * sizeof(obj_xlat[0])));
377
memset(prop_xlat, 0, (size_t)(prop_cnt * sizeof(prop_xlat[0])));
378
memset(enum_xlat, 0, (size_t)(enum_cnt * sizeof(enum_xlat[0])));
380
/* read the function set dependency table */
381
load_funcdep_from_object_file(fp, fname);
383
/* read the metaclass dependency table */
384
load_metadep_from_object_file(fp, fname);
387
* Read the symbol table. This will create translation entries for
388
* the object and property names found in the symbol table.
390
if ((err = G_prs->load_object_file(fp, fname, obj_xlat, prop_xlat,
393
/* that failed - abort the load */
397
/* read the main code stream and its fixup list */
398
G_cs_main->load_object_file(fp, fname);
400
/* read the static code stream and its fixup list */
401
G_cs_static->load_object_file(fp, fname);
403
/* read the data stream and its fixup list */
404
G_ds->load_object_file(fp, fname);
406
/* read the object data stream and its fixup list */
407
G_os->load_object_file(fp, fname);
409
/* read the intrinsic class modifier stream */
410
G_icmod_stream->load_object_file(fp, fname);
412
/* read the BigNumber stream and its fixup list */
413
G_bignum_stream->load_object_file(fp, fname);
415
/* read the static initializer ID stream */
416
G_static_init_id_stream->load_object_file(fp, fname);
418
/* read the object ID fixup list */
419
CTcIdFixup::load_object_file(fp, obj_xlat, obj_cnt, TCGEN_XLAT_OBJ, 4,
420
fname, G_keep_objfixups ? &G_objfixup : 0);
422
/* read the property ID fixup list */
423
CTcIdFixup::load_object_file(fp, prop_xlat, prop_cnt, TCGEN_XLAT_PROP, 2,
424
fname, G_keep_propfixups ? &G_propfixup : 0);
426
/* read the enum ID fixup list */
427
CTcIdFixup::load_object_file(fp, enum_xlat, enum_cnt, TCGEN_XLAT_ENUM, 2,
428
fname, G_keep_enumfixups ? &G_enumfixup : 0);
430
/* if the object file contains debugging information, read that */
431
if ((hdr_flags & TCT3_OBJHDR_DEBUG) != 0)
433
/* load the debug records */
434
load_debug_records_from_object_file(fp, fname,
436
static_cs_start_ofs);
441
* free the ID translation arrays - we no longer need them after
442
* loading the object file, because we translate everything in the
443
* course of loading, so what's left in memory when we're done uses
444
* the new global numbering system
453
/* return the result */
458
/* ------------------------------------------------------------------------ */
460
* Error handler for CTcTokenizer::load_macros_from_file()
462
class MyLoadMacErr: public CTcTokLoadMacErr
465
MyLoadMacErr(const char *fname) { fname_ = fname; }
468
virtual void log_error(int err)
470
/* check the error code */
475
G_tcmain->log_error(0, 0, TC_SEV_ERROR,
476
TCERR_OBJFILE_MACRO_SYM_TOO_LONG, fname_);
482
/* the name of the object file we're loading */
486
/* ------------------------------------------------------------------------ */
488
* Load the debug records from an object file
490
void CTcGenTarg::load_debug_records_from_object_file(
491
CVmFile *fp, const textchar_t *fname,
492
ulong main_cs_start_ofs, ulong static_cs_start_ofs)
495
ulong line_table_cnt;
498
* Note the starting number of our file descriptors - in the file,
499
* we started numbering them at zero, but if we have already loaded
500
* other object files before this one, we'll be numbering ours after
501
* the ones previously loaded. So, we'll need to fix up the
502
* references to the file descriptor indices accordingly.
504
first_filedesc = G_tok->get_next_filedesc_index();
506
/* read the source file list */
507
read_sources_from_object_file(fp);
510
* Read the line record pointers. For each line record table, we
511
* must fix up the line records to reflect the file descriptor
514
for (line_table_cnt = fp->read_uint4() ; line_table_cnt != 0 ;
522
/* read the stream ID */
523
stream_id = fp->read_byte();
525
/* find the appropriate code stream */
526
cs = (CTcCodeStream *)
527
CTcDataStream::get_stream_from_id(stream_id, fname);
529
/* get the appropriate starting offset */
530
start_ofs = (cs == G_cs_main ? main_cs_start_ofs
531
: static_cs_start_ofs);
534
* Read the next line table offset - this is the offset in the
535
* code stream of the next debug line table. Add our starting
536
* offset to get the true offset.
538
ofs = fp->read_uint4() + start_ofs;
540
/* update this table */
541
fix_up_debug_line_table(cs, ofs, first_filedesc);
544
/* read the macro definitions */
545
CVmFileStream str(fp);
546
MyLoadMacErr err_handler(fname);
547
G_tok->load_macros_from_file(&str, &err_handler);
551
* Fix up a debug line record table for the current object file
553
void CTcGenTarg::fix_up_debug_line_table(CTcCodeStream *cs,
554
ulong line_table_ofs,
560
/* read the number of line records in the table */
561
cnt = cs->readu2_at(line_table_ofs);
563
/* adjust each entry */
564
for (ofs = line_table_ofs + 2 ; cnt != 0 ;
565
--cnt, ofs += TCT3_LINE_ENTRY_SIZE)
569
/* read the old file descriptor ID */
570
filedesc = cs->readu2_at(ofs + 2);
572
/* adjust it to the new numbering system */
573
filedesc += first_filedesc;
576
cs->write2_at(ofs + 2, filedesc);
580
/* ------------------------------------------------------------------------ */
582
* Load a function set dependency table from the object file. We can
583
* add to the existing set of functions, but if we have N function sets
584
* defined already, the first N in the file must match the ones we have
587
void CTcGenTarg::load_funcdep_from_object_file(class CVmFile *fp,
588
const textchar_t *fname)
594
cnt = fp->read_int2();
596
/* read the entries */
597
for (cur = fnset_head_ ; cnt != 0 ; --cnt)
602
/* read this entry */
603
len = fp->read_uint2();
604
if (len + 1 > sizeof(buf))
606
G_tcmain->log_error(0, 0, TC_SEV_ERROR,
607
TCERR_OBJFILE_INV_FN_OR_META, fname);
611
/* read the name and null-terminate it */
612
fp->read_bytes(buf, len);
616
* if we are still scanning existing entries, make sure it
617
* matches; otherwise, add it
626
/* get the version suffixes, if any */
627
vsn = lib_find_vsn_suffix(buf, '/', 0, &name_len);
629
lib_find_vsn_suffix(cur->nm, '/', 0, &ent_name_len);
631
/* if it doesn't match, it's an error */
632
if (name_len != ent_name_len
633
|| memcmp(cur->nm, buf, name_len) != 0)
634
G_tcmain->log_error(0, 0, TC_SEV_ERROR,
635
TCERR_OBJFILE_FNSET_CONFLICT,
639
* if the new version string is higher than the old version
640
* string, keep the new version string
642
if (vsn != 0 && ent_vsn != 0 && strcmp(vsn, ent_vsn) > 0
643
&& strlen(vsn) <= strlen(ent_vsn))
646
* the new version is newer than the version in the
647
* table - overwrite the table version with the new
648
* version, so that the table keeps the newest version
649
* mentioned anywhere (newer versions are upwardly
650
* compatible with older versions, so the code that uses
651
* the older version will be equally happy with the
654
strcpy(ent_vsn, vsn);
657
/* move on to the next one */
662
/* we're past the existing list - add the new function set */
669
* Load a metaclass dependency table from the object file. We can add
670
* to the existing set of metaclasses, but if we have N metaclasses
671
* defined already, the first N in the file must match the ones we have
674
void CTcGenTarg::load_metadep_from_object_file(class CVmFile *fp,
675
const textchar_t *fname)
681
cnt = fp->read_int2();
683
/* read the entries */
684
for (cur = meta_head_ ; cnt != 0 ; --cnt)
689
/* read this entry */
690
len = fp->read_uint2();
691
if (len + 1 > sizeof(buf))
693
G_tcmain->log_error(0, 0, TC_SEV_ERROR,
694
TCERR_OBJFILE_INV_FN_OR_META, fname);
698
/* read the name and null-terminate it */
699
fp->read_bytes(buf, len);
703
* if we are still scanning existing entries, make sure it
704
* matches; otherwise, add it
713
/* find the version suffix, if any */
714
vsn = lib_find_vsn_suffix(buf, '/', 0, &name_len);
716
/* find the version suffix in this entry's name */
718
lib_find_vsn_suffix(cur->nm, '/', 0, &ent_name_len);
720
/* if it doesn't match the entry name, it's an error */
721
if (name_len != ent_name_len
722
|| memcmp(cur->nm, buf, name_len) != 0)
724
/* log a mis-matched metaclass error */
725
G_tcmain->log_error(0, 0, TC_SEV_ERROR,
726
TCERR_OBJFILE_META_CONFLICT, buf, fname);
730
* if the new version string is higher than the old version
731
* string, keep the new version string
733
if (vsn != 0 && ent_vsn != 0 && strcmp(vsn, ent_vsn) > 0
734
&& strlen(vsn) <= strlen(ent_vsn))
737
* the new version is newer than the version in the
738
* table - overwrite the table version with the new
739
* version, so that the table keeps the newest version
740
* mentioned anywhere (newer versions are upwardly
741
* compatible with older versions, so the code that uses
742
* the older version will be equally happy with the
745
strcpy(ent_vsn, vsn);
748
/* move on to the next one */
753
/* we're past the existing list - add the new metaclass */
754
add_meta(buf, len, 0);
760
/* ------------------------------------------------------------------------ */
762
* Write the source file list to an object file
764
void CTcGenTarg::write_sources_to_object_file(CVmFile *fp)
766
CTcTokFileDesc *desc;
768
/* write the number of entries */
769
fp->write_int2(G_tok->get_filedesc_count());
771
/* write the entries */
772
for (desc = G_tok->get_first_filedesc() ; desc != 0 ;
773
desc = desc->get_next())
778
/* get the filename - use the resolved local filename */
779
fname = desc->get_fname();
781
/* write the length of the filename */
785
/* write the filename */
786
fp->write_bytes(fname, len);
791
* Read a source file list from an object file
793
void CTcGenTarg::read_sources_from_object_file(CVmFile *fp)
798
/* read the number of entries */
799
cnt = fp->read_uint2();
801
/* read the entries */
802
for (i = 0 ; i < cnt ; ++i)
807
/* read the length of the entry */
808
len = fp->read_uint2();
810
/* see if it fits in our buffer */
811
if (len <= sizeof(fname))
814
fp->read_bytes(fname, len);
818
/* it's too long - truncate to the buffer size */
819
fp->read_bytes(fname, sizeof(fname));
822
fp->set_pos(fp->get_pos() + len - sizeof(fname));
824
/* note the truncated length */
829
* Add it to the tokenizer list. Always create a new entry,
830
* rather than re-using an existing entry. When loading
831
* multiple object files, this might result in the same file
832
* appearing as multiple different descriptors, but it's a small
833
* price to pay (it doesn't add too much redundant space to the
834
* image file, and in any case the information is only retained
835
* when we're compiling for debugging) for a big gain in
836
* simplicity (the source references in the object file can be
837
* fixed up simply by adding the object file's base index to all
838
* of the reference indices).
840
G_tok->create_file_desc(fname, len);
844
/* ------------------------------------------------------------------------ */
846
* Calculate pool layouts. This is called at the start of the link
847
* phase: at this point, we know the sizes of the largest constant pool
848
* and code pool objects, so we can figure the layouts of the pools.
850
void CTcGenTarg::calc_pool_layouts(size_t *first_static_page)
857
* We've parsed the entire program, so we now know the lengths of
858
* the longest string constant and the longest list constant. From
859
* this, we can figure the size of our constant pool pages: since
860
* each list or string must be contained entirely in a single page,
861
* the minimum page size is the length of the longest string or list.
863
* We must pick a power of two for our page size. We don't want to
864
* make the page size too small; each page requires a small amount
865
* of overhead, hence the more pages for a given total constant pool
866
* size, the more overhead. We also don't want to make the page
867
* size too large, because smaller page sizes will give us better
868
* performance on small machines that will have to swap pages in and
869
* out (the smaller a page, the less time it will take to load a
872
* Start at 2k, which is big enough that the data part will
873
* overwhelm the per-page overhead, but small enough that it can be
874
* loaded quickly on a small machine. If that's at least twice the
875
* length of the longest string or list, use it; otherwise, double
880
* find the length in bytes of the longest string - we require the
881
* length prefix in addition to the bytes of the string
883
max_str = max_str_len_ + VMB_LEN;
886
* find the length in bytes of the longest list - we require one
887
* data holder per element, plus the length prefix
889
max_list = (max_list_cnt_ * VMB_DATAHOLDER) + VMB_LEN;
891
/* get the larger of the two - this will be our minimum size */
893
if (max_list > max_item)
897
* if the maximum item size is under 16k, look for a size that will
898
* hold twice the maximum item size; otherwise, relax this
899
* requirement, since the pages are getting big, and look for
900
* something that merely fits the largest element
902
if (max_item < 16*1024)
905
/* calculate the constant pool layout */
906
const_layout_.calc_layout(G_ds, max_item, TRUE);
908
/* calculate the main code pool layout */
909
code_layout_.calc_layout(G_cs_main, max_bytecode_len_, TRUE);
911
/* note the number of pages of regular code */
912
*first_static_page = code_layout_.page_cnt_;
915
* add the static pool into the code pool layout, since we'll
916
* ultimately write the static code as part of the plain code pages
918
code_layout_.calc_layout(G_cs_static, max_bytecode_len_, FALSE);
922
/* ------------------------------------------------------------------------ */
924
* Write the image file
926
void CTcGenTarg::write_to_image(CVmFile *fp, uchar data_xor_mask,
927
const char tool_data[4])
931
unsigned long main_ofs;
932
vm_prop_id_t construct_prop = VM_INVALID_PROP;
933
vm_prop_id_t finalize_prop = VM_INVALID_PROP;
934
vm_prop_id_t objcall_prop = VM_INVALID_PROP;
935
tc_fnset_entry *fnset;
936
CVmImageWriter *image_writer;
940
CTcDataStream *cs_list[2];
941
size_t first_static_code_page;
944
* if we have any BigNumber data, get the BigNumber metaclass index
945
* (or define it, if the program didn't do so itself)
947
if (G_bignum_stream->get_ofs() != 0)
948
bignum_idx = find_or_add_meta("bignumber", 9, 0);
950
/* apply internal object/property ID fixups in the symbol table */
951
G_prs->apply_internal_fixups();
953
/* build the grammar productions */
954
G_prs->build_grammar_productions();
957
* Build the dictionaries. We must wait until after applying the
958
* internal fixups to build the dictionaries, so that we have the
959
* final, fully-resolved form of each object's vocabulary list before
960
* we build the dictionaries. We must also wait until after we build
961
* the grammar productions, because the grammar productions can add
962
* dictionary entries for their literal token matchers.
964
G_prs->build_dictionaries();
967
* Build the multi-method static initializers. Note: this must be done
968
* before we generate the intrinsic class objects, because we might add
969
* intrinsic class modifiers in the course of building the mm
972
build_multimethod_initializers();
974
/* make sure the the IntrinsicClass intrinsic class is itself defined */
975
int_class_idx = find_or_add_meta("intrinsic-class", 15, 0);
977
/* build the IntrinsicClass objects */
978
build_intrinsic_class_objs(G_int_class_stream);
980
/* calculate the final pool layouts */
981
calc_pool_layouts(&first_static_code_page);
983
/* build the source line location maps, if debugging */
985
build_source_line_maps();
987
/* look up the "_main" symbol in the global symbol table */
988
sym = G_prs->get_global_symtab()->find("_main");
991
* if there's no "_main" symbol, or it's not a function, it's an
996
/* "_main" isn't defined - log an error and abort */
997
G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MAIN_NOT_DEFINED);
1000
else if (sym->get_type() != TC_SYM_FUNC)
1002
/* "_main" isn't a function - log an error and abort */
1003
G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MAIN_NOT_FUNC);
1009
* Get the "_main" symbol's code pool address - this is the
1010
* program's entrypoint. We can ask for this information at
1011
* this point because we don't start writing the image file
1012
* until after the final fixup pass, which is where this address
1013
* is finally calculated.
1015
main_ofs = ((CTcSymFunc *)sym)->get_code_pool_addr();
1018
/* get the constructor and finalizer property ID's */
1019
construct_prop = (tctarg_prop_id_t)G_prs->get_constructor_prop();
1020
finalize_prop = (tctarg_prop_id_t)G_prs->get_finalize_prop();
1021
objcall_prop = (tctarg_prop_id_t)G_prs->get_objcall_prop();
1023
/* create our image writer */
1024
image_writer = new CVmImageWriter(fp);
1026
/* prepare the image file - use file format version 1 */
1027
image_writer->prepare(1, tool_data);
1029
/* write the entrypoint offset and data structure parameters */
1030
image_writer->write_entrypt(main_ofs, TCT3_METHOD_HDR_SIZE,
1031
TCT3_EXC_ENTRY_SIZE, TCT3_LINE_ENTRY_SIZE,
1032
TCT3_DBG_HDR_SIZE, TCT3_DBG_LCLSYM_HDR_SIZE,
1035
/* begin writing the symbolic items */
1036
image_writer->begin_sym_block();
1038
/* run through the list of exports in the parser */
1039
for (exp = G_prs->get_exp_head() ; exp != 0 ; exp = exp->get_next())
1045
* if this one's external name is null, it means that we've
1046
* previously encountered it as a duplicate and marked it as such
1047
* - in this case, simply skip it
1049
if (exp->get_ext_name() == 0)
1052
/* make sure it's not one of our special ones */
1053
if (exp->ext_name_matches("LastProp")
1054
|| exp->ext_name_matches("Constructor")
1055
|| exp->ext_name_matches("Destructor")
1056
|| exp->ext_name_matches("ObjectCallProp"))
1058
/* it's a reserved export - flag an error */
1059
G_tcmain->log_error(0, 0, TC_SEV_ERROR,
1060
TCERR_RESERVED_EXPORT,
1061
(int)exp->get_ext_len(),
1062
exp->get_ext_name());
1066
/* look up the symbol, defining as a property if undefined */
1067
sym = G_prs->get_global_symtab()
1068
->find_or_def_prop(exp->get_sym(), exp->get_sym_len(), FALSE);
1071
* Scan the rest of the export list for duplicates. If we find
1072
* the symbol external name exported with a different value, it's
1075
for (dup_err_cnt = 0, exp2 = exp->get_next() ; exp2 != 0 ;
1076
exp2 = exp2->get_next())
1078
/* if this one has already been marked as a duplicate, skip it */
1079
if (exp2->get_ext_name() == 0)
1082
/* check for a match of the external name */
1083
if (exp->ext_name_matches(exp2))
1086
* This one matches, so it's a redundant export for the
1087
* same name. If it's being exported as the same internal
1088
* symbol as the other one, this is fine; otherwise it's
1089
* an error, since the same external name can't be given
1090
* two different meanings.
1092
if (!exp->sym_matches(exp2))
1095
* It doesn't match - log an error. If we've already
1096
* logged an error, show a continuation error;
1097
* otherwise show the first error for the symbol.
1100
if (dup_err_cnt == 1)
1102
/* it's the first error - show the long form */
1103
G_tcmain->log_error(0, 0, TC_SEV_ERROR,
1105
(int)exp->get_ext_len(),
1106
exp->get_ext_name(),
1107
(int)exp->get_sym_len(),
1109
(int)exp2->get_sym_len(),
1114
/* it's a follow-up error */
1115
G_tcmain->log_error(0, 0, TC_SEV_ERROR,
1116
TCERR_DUP_EXPORT_AGAIN,
1117
(int)exp->get_ext_len(),
1118
exp->get_ext_name(),
1119
(int)exp2->get_sym_len(),
1125
* Regardless of whether this one matches or not, remove
1126
* it from the list by setting its external name to null -
1127
* we only want to include each symbol in the export list
1130
exp2->set_extern_name(0, 0);
1134
/* write it out according to its type */
1135
switch(sym->get_type())
1138
/* write the object symbol */
1139
image_writer->write_sym_item_objid(
1140
exp->get_ext_name(), exp->get_ext_len(),
1141
((CTcSymObj *)sym)->get_obj_id());
1145
/* write the property symbol */
1146
image_writer->write_sym_item_propid(
1147
exp->get_ext_name(), exp->get_ext_len(),
1148
((CTcSymProp *)sym)->get_prop());
1152
/* write the function symbol */
1153
image_writer->write_sym_item_func(
1154
exp->get_ext_name(), exp->get_ext_len(),
1155
((CTcSymFunc *)sym)->get_code_pool_addr());
1159
/* can't export other types */
1160
G_tcmain->log_error(0, 0, TC_SEV_ERROR,
1161
TCERR_INVALID_TYPE_FOR_EXPORT,
1162
(int)exp->get_sym_len(), exp->get_sym());
1168
* write the last property ID - this is a special synthetic export
1169
* that we provide automatically
1171
image_writer->write_sym_item_propid("LastProp", next_prop_);
1173
/* write our Constructor and Destructor property ID's */
1174
if (construct_prop != VM_INVALID_PROP)
1175
image_writer->write_sym_item_propid("Constructor", construct_prop);
1176
if (finalize_prop != VM_INVALID_PROP)
1177
image_writer->write_sym_item_propid("Destructor", finalize_prop);
1180
* write the special property ID for calling properties of anonymous
1183
if (objcall_prop != VM_INVALID_PROP)
1184
image_writer->write_sym_item_propid("ObjectCallProp", objcall_prop);
1186
/* done with the symbolic names */
1187
image_writer->end_sym_block();
1189
/* write the function-set dependency table */
1190
image_writer->begin_func_dep(fnset_cnt_);
1191
for (fnset = fnset_head_ ; fnset != 0 ; fnset = fnset->nxt)
1192
image_writer->write_func_dep_item(fnset->nm);
1193
image_writer->end_func_dep();
1195
/* start the metaclass dependency table */
1196
image_writer->begin_meta_dep(meta_cnt_);
1198
/* write the metaclass dependency items */
1199
for (meta = meta_head_ ; meta != 0 ; meta = meta->nxt)
1201
/* write the dependency item */
1202
image_writer->write_meta_dep_item(meta->nm);
1204
/* if there's an associated symbol, write the property list */
1207
CTcSymMetaProp *prop;
1209
/* scan the list of properties and write each one */
1210
for (prop = meta->sym->get_prop_head() ; prop != 0 ;
1213
/* write this item's property */
1214
image_writer->write_meta_item_prop(prop->prop_->get_prop());
1219
/* end the metaclass dependency table */
1220
image_writer->end_meta_dep();
1222
/* write the code pool streams (don't bother masking the code bytes) */
1223
cs_list[0] = G_cs_main;
1224
cs_list[1] = G_cs_static;
1225
code_layout_.write_to_image(cs_list, 2, image_writer, 1, 0);
1228
* write the constant pool (applying the constant pool data mask to
1229
* obscure any text strings in the data)
1231
const_layout_.write_to_image(&G_ds, 1, image_writer, 2, data_xor_mask);
1233
/* write the "TADS object" data */
1234
write_tads_objects_to_image(G_os, image_writer, TCT3_METAID_TADSOBJ);
1236
/* write the intrinsic class modifier object data */
1237
write_tads_objects_to_image(G_icmod_stream, image_writer,
1240
/* write the dictionary data - this is a stream of dictionary objects */
1241
write_nontads_objs_to_image(G_dict_stream, image_writer,
1242
TCT3_METAID_DICT, TRUE);
1244
/* write the grammar data - this is a stream of production objects */
1245
write_nontads_objs_to_image(G_gramprod_stream, image_writer,
1246
TCT3_METAID_GRAMPROD, TRUE);
1248
/* if we have any BigNumber data, write it out */
1249
if (G_bignum_stream->get_ofs() != 0)
1250
write_nontads_objs_to_image(G_bignum_stream,
1251
image_writer, bignum_idx, FALSE);
1253
/* if we have any IntrinsicClass data, write it out */
1254
if (G_int_class_stream->get_ofs() != 0)
1255
write_nontads_objs_to_image(G_int_class_stream, image_writer,
1256
int_class_idx, FALSE);
1258
/* write the static initializer list */
1259
write_static_init_list(image_writer,
1260
first_static_code_page * code_layout_.page_size_);
1262
/* write debug information if desired */
1265
/* write the source file table */
1266
write_sources_to_image(image_writer);
1268
/* write the global symbol table to the image file */
1269
write_global_symbols_to_image(image_writer);
1271
/* write the method header list */
1272
write_method_list_to_image(image_writer);
1274
/* write the macro records */
1275
write_macros_to_image(image_writer);
1278
/* finish the image file */
1279
image_writer->finish();
1281
/* delete our image writer */
1282
delete image_writer;
1286
/* ------------------------------------------------------------------------ */
1288
* Write the static initializer ID list
1290
void CTcGenTarg::write_static_init_list(CVmImageWriter *image_writer,
1298
* calculate the number of initializers - this is simply the size of
1299
* the stream divided by the size of each record (4 bytes for object
1300
* ID, 2 bytes for property ID)
1302
init_cnt = G_static_init_id_stream->get_ofs() / 6;
1304
/* add the multi-method initializer object, if there is one */
1305
if (mminit_obj_ != VM_INVALID_OBJ)
1308
/* start the block */
1309
image_writer->begin_sini_block(main_cs_size, init_cnt);
1311
/* write the multi-method initializer object, if applicable */
1312
if (mminit_obj_ != VM_INVALID_OBJ)
1314
/* write the object data */
1316
oswp4(buf, mminit_obj_); /* the object ID */
1317
oswp2(buf+4, 1); /* our arbitrary initializer property ID */
1318
image_writer->write_bytes(buf, 6);
1321
/* write the bytes */
1322
for (ofs = 0, rem = G_static_init_id_stream->get_ofs() ; rem != 0 ; )
1327
/* get the next chunk */
1328
ptr = G_static_init_id_stream->get_block_ptr(ofs, rem, &cur);
1330
/* write this chunk */
1331
image_writer->write_bytes(ptr, cur);
1333
/* advance past this chunk */
1339
image_writer->end_sini_block();
1342
/* ------------------------------------------------------------------------ */
1344
* Build synthesized code. This is called after all of the object files
1345
* are loaded and before we generate the final image file, to give the
1346
* linker a chance to generate any automatically generated code. We use
1347
* this to generate the stub base functions for multi-methods.
1357
/* _multiMethodCall function symbol */
1360
/* number of multi-method stubs we generated */
1364
void CTcGenTarg::build_synthesized_code()
1368
/* look up the _multiMethodCall function */
1369
ctx.mmc = (CTcSymFunc *)G_prs->get_global_symtab()->find(
1370
"_multiMethodCall", 16);
1373
* our generated code isn't part of any object file - flag a new object
1374
* file so that we don't get confused into thinking this came from the
1375
* last object file loaded
1377
G_cs_static->set_object_file_start_ofs();
1378
G_os->set_object_file_start_ofs();
1380
/* build out the stubs for the multi-method base functions */
1381
G_prs->get_global_symtab()->enum_entries(&multimethod_stub_cb, &ctx);
1384
* if we generated any stubs, we definitely need _multiMethodCall to be
1385
* defined - if it's not, it's an error
1387
if (ctx.cnt != 0 && (ctx.mmc == 0 || ctx.mmc->get_type() != TC_SYM_FUNC))
1389
G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MISSING_MMREG,
1390
"_multiMethodCall");
1394
/* callback context - build multi-method base function stubs */
1395
void CTcGenTarg::multimethod_stub_cb(void *ctx0, CTcSymbol *sym)
1397
mmstub_ctx *ctx = (mmstub_ctx *)ctx0;
1399
/* if this is a function, check to see if it's a multi-method stub */
1400
if (sym->get_type() == TC_SYM_FUNC)
1402
CTcSymFunc *fsym = (CTcSymFunc *)sym;
1405
* It's a base function if it's marked as a multi-method and it
1406
* doesn't have a '*' in its name. (If there's a '*', it's a
1407
* concrete multi-method rather than a base function.)
1409
if (fsym->is_multimethod())
1411
/* it's marked as a multi-method - check for a decorated name */
1412
const char *p = sym->getstr();
1413
size_t rem = sym->getlen();
1414
for ( ; rem != 0 && *p != '*' ; ++p, --rem) ;
1417
tct3_method_gen_ctx gen_ctx;
1420
* It's a multi-method base function - build out its stub.
1421
* The stub function is a varargs function with no fixed
1422
* parameters - i.e., funcName(...). Its body simply calls
1423
* _multiMethodCall with a pointer to itself as the base
1426
G_cg->open_method(G_cs_main,
1427
fsym, fsym->get_fixup_list_anchor(),
1428
0, 0, 0, TRUE, FALSE, FALSE, &gen_ctx);
1430
/* set the anchor in the function symbol */
1431
fsym->set_anchor(gen_ctx.anchor);
1434
* turn the arguments into a list, leaving this on the
1435
* stack as the second argument for _multiMethodCall
1437
G_cg->write_op(OPC_PUSHPARLST);
1441
/* push the function address argument */
1442
CTcConstVal funcval;
1443
funcval.set_funcptr(fsym);
1444
CTPNConst cfunc(&funcval);
1445
cfunc.gen_code(FALSE, FALSE);
1449
* call _multiMethodCall, if it's defined (if not, the
1450
* caller will flag it as an error, so we don't need to
1451
* worry about that here - just skip generating the call)
1454
ctx->mmc->gen_code_call(FALSE, 2, FALSE);
1456
/* return the result */
1457
G_cg->write_op(OPC_RETVAL);
1460
/* finish the method */
1461
G_cg->close_method(0, 0, 0, &gen_ctx);
1462
G_cg->close_method_cleanup(&gen_ctx);
1464
/* the stub symbol now has a definition */
1465
fsym->set_extern(FALSE);
1474
/* ------------------------------------------------------------------------ */
1476
* Start a OBJS header for a TadsObject to a given stream. This only
1477
* writes the fixed part; the caller must then write the superclass list
1478
* and the property table. After the contents have been written, call
1479
* close_tadsobj() to finalize the header data.
1481
void CTcGenTarg::open_tadsobj(tct3_tadsobj_ctx *ctx,
1482
CTcDataStream *stream,
1484
int sc_cnt, int prop_cnt,
1485
unsigned int internal_flags,
1486
unsigned int vm_flags)
1488
/* remember the stream in the context */
1489
ctx->stream = stream;
1491
/* write the internal header */
1492
stream->write2(internal_flags);
1494
/* note the start of the VM object data */
1495
ctx->obj_ofs = stream->get_ofs();
1497
/* write the fixed header data */
1498
stream->write_obj_id(obj_id); /* object ID */
1499
stream->write2(0); /* byte size placeholder - we'll fix up at "close" */
1500
stream->write2(sc_cnt); /* superclass count */
1501
stream->write2(prop_cnt); /* property count */
1502
stream->write2(vm_flags); /* object flags */
1506
* Close a TadsObject header. This must be called after the object's
1507
* contents have been written so that we can fix up the header with the
1510
void CTcGenTarg::close_tadsobj(tct3_tadsobj_ctx *ctx)
1512
/* fix up the object size data */
1513
ctx->stream->write2_at(ctx->obj_ofs + 4,
1514
ctx->stream->get_ofs() - ctx->obj_ofs - 6);
1518
/* ------------------------------------------------------------------------ */
1520
* Linker support: ensure that the given intrinsic class has a modifier
1521
* object. If there's no modifier, we'll create one and add the code for
1522
* it to the object stream.
1524
* This should only be called during the linking phase, after code
1525
* generation is completed. If you want to create a modifier during
1526
* compilation, you should instead use CTcParser::find_or_def_obj(), since
1527
* that creates the necessary structures for object file generation and
1530
void CTcGenTarg::linker_ensure_mod_obj(CTcSymMetaclass *mc)
1532
/* if there's no modifier object, create one */
1533
if (mc->get_mod_obj() == 0)
1535
/* create a modifier object */
1536
CTcSymObj *mod_sym = CTcSymObj::synthesize_modified_obj_sym(FALSE);
1538
/* set it to be an IntrinsicClassModifier object */
1539
mod_sym->set_metaclass(TC_META_ICMOD);
1541
/* link the modifier to the metaclass */
1542
mc->set_mod_obj(mod_sym);
1545
* generate the object data - this is simply an empty object with
1546
* no superclasses, and it goes in the intrinsic class modifier
1549
tct3_tadsobj_ctx obj_ctx;
1551
&obj_ctx, G_icmod_stream,
1552
mod_sym->get_obj_id(), 0, 0, 0, 0);
1553
G_cg->close_tadsobj(&obj_ctx);
1558
* Ensure that the given intrinsic class has a modifier object, by name.
1560
void CTcGenTarg::linker_ensure_mod_obj(const char *name, size_t len)
1562
/* look up the symbol */
1563
CTcSymMetaclass *mc = (CTcSymMetaclass *)G_prs->get_global_symtab()
1566
/* if we found the metaclass symbol, add a modifier if needed */
1567
if (mc != 0 && mc->get_type() == TC_SYM_METACLASS)
1568
linker_ensure_mod_obj(mc);
1572
/* ------------------------------------------------------------------------ */
1574
* Build the multi-method initializers
1577
/* enumerator callback context */
1586
/* _multiMethodRegister function symbol */
1589
/* number of multi-method registrations we generated */
1593
/* main initializer builder */
1594
void CTcGenTarg::build_multimethod_initializers()
1596
tct3_method_gen_ctx genctx;
1599
/* look up the _multiMethodRegister function */
1600
ctx.mmr = (CTcSymFunc *)G_prs->get_global_symtab()->find(
1601
"_multiMethodRegister", 20);
1604
* open the method - it's a static initializer, so write it to the
1607
G_cg->open_method(G_cs_static, 0, 0, 0, 0, 0, FALSE, FALSE, FALSE,
1610
/* scan the symbol table for multimethods and generate initializers */
1611
G_prs->get_global_symtab()->enum_entries(&multimethod_init_cb, &ctx);
1614
* if we found any multi-methods, generate a call to
1615
* _multiMethodBuildBindings
1619
/* look up the function - it's an error if it's not defined */
1620
CTcSymFunc *mmb = (CTcSymFunc *)G_prs->get_global_symtab()->find(
1621
"_multiMethodBuildBindings", 25);
1622
if (mmb == 0 || mmb->get_type() != TC_SYM_FUNC)
1624
G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MISSING_MMREG,
1625
"_multiMethodBuildBindings");
1629
/* write the call instruction */
1630
G_cg->write_op(OPC_CALL);
1631
G_cs->write(0); /* argument count */
1632
mmb->add_abs_fixup(G_cs); /* function address fixup */
1633
G_cs->write4(0); /* fixup placeholder */
1636
/* close the method and clean up */
1637
G_cg->close_method(0, 0, 0, &genctx);
1638
G_cg->close_method_cleanup(&genctx);
1641
* if we generated any registrations, create the initializer object -
1642
* this will go in the static initializer block to trigger invocation
1643
* of the registration routine at load time
1647
/* we have multi-methods, so we definitely need _multiMethodRegister */
1648
if (ctx.mmr == 0 || ctx.mmr->get_type() != TC_SYM_FUNC)
1650
G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MISSING_MMREG,
1651
"_multiMethodRegister");
1655
/* create an anonymous object to hold the initializer code */
1656
mminit_obj_ = G_cg->new_obj_id();
1658
/* write the object header: no superclasses, 1 property */
1659
tct3_tadsobj_ctx obj_ctx;
1660
open_tadsobj(&obj_ctx, G_os, mminit_obj_, 0, 1, 0, 0);
1662
/* write the static initializer property */
1663
G_os->write2(1); /* arbitrary property ID */
1664
G_os->write(VM_CODEOFS); /* offset of the code we just generated */
1665
CTcAbsFixup::add_abs_fixup(
1666
&genctx.anchor->fixup_info_.internal_fixup_head_,
1667
G_os, G_os->get_ofs());
1668
G_os->write4(0); /* placeholder */
1670
/* fix up the object size data */
1671
close_tadsobj(&obj_ctx);
1674
/* switch back to the main stream */
1678
/* callback context - build multi-method registration calls */
1679
void CTcGenTarg::multimethod_init_cb(void *ctx0, CTcSymbol *sym)
1681
mminit_ctx *ctx = (mminit_ctx *)ctx0;
1683
/* if this is a function, check to see if it's a multi-method instance */
1684
if (sym->get_type() == TC_SYM_FUNC)
1686
CTcSymFunc *fsym = (CTcSymFunc *)sym;
1689
* multi-method instances have names of the form
1690
* "name*type1,type2", so check the name to see if it fits the
1693
const char *p = sym->getstr();
1694
size_t rem = sym->getlen();
1696
for ( ; rem != 0 ; ++p, --rem)
1699
* if we found a '*', it's a multimethod; otherwise, if it's
1700
* anything other than a symbol character, it's not a
1708
else if (!is_sym(*p))
1713
* If it's a multi-method symbol, build the initializer. If it's
1714
* the base function for a multi-method, build out the stub
1721
/* note the function base name - it's the part up to the '*' */
1722
const char *funcname = sym->getstr();
1723
size_t funclen = (size_t)(p - funcname);
1725
/* look up the base function symbol */
1726
CTcSymFunc *base_sym = (CTcSymFunc *)G_prs->get_global_symtab()
1727
->find(funcname, funclen);
1729
/* if it's not defined as a function, ignore it */
1730
if (base_sym == 0 || base_sym->get_type() != TC_SYM_FUNC)
1734
* skip to the end of the string, and remove the '*' from the
1741
* Run through the decorated name and look up each mentioned
1742
* class. We need to push the parameters onto the stack in
1743
* reverse order to match the VM calling conventions.
1745
for (argc = 0 ; rem != 0 ; ++argc)
1747
/* remember where the current name starts */
1750
/* skip the terminator for this item */
1753
/* scan backwards to the previous delimiter */
1754
for (plen = 0 ; rem != 0 && *(p-1) != ';' ;
1755
--p, --rem, ++plen) ;
1757
/* look up this name */
1761
* empty name - this slot accepts any type; represent
1762
* this in the run-time formal list with 'nil'
1764
G_cg->write_op(OPC_PUSHNIL);
1767
* An untyped slot is implicitly an Object slot, so we
1768
* need to make sure that Object can participate in the
1769
* binding property system by ensuring that it has a
1772
G_cg->linker_ensure_mod_obj("Object", 6);
1774
else if (plen == 3 && memcmp(p, "...", 3) == 0)
1777
* varargs indicator - represent this in the list with
1778
* the literal string '...'
1781
val.set_sstr("...", 3);
1782
CTPNConst cval(&val);
1783
cval.gen_code(FALSE, FALSE);
1786
* a varargs slot is implicitly an Object slot, so make
1787
* sure Object has a modifier object
1789
G_cg->linker_ensure_mod_obj("Object", 6);
1793
/* class name - look it up */
1794
CTcSymbol *cl = G_prs->get_global_symtab()->find(p, plen);
1798
* if it's missing, unresolved, or not an object, flag
1802
|| (cl->get_type() == TC_SYM_OBJ
1803
&& ((CTcSymObj *)cl)->is_extern()))
1805
G_tcmain->log_error(
1806
0, 0, TC_SEV_ERROR, TCERR_UNDEF_SYM,
1810
else if (cl->get_type() == TC_SYM_OBJ)
1812
/* get the object information */
1813
CTcSymObj *co = (CTcSymObj *)cl;
1814
val.set_obj(co->get_obj_id(), co->get_metaclass());
1816
else if (cl->get_type() == TC_SYM_METACLASS)
1818
/* get the metaclass information */
1819
CTcSymMetaclass *cm = (CTcSymMetaclass *)cl;
1820
val.set_obj(cm->get_class_obj(), TC_META_UNKNOWN);
1823
* If this metaclass doesn't have a modifier
1824
* object, create one for it. This is needed
1825
* because the run-time library's multi-method
1826
* implementation stores the method binding
1827
* information in properties of the parameter
1828
* objects. Since we're using this metaclass as a
1829
* parameter type, we'll need to write at least one
1830
* property to it. We can only write properties to
1831
* intrinsic class objects when they're equipped
1832
* with modifier objects.
1834
* The presence of a modifier object has no effect
1835
* at all on performance for ordinary method call
1836
* operations on the intrinsic class, and the
1837
* modifier itself is just a bare object, so the
1838
* cost of creating this extra object is trivial.
1840
G_cg->linker_ensure_mod_obj(cm);
1844
/* it's not a valid object type */
1845
G_tcmain->log_error(
1846
0, 0, TC_SEV_ERROR, TCERR_MMPARAM_NOT_OBJECT,
1847
(int)plen, p, (int)funclen, funcname);
1852
* represent the object or class in the parameter list
1853
* with the object reference
1855
CTPNConst cval(&val);
1856
cval.gen_code(FALSE, FALSE);
1859
/* note the value we pushed */
1863
/* build and push the list from the parameters */
1866
G_cg->write_op(OPC_NEW1);
1867
G_cs->write((char)argc);
1871
G_cg->write_op(OPC_NEW2);
1874
G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_LIST));
1875
G_cg->write_op(OPC_GETR0);
1876
G_cg->note_pop(argc);
1879
/* push the function pointer argument */
1880
G_cg->write_op(OPC_PUSHFNPTR);
1881
fsym->add_abs_fixup(G_cs);
1885
/* push the base function pointer argument */
1886
CTcConstVal funcval;
1887
funcval.set_funcptr(base_sym);
1888
CTPNConst cfunc(&funcval);
1889
cfunc.gen_code(FALSE, FALSE);
1893
* call _multiMethodRegister, if it's available (if it's not,
1894
* our caller will flag this as an error, so just skip the code
1899
G_cg->write_op(OPC_CALL);
1900
G_cs->write(3); /* argument count */
1901
ctx->mmr->add_abs_fixup(G_cs); /* function address fixup */
1902
G_cs->write4(0); /* fixup placeholder */
1905
/* the 3 arguments will be gone on return */
1908
/* count the registration */
1914
/* ------------------------------------------------------------------------ */
1916
* Build the IntrinsicClass objects
1918
void CTcGenTarg::build_intrinsic_class_objs(CTcDataStream *str)
1920
tc_meta_entry *meta;
1924
* run through the dependency table, and create an IntrinsicClass
1925
* object for each entry
1927
for (idx = 0, meta = meta_head_ ; meta != 0 ; meta = meta->nxt, ++idx)
1930
* if we have a symbol for this class, add the object to the
1931
* intrinsic class stream
1935
/* write the OBJS header */
1936
str->write4(meta->sym->get_class_obj());
1940
* write the data - the data length (8), followed by the
1941
* intrinsic class index that this object is associated
1942
* with, followed by the modifier object
1946
str->write4(meta->sym->get_mod_obj() == 0
1948
: meta->sym->get_mod_obj()->get_obj_id());
1951
* fix up the inheritance chain in the modifier objects, if
1954
meta->sym->fix_mod_obj_sc_list();
1959
/* ------------------------------------------------------------------------ */
1961
* Build the source file line maps. These maps provide listings from
1962
* the source location to the executable location, so the debugger can
1963
* do things such as set a breakpoint at a given source file location.
1965
void CTcGenTarg::build_source_line_maps()
1967
CTcStreamAnchor *anchor;
1969
/* go through the list of anchors in the code stream */
1970
for (anchor = G_cs->get_first_anchor() ; anchor != 0 ;
1971
anchor = anchor->nxt_)
1979
/* get the anchor's stream offset */
1980
start_ofs = anchor->get_ofs();
1982
/* get the anchor's absolute address in the image file */
1983
start_addr = anchor->get_addr();
1985
/* read the debug table offset from the method header */
1986
dbg_ofs = start_ofs + G_cs->readu2_at(start_ofs + 8);
1988
/* if there's no debug table for this method, go on to the next */
1989
if (dbg_ofs == start_ofs)
1992
/* read the number of line entries */
1993
cnt = G_cs->readu2_at(dbg_ofs + TCT3_DBG_HDR_SIZE);
1995
/* go through the individual line entries */
1996
for (ofs = dbg_ofs + TCT3_DBG_HDR_SIZE + 2 ; cnt != 0 ;
1997
--cnt, ofs += TCT3_LINE_ENTRY_SIZE)
2003
CTcTokFileDesc *file_desc;
2006
* get the file position, and the byte-code offset from the
2007
* start of the method of the executable code for the line
2009
method_ofs = G_cs->readu2_at(ofs);
2010
file_id = G_cs->readu2_at(ofs + 2);
2011
linenum = G_cs->readu4_at(ofs + 4);
2013
/* calculate the absolute address of the line in the image file */
2014
line_addr = start_addr + method_ofs;
2016
/* find the given file descriptor */
2017
file_desc = G_tok->get_filedesc(file_id);
2020
* get the original file descriptor, since we always want to
2021
* add to the original, not to the duplicates, if the file
2022
* appears more than once (because this is a one-way mapping
2023
* from file to byte-code location - we thus require a
2026
if (file_desc->get_orig() != 0)
2027
file_desc = file_desc->get_orig();
2029
/* add this line to the file descriptor */
2030
file_desc->add_source_line(linenum, line_addr);
2036
/* ------------------------------------------------------------------------ */
2038
* Callback to write enumerated source lines to an image file
2040
static void write_source_lines_cb(void *ctx, ulong linenum, ulong code_addr)
2042
CVmImageWriter *image_writer;
2044
/* get the image writer */
2045
image_writer = (CVmImageWriter *)ctx;
2047
/* write the data */
2048
image_writer->write_srcf_line_entry(linenum, code_addr);
2052
* Write the list of source file descriptors to an image file
2054
void CTcGenTarg::write_sources_to_image(CVmImageWriter *image_writer)
2056
CTcTokFileDesc *desc;
2058
/* write the block prefix */
2059
image_writer->begin_srcf_block(G_tok->get_filedesc_count());
2061
/* write the entries */
2062
for (desc = G_tok->get_first_filedesc() ; desc != 0 ;
2063
desc = desc->get_next())
2068
* Get the filename. Use the fully resolved local filename, so
2069
* that the debugger can correlate the resolved file back to the
2070
* project configuration. This ties the debug records to the local
2071
* directory structure, but the only drawback of this is that the
2072
* program must be recompiled wherever it's to be used with the
2075
fname = desc->get_fname();
2078
* if we're in test reporting mode, write only the root name, not
2079
* the full name - this insulates test logs from the details of
2080
* local pathname conventions and the local directory structure,
2081
* allowing for more portable test logs
2083
if (G_tcmain->get_test_report_mode())
2084
fname = os_get_root_name((char *)fname);
2086
/* begin this entry */
2087
image_writer->begin_srcf_entry(desc->get_orig_index(), fname);
2089
/* write the source lines */
2090
desc->enum_source_lines(write_source_lines_cb, image_writer);
2092
/* end this entry */
2093
image_writer->end_srcf_entry();
2097
image_writer->end_srcf_block();
2101
* Write the method header list to the image file
2103
void CTcGenTarg::write_method_list_to_image(CVmImageWriter *image_writer)
2105
CTcStreamAnchor *anchor;
2107
/* begin the method header list block in the image file */
2108
image_writer->begin_mhls_block();
2110
/* go through the list of anchors in the code stream */
2111
for (anchor = G_cs->get_first_anchor() ; anchor != 0 ;
2112
anchor = anchor->nxt_)
2114
/* write this entry's code pool address */
2115
image_writer->write_mhls_entry(anchor->get_addr());
2119
image_writer->end_mhls_block();
2123
* Write the preprocessor macros to the image file, for debugger use
2125
void CTcGenTarg::write_macros_to_image(CVmImageWriter *image_writer)
2127
/* begin the macro block */
2128
image_writer->begin_macr_block();
2131
* ask the tokenizer to dump the data directly to the file underlying
2134
G_tok->write_macros_to_file_for_debug(image_writer->get_fp());
2136
/* end the macro block */
2137
image_writer->end_macr_block();
2140
/* ------------------------------------------------------------------------ */
2142
* Callback context for global symbol table writer
2144
struct write_sym_to_image_cb
2146
/* number of symbols written */
2149
/* the image writer */
2150
CVmImageWriter *image_writer;
2154
* Callback for writing the global symbol table to an object file
2156
static void write_sym_to_image(void *ctx0, CTcSymbol *sym)
2158
write_sym_to_image_cb *ctx;
2160
/* cast the context */
2161
ctx = (write_sym_to_image_cb *)ctx0;
2164
* If the symbol's name starts with a period, don't write it - the
2165
* compiler constructs certain private symbol names for its own
2166
* internal use, and marks them as such by starting the name with a
2167
* period. These symbols cannot be used to evaluate expressions, so
2168
* they're of no use in teh global symbol table in the image file.
2170
if (sym->get_sym()[0] == '.')
2173
/* ask the symbol to do the work */
2174
if (sym->write_to_image_file_global(ctx->image_writer))
2176
/* we wrote the symbol - count it */
2182
* Write the global symbol table to an object file
2184
void CTcGenTarg::write_global_symbols_to_image(CVmImageWriter *image_writer)
2186
write_sym_to_image_cb ctx;
2188
/* set up the callback context */
2190
ctx.image_writer = image_writer;
2192
/* start the block */
2193
image_writer->begin_gsym_block();
2195
/* ask the symbol table to enumerate itself through our symbol writer */
2196
G_prs->get_global_symtab()->enum_entries(&write_sym_to_image, &ctx);
2199
image_writer->end_gsym_block(ctx.count);
2202
/* ------------------------------------------------------------------------ */
2204
* Look up a property
2206
vm_prop_id_t CTcGenTarg::look_up_prop(const char *propname, int required,
2207
int err_if_undef, int err_if_not_prop)
2211
/* look up the symbol */
2212
sym = G_prs->get_global_symtab()->find(propname);
2214
/* check to see if it's defined and of the proper type */
2217
/* log the 'undefined' error */
2218
G_tcmain->log_error(0, 0, required ? TC_SEV_ERROR : TC_SEV_PEDANTIC,
2221
else if (sym->get_type() != TC_SYM_PROP)
2223
/* log the 'not a property' error */
2224
G_tcmain->log_error(0, 0, required ? TC_SEV_ERROR : TC_SEV_PEDANTIC,
2229
/* return the property ID */
2230
return ((CTcSymProp *)sym)->get_prop();
2233
/* if we got here, we didn't find a valid property */
2234
return VM_INVALID_PROP;
2238
/* ------------------------------------------------------------------------ */
2240
* Write a TADS object stream to the image file. We'll write blocks of
2241
* size up to somewhat less than 64k, to ensure that the file is usable on
2244
void CTcGenTarg::write_tads_objects_to_image(CTcDataStream *os,
2245
CVmImageWriter *image_writer,
2248
/* write the persistent (non-transient) objects */
2249
write_tads_objects_to_image(os, image_writer, meta_idx, FALSE);
2251
/* write the transient objects */
2252
write_tads_objects_to_image(os, image_writer, meta_idx, TRUE);
2256
* Write the TADS object stream to the image file, writing only persistent
2257
* or transient objects.
2259
void CTcGenTarg::write_tads_objects_to_image(CTcDataStream *os,
2260
CVmImageWriter *image_writer,
2261
int meta_idx, int trans)
2265
/* keep going until we've written the whole file */
2266
for (start_ofs = 0 ; start_ofs < os->get_ofs() ; )
2274
* Scan the stream. Each entry in the stream is a standard
2275
* object record, which means that it starts with the object ID
2276
* (UINT4) and the length (UINT2) of the metaclass-specific
2277
* data, which is then followed by the metaclass data. Skip as
2278
* many objects as we can while staying within our approximately
2281
for (block_size = 0, ofs = start_ofs, cnt = 0 ; ; )
2285
size_t orig_prop_cnt;
2286
size_t write_prop_cnt;
2291
/* if we've reached the end of the stream, we're done */
2292
if (ofs >= os->get_ofs())
2295
/* remember the starting offset */
2298
/* read our internal flags */
2299
flags = os->readu2_at(ofs + TCT3_OBJ_INTERNHDR_FLAGS_OFS);
2302
* get the size of this block - this is the
2303
* metaclass-specific data size at offset 4 in the T3
2304
* metaclass header, plus the size of the T3 metaclass
2305
* header, plus the size of our internal header
2307
siz = TCT3_OBJ_INTERNHDR_SIZE
2308
+ TCT3_META_HEADER_SIZE
2309
+ os->readu2_at(ofs + TCT3_META_HEADER_OFS + 4);
2312
* Calculate the offset of the next block. Note that this is
2313
* the current offset plus the original block size; the amount
2314
* of data we end up writing might be less than the original
2315
* block size because we might have deleted property slots
2316
* when we sorted and compressed the property table.
2318
next_ofs = ofs + siz;
2320
/* if this object was deleted, skip it */
2321
if ((flags & TCT3_OBJ_REPLACED) != 0)
2328
* if this object is of the wrong persistent/transient type,
2331
if (((flags & TCT3_OBJ_TRANSIENT) != 0) != (trans != 0))
2338
* if this would push us over the limit, stop here and start a
2341
if (block_size + siz > 64000L)
2345
* We must sort the property table, in order of ascending
2346
* property ID, before we write the image file. We had to
2347
* wait until now to do this, because the final property ID
2348
* assignments aren't made until link time.
2350
write_prop_cnt = sort_object_prop_table(os, ofs);
2352
/* note the original property count */
2353
orig_prop_cnt = CTPNStmObject::get_stream_prop_cnt(os, ofs);
2356
* Then temporarily pdate the property count in the stream, in
2357
* case we changed it in the sorting process.
2359
* Calculate the new size of the data to write. Note that we
2360
* must add in the size of the T3 metaclass header, since this
2361
* isn't reflected in the data size.
2364
CTPNStmObject::set_stream_prop_cnt(os, ofs, write_prop_cnt)
2365
+ TCT3_META_HEADER_SIZE;
2368
* if this is the first object in this block, write the
2372
image_writer->begin_objs_block(meta_idx, FALSE, trans);
2375
* skip past our internal header - we don't want to write
2376
* our internal header to the image file, since this was
2377
* purely for our own use in the compiler and linker
2379
ofs += TCT3_OBJ_INTERNHDR_SIZE;
2382
* write the object data; write the size returned from
2383
* sorting the property table, which might be different than
2384
* the original block data size in the stream, because we
2385
* might have compressed the property table
2387
for (rem_len = write_size ; rem_len != 0 ; )
2392
/* get the next block */
2393
p = os->get_block_ptr(ofs, rem_len, &avail_len);
2396
image_writer->write_objs_bytes(p, avail_len);
2398
/* move past this block */
2400
rem_len -= avail_len;
2403
/* count the object */
2406
/* restore the original stream property count */
2407
CTPNStmObject::set_stream_prop_cnt(os, orig_ofs, orig_prop_cnt);
2409
/* move on to the next block */
2413
/* if we wrote any objects, end the block */
2415
image_writer->end_objs_block(cnt);
2417
/* move on to the next block */
2422
/* ------------------------------------------------------------------------ */
2424
* Write an object stream of non-TADS objects to the image file
2426
void CTcGenTarg::write_nontads_objs_to_image(CTcDataStream *os,
2427
CVmImageWriter *image_writer,
2428
int meta_idx, int large_objs)
2432
/* keep going until we've written the whole file */
2433
for (start_ofs = 0 ; start_ofs < os->get_ofs() ; )
2441
* Scan the stream. Each entry in the stream is either a small or
2442
* large object record,, which means that it starts with the
2443
* object ID (UINT4) and the length (UINT2 for small, UINT4 for
2444
* large) of the metaclass-specific data, which is then followed
2445
* by the metaclass data.
2447
* Include as many objects as we can while staying within our
2448
* approximately 64k limit, if this is a small-format block; fill
2449
* the block without limit if this is a large-format block.
2451
for (block_size = 0, ofs = start_ofs, cnt = 0 ; ; )
2456
/* if we've reached the end of the stream, we're done */
2457
if (ofs >= os->get_ofs())
2461
* get the size of this block - this is the
2462
* metaclass-specific data size at offset 4 in the T3
2463
* metaclass header, plus the size of the T3 metaclass
2469
* Get the 32-bit size value. Note that we don't worry
2470
* about limiting the overall block size to 64k when we're
2471
* writing a "large" object block.
2473
siz = (ulong)os->readu4_at(ofs + 4)
2474
+ TCT3_LARGE_META_HEADER_SIZE;
2478
/* get the 16-bit size value */
2479
siz = (ulong)os->read2_at(ofs + 4)
2480
+ TCT3_META_HEADER_SIZE;
2483
* Since this is a small-object block, limit the aggregate
2484
* size of the entire block to 64k. So, if this block
2485
* would push us over the 64k aggregate for the block,
2486
* start a new OBJS block with this object.
2488
if (cnt != 0 && block_size + siz > 64000L)
2493
* if this is the first object in this block, write the
2494
* block header - the dictionary uses large object headers,
2498
image_writer->begin_objs_block(meta_idx, large_objs, FALSE);
2500
/* calculate the offset of the next block */
2501
next_ofs = ofs + siz;
2503
/* write the object data */
2504
for (rem_len = siz ; rem_len != 0 ; )
2509
/* get the next block */
2510
p = os->get_block_ptr(ofs, rem_len, &avail_len);
2513
image_writer->write_objs_bytes(p, avail_len);
2515
/* move past this block */
2517
rem_len -= avail_len;
2520
/* count the object */
2523
/* move on to the next block */
2527
/* if we wrote any objects, end the block */
2529
image_writer->end_objs_block(cnt);
2531
/* move on to the next block */
2537
/* ------------------------------------------------------------------------ */
2539
* Property comparison callback function for qsort() when invoked from
2540
* sort_object_prop_table()
2542
//extern "C" int prop_compare(const void *p1, const void *p2);
2544
static int prop_compare(const void *p1, const void *p2)
2552
/* compare them and return the result */
2553
return (id1 < id2 ? -1 : id1 == id2 ? 0 : 1);
2558
* Sort an object's property table. This puts the property table into
2559
* order of ascending property ID, and deletes any unused properties from
2562
* Note that we do NOT update the stream to indicate the reduced number of
2563
* properties if we delete any properties. Instead, we simply return the
2564
* new number of properties.
2566
size_t CTcGenTarg::sort_object_prop_table(CTcDataStream *os, ulong start_ofs)
2568
uint prop_table_size;
2569
ulong orig_prop_cnt;
2574
/* read the number of properties from the header */
2575
prop_cnt = CTPNStmObject::get_stream_prop_cnt(os, start_ofs);
2577
/* remember the original property count, in case we delete unused slots */
2578
orig_prop_cnt = prop_cnt;
2580
/* calculate the property table size */
2581
prop_table_size = prop_cnt * TCT3_TADSOBJ_PROP_SIZE;
2583
/* get the offset of the first property */
2584
prop_ofs = CTPNStmObject::get_stream_first_prop_ofs(os, start_ofs);
2586
/* reallocate the sort buffer if necessary */
2587
if (prop_table_size > sort_buf_size_)
2589
/* increase the sort buffer size to the next 4k increment */
2590
sort_buf_size_ = (prop_table_size + 4095) & ~4096;
2592
/* reallocate the buffer */
2593
sort_buf_ = (char *)t3realloc(sort_buf_, sort_buf_size_);
2594
if (sort_buf_ == 0 || sort_buf_size_ < prop_table_size)
2595
G_tok->throw_internal_error(TCERR_CODEGEN_NO_MEM);
2598
/* extract the table into our buffer */
2599
os->copy_to_buf(sort_buf_, prop_ofs, prop_table_size);
2602
* Compress the table by removing any properties that have been
2603
* marked as deleted -- if we had any 'modify + replace' properties
2604
* that we resolved at link time, we will have marked those
2605
* properties for deletion by setting their property ID's to zero in
2606
* the table. Scan the table for any such properties and remove
2609
for (src = dst = 0, prop_cnt = 0 ; src < prop_table_size ;
2610
src += TCT3_TADSOBJ_PROP_SIZE)
2612
/* if this property isn't marked for deletion, keep it */
2613
if (osrp2(sort_buf_ + src) != VM_INVALID_PROP)
2616
* we're keeping it - if we can move it to a lower table
2617
* position, copy the data to the new position, otherwise
2621
memcpy(sort_buf_ + dst, sort_buf_ + src,
2622
TCT3_TADSOBJ_PROP_SIZE);
2625
* advance the destination pointer past this slot, since
2626
* we're going to keep the data in the slot
2628
dst += TCT3_TADSOBJ_PROP_SIZE;
2630
/* count this property, since we're keeping it */
2635
/* sort the table */
2636
qsort(sort_buf_, prop_cnt, TCT3_TADSOBJ_PROP_SIZE, &prop_compare);
2638
/* add back any unused slots after all of the sorted slots */
2639
for ( ; dst < prop_table_size ; dst += TCT3_TADSOBJ_PROP_SIZE)
2640
oswp2(sort_buf_ + dst, VM_INVALID_PROP);
2642
/* put the sorted table back in the buffer */
2643
os->write_at(prop_ofs, sort_buf_, prop_table_size);
2645
/* return the (possibly reduced) number of properties */
2651
* callback context for enumerating a dictionary
2653
struct enum_dict_ctx
2655
/* number of entries written so far */
2660
* Generate code for a dictionary object
2662
void CTcGenTarg::gen_code_for_dict(CTcDictEntry *dict)
2670
* Write the OBJS header - object ID plus byte count for
2671
* metaclass-specific data (use a placeholder length for now)
2673
G_dict_stream->write4(dict->get_sym()->get_obj_id());
2674
size_ofs = G_dict_stream->get_ofs();
2675
G_dict_stream->write4(0);
2678
* Write the metaclass-specific data for the 'dictionary' metaclass
2681
/* write a nil comparator object initially */
2682
G_dict_stream->write4(0);
2684
/* write a placeholder for the entry count */
2685
entry_cnt_ofs = G_dict_stream->get_ofs();
2686
G_dict_stream->write2(0);
2688
/* write the dictionary entries */
2690
dict->get_hash_table()->enum_entries(&enum_dict_gen_cb, &ctx);
2692
/* remember the ending offset of the table */
2693
end_ofs = G_dict_stream->get_ofs();
2695
/* go back and fix up the total size of the object data */
2696
G_dict_stream->write4_at(size_ofs, end_ofs - size_ofs - 4);
2698
/* fix up the dictionary entry count */
2699
G_dict_stream->write2_at(entry_cnt_ofs, ctx.cnt);
2703
* Callback - enumerate dictionary entries for code generation
2705
void CTcGenTarg::enum_dict_gen_cb(void *ctx0, CVmHashEntry *entry0)
2707
enum_dict_ctx *ctx = (enum_dict_ctx *)ctx0;
2708
CVmHashEntryPrsDict *entry = (CVmHashEntryPrsDict *)entry0;
2714
CTcPrsDictItem *item;
2716
/* count this entry */
2719
/* limit the key length to 255 bytes */
2720
len = entry->getlen();
2724
/* copy the entry to our buffer */
2725
memcpy(buf, entry->getstr(), len);
2727
/* apply the XOR obfuscation to the key text */
2728
for (p = buf, rem = len ; rem != 0 ; ++p, --rem)
2731
/* write the length of the key followed by the key string */
2732
G_dict_stream->write((uchar)len);
2733
G_dict_stream->write(buf, len);
2735
/* count the items in this entry */
2736
for (cnt = 0, item = entry->get_list() ; item != 0 ;
2737
++cnt, item = item->nxt_) ;
2739
/* write the number of entries */
2740
G_dict_stream->write2(cnt);
2742
/* write the entries */
2743
for (item = entry->get_list() ; item != 0 ; item = item->nxt_)
2745
/* write the object ID and property ID of this entry */
2746
G_dict_stream->write4(item->obj_);
2747
G_dict_stream->write2(item->prop_);
2752
* Generate code for a grammar production
2754
void CTcGenTarg::gen_code_for_gramprod(CTcGramProdEntry *prod)
2759
CTcGramProdAlt *alt;
2760
CTcDataStream *str = G_gramprod_stream;
2763
* write the OBJS header - object ID plus byte count for
2764
* metaclass-specific data (use a placeholder length for now)
2766
str->write4(prod->get_prod_sym()->get_obj_id());
2767
size_ofs = str->get_ofs();
2771
* Write the metaclass-specific data for the 'grammar-production'
2775
/* count the alternatives */
2776
for (cnt = 0, alt = prod->get_alt_head() ; alt != 0 ;
2777
++cnt, alt = alt->get_next()) ;
2780
* If this production has no alternatives and was not explicitly
2781
* declared, flag an error indicating that the production is
2782
* undeclared. We treat this as an error because there's a good chance
2783
* that the an alternative referring to the production misspelled the
2784
* name. If the production was explicitly declared, then we have
2785
* sufficient confirmation that the name is correct, so no error is
2788
if (cnt == 0 && !prod->is_declared())
2789
G_tcmain->log_error(0, 0, TC_SEV_ERROR,
2790
TCERR_GRAMPROD_HAS_NO_ALTS,
2791
(int)prod->get_prod_sym()->get_sym_len(),
2792
prod->get_prod_sym()->get_sym());
2795
* The count has to fit in 16 bits; it's surprisingly easy to exceed
2796
* this by using the power of permutation (with nested '|'
2797
* alternatives), so check for overflow and flag an error. Even though
2798
* it's not hard to exceed the limit, it's not desirable to create so
2799
* many permutations, so the limit isn't really in need of being
2800
* raised; it's better to rewrite a rule with a huge number of
2801
* permutations using sub-productions.
2804
G_tcmain->log_error(0, 0, TC_SEV_ERROR,
2805
TCERR_GRAMPROD_TOO_MANY_ALTS,
2806
(int)prod->get_prod_sym()->get_sym_len(),
2807
prod->get_prod_sym()->get_sym());
2809
/* write the number of alternatives */
2812
/* write the alternatives */
2813
for (alt = prod->get_alt_head() ; alt != 0 ; alt = alt->get_next())
2815
CTcGramProdTok *tok;
2817
/* write the score and badness for the alternative */
2818
str->write2(alt->get_score());
2819
str->write2(alt->get_badness());
2821
/* write the processor object ID for this alternative */
2822
str->write4(alt->get_processor_obj()->get_obj_id());
2824
/* count the tokens in this alternative */
2825
for (cnt = 0, tok = alt->get_tok_head() ; tok != 0 ;
2826
++cnt, tok = tok->get_next()) ;
2828
/* write the token count */
2831
/* write the tokens */
2832
for (tok = alt->get_tok_head() ; tok != 0 ; tok = tok->get_next())
2836
/* write the property association */
2837
str->write2((uint)tok->get_prop_assoc());
2839
/* write the token data */
2840
switch(tok->get_type())
2843
/* write the type */
2844
str->write((uchar)VMGRAM_MATCH_PROD);
2846
/* write the sub-production object ID */
2847
str->write4((ulong)tok->getval_prod()->get_obj_id());
2850
case TCGRAM_PART_OF_SPEECH:
2851
/* write the type */
2852
str->write((uchar)VMGRAM_MATCH_SPEECH);
2854
/* write the part-of-speech property */
2855
str->write2((uint)tok->getval_part_of_speech());
2858
case TCGRAM_PART_OF_SPEECH_LIST:
2859
/* write the type */
2860
str->write((uchar)VMGRAM_MATCH_NSPEECH);
2862
/* write the number of elements in the property list */
2863
str->write2((uint)tok->getval_part_list_len());
2865
/* write each element */
2866
for (idx = 0 ; idx < tok->getval_part_list_len() ; ++idx)
2867
str->write2((uint)tok->getval_part_list_ele(idx));
2872
case TCGRAM_LITERAL:
2873
/* write the type */
2874
str->write((uchar)VMGRAM_MATCH_LITERAL);
2876
/* write the string length prefix */
2877
str->write2(tok->getval_literal_len());
2879
/* write the string text */
2880
str->write(tok->getval_literal_txt(),
2881
tok->getval_literal_len());
2884
* add the word to the dictionary that was active when the
2885
* alternative was defined
2887
if (alt->get_dict() != 0)
2890
* there's a dictionary - add the word, associating it
2891
* with the production object and with the parser's
2892
* miscVocab property
2894
alt->get_dict()->add_word(
2895
tok->getval_literal_txt(), tok->getval_literal_len(),
2896
FALSE, prod->get_prod_sym()->get_obj_id(),
2897
G_prs->get_miscvocab_prop());
2901
case TCGRAM_TOKEN_TYPE:
2902
/* write the type */
2903
str->write((uchar)VMGRAM_MATCH_TOKTYPE);
2905
/* write the enum ID of the token */
2906
str->write4(tok->getval_token_type());
2910
/* write the type - there's no additional data */
2911
str->write((uchar)VMGRAM_MATCH_STAR);
2921
/* remember the ending offset of the object data */
2922
end_ofs = str->get_ofs();
2924
/* go back and fix up the total size of the object data */
2925
str->write4_at(size_ofs, end_ofs - size_ofs - 4);
2929
/* ------------------------------------------------------------------------ */
2931
* Data Stream Layout Manager
2935
* calculate the size of the pool pages, given the size of the largest
2938
void CTcStreamLayout::calc_layout(CTcDataStream *ds, ulong max_len,
2943
CTcStreamAnchor *anchor;
2945
/* if this is the first page, handle some things specially */
2951
* Starting at 2k, look for a page size that will fit the
2952
* desired minimum size.
2954
for (pgsiz = 2048 ; pgsiz < max_len ; pgsiz <<= 1) ;
2956
/* remember our selected page size */
2959
/* start at the bottom of the first page */
2967
* this isn't the first page - if there are no anchors, don't
2968
* bother adding anything
2970
if (ds->get_first_anchor() == 0)
2974
* start at the end of the last existing page - this will ensure
2975
* that everything added from the new stream will go onto a
2976
* brand new page after everything from the previous stream
2979
free_ofs = page_size_ * page_cnt_;
2983
* Run through the list of stream anchors and calculate the layout.
2984
* For each item, assign its final pool address and apply its
2987
for (anchor = ds->get_first_anchor() ; anchor != 0 ;
2988
anchor = anchor->nxt_)
2993
* if this anchor has been marked as replaced, don't include it
2994
* in our calculations, because we don't want to include this
2995
* block in the image file
2997
if (anchor->is_replaced())
3001
* if this item fits on the current page, assign it the next
3002
* sequential address; otherwise, go to the next page
3004
* if this anchor is at the dividing point, put it on the next
3005
* page, unless we just started a new page
3007
len = anchor->get_len(ds);
3011
* we must start the next page - skip to the next page by
3012
* moving past the remaining free space on this page
3016
/* count the new page */
3019
/* the whole next page is available to us now */
3024
* set the anchor's final address, which will apply fixups for
3025
* the object's fixup list
3027
anchor->set_addr(free_ofs);
3029
/* advance past this block */
3034
/* if there's no data at all, we have zero pages */
3041
* Write our stream to an image file
3043
void CTcStreamLayout::write_to_image(CTcDataStream **ds_arr, size_t ds_cnt,
3044
CVmImageWriter *image_writer,
3045
int pool_id, uchar xor_mask)
3047
CTcStreamAnchor *anchor;
3049
ulong next_page_start;
3052
/* write the constant pool definition block - the pool's ID is 2 */
3053
image_writer->write_pool_def(pool_id, page_cnt_, page_size_, TRUE);
3056
* start out before the first page - the next page starts with the
3057
* item at offset zero
3060
next_page_start = 0;
3062
/* run through each stream */
3063
for ( ; ds_cnt != 0 ; ++ds_arr, --ds_cnt)
3067
/* get the current stream */
3070
/* run through the anchor list for this stream */
3071
for (anchor = ds->get_first_anchor() ; anchor != 0 ;
3072
anchor = anchor->nxt_)
3079
* if this anchor is marked as replaced, skip it entirely -
3080
* we omit replaced blocks from the image file, because
3081
* they're completely unreachable
3083
if (anchor->is_replaced())
3087
* if this item's assigned address is on the next page, move
3090
len = anchor->get_len(ds);
3091
addr = anchor->get_addr();
3092
if (addr == next_page_start)
3094
/* if this isn't the first page, close the previous page */
3096
image_writer->end_pool_page();
3098
/* start the new page */
3099
image_writer->begin_pool_page(pool_id, pgnum, TRUE, xor_mask);
3101
/* this item is at the start of the new page */
3102
free_ofs = next_page_start;
3104
/* count the new page */
3107
/* calculate the address of the start of the next page */
3108
next_page_start += page_size_;
3111
/* advance past this block */
3115
* write the data from the stream to the image file - we
3116
* must iterate over the chunks the code stream returns,
3117
* since it might not be able to return the entire block in
3118
* a single operation
3120
for (stream_ofs = anchor->get_ofs() ; len != 0 ; )
3125
/* get the pointer to this chunk */
3126
ptr = ds->get_block_ptr(stream_ofs, len, &cur);
3128
/* write this chunk */
3129
image_writer->write_pool_page_bytes(ptr, cur, xor_mask);
3131
/* advance our pointers past this chunk */
3138
/* if we started a page, end it */
3140
image_writer->end_pool_page();
3143
/* ------------------------------------------------------------------------ */
3145
* Object Symbol subclass - image-file functions
3149
* mark the compiled data for the object as a 'class' object
3151
void CTcSymObj::mark_compiled_as_class()
3156
/* get the appropriate stream for generating the data */
3159
/* get my original object flags */
3160
flags = CTPNStmObject::get_stream_obj_flags(str, stream_ofs_);
3162
/* add in the 'class' flag */
3163
flags |= TCT3_OBJFLG_CLASS;
3165
/* set the updated flags */
3166
CTPNStmObject::set_stream_obj_flags(str, stream_ofs_, flags);
3170
* Delete a property from our modified base classes
3172
void CTcSymObj::delete_prop_from_mod_base(tctarg_prop_id_t prop_id)
3178
/* get the correct data stream */
3181
/* get the number of properties in the object */
3182
prop_cnt = CTPNStmObject::get_stream_prop_cnt(str, stream_ofs_);
3184
/* find the property in our property table */
3185
for (i = 0 ; i < prop_cnt ; ++i)
3187
/* if this property ID matches, delete it */
3188
if (CTPNStmObject::get_stream_prop_id(str, stream_ofs_, i)
3191
/* delete the object by setting its ID to 'invalid' */
3192
CTPNStmObject::set_stream_prop_id(str, stream_ofs_, i,
3196
* there's no need to look any further - a property can
3197
* occur only once in an object
3205
* Build the dictionary
3207
void CTcSymObj::build_dictionary()
3213
* Inherit the default handling - this will explicitly add all
3214
* superclass dictionary data into my own internal dictionary list,
3215
* so that we don't have to worry at all about superclasses here.
3216
* This will also add our words to my associated dictionary object.
3218
CTcSymObjBase::build_dictionary();
3220
/* if I'm not a regular tads object, there's nothing to do here */
3221
if (metaclass_ != TC_META_TADSOBJ)
3225
* Examine my properties. Each time we find a property whose value
3226
* is set to vocab-list, replace it with an actual list of strings
3227
* for my vocabulary words associated with the property.
3230
/* get the number of properties in the object */
3231
prop_cnt = CTPNStmObject::get_stream_prop_cnt(G_os, stream_ofs_);
3233
/* find the property in our property table */
3234
for (i = 0 ; i < prop_cnt ; ++i)
3237
vm_datatype_t prop_type;
3239
/* get this property value */
3240
prop_type = CTPNStmObject::get_stream_prop_type(G_os, stream_ofs_, i);
3243
* if it's a vocabulary list placeholder, replace it with the
3244
* actual list of vocabulary strings
3246
if (prop_type == VM_VOCAB_LIST)
3248
vm_prop_id_t prop_id;
3249
CTcVocabEntry *entry;
3253
/* get the property ID */
3254
prop_id = CTPNStmObject::get_stream_prop_id(G_os, stream_ofs_, i);
3256
/* get the value offset of this property */
3257
prop_val_ofs = CTPNStmObject::
3258
get_stream_prop_val_ofs(G_os, stream_ofs_, i);
3261
lst = new CTPNList();
3264
* scan my internal vocabulary list and add the entries
3265
* associated with this property
3267
for (entry = vocab_ ; entry != 0 ; entry = entry->nxt_)
3269
/* if this one matches our property, add it */
3270
if (entry->prop_ == prop_id)
3272
CTcConstVal str_val;
3275
/* create a string element */
3276
str_val.set_sstr(entry->txt_, entry->len_);
3277
ele = new CTPNConst(&str_val);
3279
/* add it to the list */
3280
lst->add_element(ele);
3285
* Overwrite the original property value with the new list.
3286
* If the list is empty, this object doesn't define or
3287
* inherit any vocabulary of this property at all, so we can
3288
* clear the property entirely.
3290
if (lst->get_count() == 0)
3293
* delete the property from the object by setting its
3294
* property ID to 'invalid'
3297
set_stream_prop_id(G_os, stream_ofs_, i, VM_INVALID_PROP);
3301
/* write the list value to the property */
3303
G_cg->write_const_as_dh(G_os, prop_val_ofs, &val);
3310
/* ------------------------------------------------------------------------ */
3312
* Symbol table entry routines for writing a symbol to the global symbol
3313
* table in the debug records in the image file
3317
* write the symbol to an image file's global symbol table
3319
int CTcSymFunc::write_to_image_file_global(class CVmImageWriter *image_writer)
3323
/* build our extra data buffer */
3324
oswp4(buf, get_code_pool_addr());
3325
oswp2(buf + 4, get_argc());
3326
buf[6] = (is_varargs() != 0);
3327
buf[7] = (has_retval() != 0);
3329
/* write the data */
3330
image_writer->write_gsym_entry(get_sym(), get_sym_len(),
3331
(int)TC_SYM_FUNC, buf, 8);
3333
/* we wrote the symbol */
3338
* write the symbol to an image file's global symbol table
3340
int CTcSymObj::write_to_image_file_global(class CVmImageWriter *image_writer)
3344
/* store our object ID in the extra data buffer */
3345
oswp4(buf, obj_id_);
3347
/* add our modifying object ID, if we have a modifying object */
3348
if (get_modifying_sym() != 0)
3349
oswp4(buf + 4, get_modifying_sym()->get_obj_id());
3353
/* write the data */
3354
image_writer->write_gsym_entry(get_sym(), get_sym_len(),
3355
(int)TC_SYM_OBJ, buf, 8);
3357
/* we wrote the symbol */
3362
* write the symbol to an image file's global symbol table
3364
int CTcSymProp::write_to_image_file_global(class CVmImageWriter *image_writer)
3368
/* build our extra data buffer */
3369
oswp2(buf, (uint)get_prop());
3371
/* write the data */
3372
image_writer->write_gsym_entry(get_sym(), get_sym_len(),
3373
(int)TC_SYM_PROP, buf, 2);
3375
/* we wrote the symbol */
3380
* write the symbol to an image file's global symbol table
3382
int CTcSymEnum::write_to_image_file_global(class CVmImageWriter *image_writer)
3386
/* build our extra data buffer */
3387
oswp4(buf, get_enum_id());
3389
/* build our flags */
3394
/* write the data */
3395
image_writer->write_gsym_entry(get_sym(), get_sym_len(),
3396
(int)TC_SYM_ENUM, buf, 5);
3398
/* we wrote the symbol */
3403
* write the symbol to an image file's global symbol table
3405
int CTcSymMetaclass::
3406
write_to_image_file_global(class CVmImageWriter *image_writer)
3410
/* build our extra data buffer */
3411
oswp2(buf, meta_idx_);
3412
oswp4(buf + 2, class_obj_);
3414
/* write the data */
3415
image_writer->write_gsym_entry(get_sym(), get_sym_len(),
3416
(int)TC_SYM_METACLASS, buf, 6);
3418
/* we wrote the symbol */
3423
* Fix up the inheritance chain in the modifier objects
3425
void CTcSymMetaclass::fix_mod_obj_sc_list()
3428
CTcSymObj *obj_base;
3431
* go through our chain of modifier objects, and make sure the
3432
* stream data for each object points to its correct superclass
3434
for (obj = mod_obj_ ; obj != 0 ; obj = obj_base)
3438
/* get the correct data stream */
3439
str = obj->get_stream();
3441
/* get the base object for this symbol */
3442
obj_base = obj->get_mod_base_sym();
3445
* if there's no base object, there's no superclass entry to
3446
* adjust for this object
3452
* set the superclass in this object to point to this base
3455
CTPNStmObject::set_stream_sc(str, obj->get_stream_ofs(),
3456
0, obj_base->get_obj_id());
3461
* write the symbol to an image file's global symbol table
3463
int CTcSymBif::write_to_image_file_global(class CVmImageWriter *image_writer)
3467
/* build our extra data buffer */
3468
oswp2(buf, get_func_idx());
3469
oswp2(buf + 2, get_func_set_id());
3470
buf[4] = (has_retval() != 0);
3471
oswp2(buf + 5, get_min_argc());
3472
oswp2(buf + 7, get_max_argc());
3473
buf[9] = (is_varargs() != 0);
3475
/* write the data */
3476
image_writer->write_gsym_entry(get_sym(), get_sym_len(),
3477
(int)TC_SYM_BIF, buf, 10);
3479
/* we wrote the symbol */
3484
* write the symbol to an image file's global symbol table
3486
int CTcSymExtfn::write_to_image_file_global(class CVmImageWriter *iw)
3488
//$$$ to be implemented