cfad47cfa3/t3compiler/tads3/tct3img.cpp

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
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.  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.  
47
 */
48
static const char obj_file_sig[] = "TADS3.Object.000E\n\r\032";
49
50
51
/* ------------------------------------------------------------------------ */
52
/*
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.  
56
 */
57
void CTcGenTarg::write_to_object_file(CVmFile *fp, CTcMake *)
58
{
59
    ulong flags;
60
    
61
    /* write the signature */
62
    fp->write_bytes(obj_file_sig, sizeof(obj_file_sig) - 1);
63
64
    /* compute the object file flags */
65
    flags = 0;
66
    if (G_debug)
67
        flags |= TCT3_OBJHDR_DEBUG;
68
69
    /* write the flags */
70
    fp->write_int4(flags);
71
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_);
76
77
    /* 
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.  
84
     */
85
    fp->write_int4(next_obj_);
86
    fp->write_int4(next_prop_);
87
    fp->write_int4(G_prs->get_enum_count());
88
    
89
    /* write the function set dependency table */
90
    write_funcdep_to_object_file(fp);
91
92
    /* 
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) 
98
     */
99
    write_metadep_to_object_file(fp);
100
101
    /* write the global symbol table */
102
    G_prs->write_to_object_file(fp);
103
104
    /* write the main code stream and its fixup list */
105
    G_cs_main->write_to_object_file(fp);
106
107
    /* write the static code stream and its fixup list */
108
    G_cs_static->write_to_object_file(fp);
109
110
    /* write the data stream and its fixup list */
111
    G_ds->write_to_object_file(fp);
112
113
    /* write the object stream and its fixup list */
114
    G_os->write_to_object_file(fp);
115
116
    /* write the intrinsic class modifier stream */
117
    G_icmod_stream->write_to_object_file(fp);
118
119
    /* write the BigNumber stream and its fixup list */
120
    G_bignum_stream->write_to_object_file(fp);
121
122
    /* write the static initializer ID stream */
123
    G_static_init_id_stream->write_to_object_file(fp);
124
125
    /* write the object ID fixup list */
126
    CTcIdFixup::write_to_object_file(fp, G_objfixup);
127
128
    /* write the property ID fixup list */
129
    CTcIdFixup::write_to_object_file(fp, G_propfixup);
130
131
    /* write the enumerator ID fixup list */
132
    CTcIdFixup::write_to_object_file(fp, G_enumfixup);
133
134
    /* write debugging information if we're compiling for the debugger */
135
    if (G_debug)
136
    {
137
        tct3_debug_line_page *pg;
138
        
139
        /* write the source file list */
140
        write_sources_to_object_file(fp);
141
142
        /* 
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.  
148
         */
149
        fp->write_int4(debug_line_cnt_);
150
151
        /* now write the pointers, one page at a time */
152
        for (pg = debug_line_head_ ; pg != 0 ; pg = pg->nxt)
153
        {
154
            size_t pgcnt;
155
            
156
            /* 
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 
160
             */
161
            if (pg->nxt == 0)
162
                pgcnt = (size_t)(debug_line_cnt_ % TCT3_DEBUG_LINE_PAGE_SIZE);
163
            else
164
                pgcnt = TCT3_DEBUG_LINE_PAGE_SIZE;
165
166
            /* 
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.  
170
             */
171
            fp->write_bytes((char *)pg->line_ofs,
172
                            pgcnt * TCT3_DEBUG_LINE_REC_SIZE);
173
        }
174
175
        /* write the #define symbols */
176
        G_tok->write_macros_to_file_for_debug(fp);
177
    }
178
}
179
180
/* ------------------------------------------------------------------------ */
181
/*
182
 *   Write the function-set dependency table to an object file 
183
 */
184
void CTcGenTarg::write_funcdep_to_object_file(CVmFile *fp)
185
{
186
    tc_fnset_entry *cur;
187
188
    /* write the count */
189
    fp->write_int2(fnset_cnt_);
190
191
    /* write the entries */
192
    for (cur = fnset_head_ ; cur != 0 ; cur = cur->nxt)
193
    {
194
        size_t len;
195
196
        len = strlen(cur->nm);
197
        fp->write_int2(len);
198
        fp->write_bytes(cur->nm, len);
199
    }
200
}
201
202
/*
203
 *   Write the metaclass dependency table to an object file 
204
 */
205
void CTcGenTarg::write_metadep_to_object_file(CVmFile *fp)
206
{
207
    tc_meta_entry *cur;
208
209
    /* write the count */
210
    fp->write_int2(meta_cnt_);
211
212
    /* write the entries */
213
    for (cur = meta_head_ ; cur != 0 ; cur = cur->nxt)
214
    {
215
        size_t len;
216
217
        len = strlen(cur->nm);
218
        fp->write_int2(len);
219
        fp->write_bytes(cur->nm, len);
220
    }
221
}
222
223
224
/* ------------------------------------------------------------------------ */
225
/*
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).
233
 *   
234
 *   Returns zero on success; logs errors and returns non-zero on error.  
235
 */
236
int CTcGenTarg::load_object_file(CVmFile *fp, const textchar_t *fname)
237
{
238
    char buf[128];
239
    ulong obj_cnt;
240
    ulong prop_cnt;
241
    ulong enum_cnt;
242
    vm_obj_id_t *obj_xlat = 0;
243
    vm_prop_id_t *prop_xlat = 0;
244
    ulong *enum_xlat = 0;
245
    int err;
246
    ulong hdr_flags;
247
    ulong siz;
248
    ulong main_cs_start_ofs;
249
    ulong static_cs_start_ofs;
250
    
251
    /*
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.  
257
     */
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();
265
266
    /* 
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 
270
     */
271
    main_cs_start_ofs = G_cs_main->get_ofs();
272
    static_cs_start_ofs = G_cs_static->get_ofs();
273
    
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)
277
    {
278
        G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_INV_SIG);
279
        return 1;
280
    }
281
282
    /* read the file header flags */
283
    hdr_flags = fp->read_uint4();
284
285
    /*
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
289
     *   effect. 
290
     */
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);
294
295
    /*
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
299
     *   entire program. 
300
     */
301
302
    /* read and note the maximum string constant length */
303
    siz = fp->read_uint4();
304
    if (siz > max_str_len_)
305
        max_str_len_ = siz;
306
307
    /* read and note the maximum list size */
308
    siz = fp->read_uint4();
309
    if (siz > max_list_cnt_)
310
        max_list_cnt_ = siz;
311
    
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;
316
317
    /*
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 
321
     */
322
    obj_cnt = fp->read_uint4();
323
    prop_cnt = fp->read_uint4();
324
    enum_cnt = fp->read_uint4();
325
326
    /*
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
333
     *   prop_cnt.
334
     *   
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.  
344
     */
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)
348
    {
349
        G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_TOO_MANY_IDS);
350
        return 2;
351
    }
352
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])));
360
361
    /* check to make sure we got the memory */
362
    if (obj_xlat == 0 || prop_xlat == 0 || enum_xlat == 0)
363
    {
364
        /* log an error and return failure */
365
        G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_OUT_OF_MEM);
366
        err = 3;
367
        goto done;
368
    }
369
370
    /* 
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.
375
     */
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])));
379
380
    /* read the function set dependency table */
381
    load_funcdep_from_object_file(fp, fname);
382
383
    /* read the metaclass dependency table */
384
    load_metadep_from_object_file(fp, fname);
385
386
    /* 
387
     *   Read the symbol table.  This will create translation entries for
388
     *   the object and property names found in the symbol table. 
389
     */
390
    if ((err = G_prs->load_object_file(fp, fname, obj_xlat, prop_xlat,
391
                                       enum_xlat)) != 0)
392
    {
393
        /* that failed - abort the load */
394
        goto done;
395
    }
396
397
    /* read the main code stream and its fixup list */
398
    G_cs_main->load_object_file(fp, fname);
399
400
    /* read the static code stream and its fixup list */
401
    G_cs_static->load_object_file(fp, fname);
402
403
    /* read the data stream and its fixup list */
404
    G_ds->load_object_file(fp, fname);
405
406
    /* read the object data stream and its fixup list */
407
    G_os->load_object_file(fp, fname);
408
409
    /* read the intrinsic class modifier stream */
410
    G_icmod_stream->load_object_file(fp, fname);
411
412
    /* read the BigNumber stream and its fixup list */
413
    G_bignum_stream->load_object_file(fp, fname);
414
415
    /* read the static initializer ID stream */
416
    G_static_init_id_stream->load_object_file(fp, fname);
417
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);
421
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);
425
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);
429
430
    /* if the object file contains debugging information, read that */
431
    if ((hdr_flags & TCT3_OBJHDR_DEBUG) != 0)
432
    {
433
        /* load the debug records */
434
        load_debug_records_from_object_file(fp, fname,
435
                                            main_cs_start_ofs,
436
                                            static_cs_start_ofs);
437
    }
438
439
done:
440
    /* 
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 
445
     */
446
    if (obj_xlat != 0)
447
        t3free(obj_xlat);
448
    if (prop_xlat != 0)
449
        t3free(prop_xlat);
450
    if (enum_xlat != 0)
451
        t3free(enum_xlat);
452
453
    /* return the result */
454
    return err;
455
}
456
457
458
/* ------------------------------------------------------------------------ */
459
/*
460
 *   Error handler for CTcTokenizer::load_macros_from_file() 
461
 */
462
class MyLoadMacErr: public CTcTokLoadMacErr
463
{
464
public:
465
    MyLoadMacErr(const char *fname) { fname_ = fname; }
466
467
    /* log an error */
468
    virtual void log_error(int err)
469
    {
470
        /* check the error code */
471
        switch(err)
472
        {
473
        case 1:
474
        case 2:
475
            G_tcmain->log_error(0, 0, TC_SEV_ERROR,
476
                                TCERR_OBJFILE_MACRO_SYM_TOO_LONG, fname_);
477
            break;
478
        }
479
    }
480
481
private:
482
    /* the name of the object file we're loading */
483
    const char *fname_;
484
};
485
486
/* ------------------------------------------------------------------------ */
487
/*
488
 *   Load the debug records from an object file 
489
 */
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)
493
{
494
    int first_filedesc;
495
    ulong line_table_cnt;
496
497
    /* 
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.  
503
     */
504
    first_filedesc = G_tok->get_next_filedesc_index();
505
        
506
    /* read the source file list */
507
    read_sources_from_object_file(fp);
508
509
    /*
510
     *   Read the line record pointers.  For each line record table, we
511
     *   must fix up the line records to reflect the file descriptor
512
     *   numbering system.  
513
     */
514
    for (line_table_cnt = fp->read_uint4() ; line_table_cnt != 0 ;
515
         --line_table_cnt)
516
    {
517
        uchar stream_id;
518
        ulong ofs;
519
        CTcCodeStream *cs;
520
        ulong start_ofs;
521
522
        /* read the stream ID */
523
        stream_id = fp->read_byte();
524
525
        /* find the appropriate code stream */
526
        cs = (CTcCodeStream *)
527
             CTcDataStream::get_stream_from_id(stream_id, fname);
528
529
        /* get the appropriate starting offset */
530
        start_ofs = (cs == G_cs_main ? main_cs_start_ofs
531
                                     : static_cs_start_ofs);
532
        
533
        /* 
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.  
537
         */
538
        ofs = fp->read_uint4() + start_ofs;
539
        
540
        /* update this table */
541
        fix_up_debug_line_table(cs, ofs, first_filedesc);
542
    }
543
544
    /* read the macro definitions */
545
    CVmFileStream str(fp);
546
    MyLoadMacErr err_handler(fname);
547
    G_tok->load_macros_from_file(&str, &err_handler);
548
}
549
550
/*
551
 *   Fix up a debug line record table for the current object file
552
 */
553
void CTcGenTarg::fix_up_debug_line_table(CTcCodeStream *cs,
554
                                         ulong line_table_ofs,
555
                                         int first_filedesc)
556
{
557
    uint cnt;
558
    ulong ofs;
559
    
560
    /* read the number of line records in the table */
561
    cnt = cs->readu2_at(line_table_ofs);
562
563
    /* adjust each entry */
564
    for (ofs = line_table_ofs + 2 ; cnt != 0 ;
565
         --cnt, ofs += TCT3_LINE_ENTRY_SIZE)
566
    {
567
        uint filedesc;
568
        
569
        /* read the old file descriptor ID */
570
        filedesc = cs->readu2_at(ofs + 2);
571
572
        /* adjust it to the new numbering system */
573
        filedesc += first_filedesc;
574
575
        /* write it back */
576
        cs->write2_at(ofs + 2, filedesc);
577
    }
578
}
579
580
/* ------------------------------------------------------------------------ */
581
/*
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
585
 *   loaded exactly. 
586
 */
587
void CTcGenTarg::load_funcdep_from_object_file(class CVmFile *fp,
588
                                               const textchar_t *fname)
589
{
590
    int cnt;
591
    tc_fnset_entry *cur;
592
593
    /* read the count */
594
    cnt = fp->read_int2();
595
596
    /* read the entries */
597
    for (cur = fnset_head_ ; cnt != 0 ; --cnt)
598
    {
599
        char buf[128];
600
        size_t len;
601
        
602
        /* read this entry */
603
        len = fp->read_uint2();
604
        if (len + 1 > sizeof(buf))
605
        {
606
            G_tcmain->log_error(0, 0, TC_SEV_ERROR,
607
                                TCERR_OBJFILE_INV_FN_OR_META, fname);
608
            return;
609
        }
610
611
        /* read the name and null-terminate it */
612
        fp->read_bytes(buf, len);
613
        buf[len] = '\0';
614
615
        /* 
616
         *   if we are still scanning existing entries, make sure it
617
         *   matches; otherwise, add it 
618
         */
619
        if (cur != 0)
620
        {
621
            const char *vsn;
622
            char *ent_vsn;
623
            size_t name_len;
624
            size_t ent_name_len;
625
626
            /* get the version suffixes, if any */
627
            vsn = lib_find_vsn_suffix(buf, '/', 0, &name_len);
628
            ent_vsn = (char *)
629
                      lib_find_vsn_suffix(cur->nm, '/', 0, &ent_name_len);
630
            
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,
636
                                    buf, fname);
637
638
            /* 
639
             *   if the new version string is higher than the old version
640
             *   string, keep the new version string 
641
             */
642
            if (vsn != 0 && ent_vsn != 0 && strcmp(vsn, ent_vsn) > 0
643
                && strlen(vsn) <= strlen(ent_vsn))
644
            {
645
                /* 
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
652
                 *   newer version) 
653
                 */
654
                strcpy(ent_vsn, vsn);
655
            }
656
657
            /* move on to the next one */
658
            cur = cur->nxt;
659
        }
660
        else
661
        {
662
            /* we're past the existing list - add the new function set */
663
            add_fnset(buf, len);
664
        }
665
    }
666
}
667
668
/*
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
672
 *   loaded exactly.  
673
 */
674
void CTcGenTarg::load_metadep_from_object_file(class CVmFile *fp,
675
                                               const textchar_t *fname)
676
{
677
    int cnt;
678
    tc_meta_entry *cur;
679
680
    /* read the count */
681
    cnt = fp->read_int2();
682
683
    /* read the entries */
684
    for (cur = meta_head_ ; cnt != 0 ; --cnt)
685
    {
686
        char buf[128];
687
        size_t len;
688
689
        /* read this entry */
690
        len = fp->read_uint2();
691
        if (len + 1 > sizeof(buf))
692
        {
693
            G_tcmain->log_error(0, 0, TC_SEV_ERROR,
694
                                TCERR_OBJFILE_INV_FN_OR_META, fname);
695
            return;
696
        }
697
698
        /* read the name and null-terminate it */
699
        fp->read_bytes(buf, len);
700
        buf[len] = '\0';
701
702
        /* 
703
         *   if we are still scanning existing entries, make sure it
704
         *   matches; otherwise, add it 
705
         */
706
        if (cur != 0)
707
        {
708
            const char *vsn;
709
            char *ent_vsn;
710
            size_t name_len;
711
            size_t ent_name_len;
712
713
            /* find the version suffix, if any */
714
            vsn = lib_find_vsn_suffix(buf, '/', 0, &name_len);
715
716
            /* find the version suffix in this entry's name */
717
            ent_vsn = (char *)
718
                      lib_find_vsn_suffix(cur->nm, '/', 0, &ent_name_len);
719
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)
723
            {
724
                /* log a mis-matched metaclass error */
725
                G_tcmain->log_error(0, 0, TC_SEV_ERROR,
726
                                    TCERR_OBJFILE_META_CONFLICT, buf, fname);
727
            }
728
729
            /* 
730
             *   if the new version string is higher than the old version
731
             *   string, keep the new version string 
732
             */
733
            if (vsn != 0 && ent_vsn != 0 && strcmp(vsn, ent_vsn) > 0
734
                && strlen(vsn) <= strlen(ent_vsn))
735
            {
736
                /* 
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
743
                 *   newer version) 
744
                 */
745
                strcpy(ent_vsn, vsn);
746
            }
747
                
748
            /* move on to the next one */
749
            cur = cur->nxt;
750
        }
751
        else
752
        {
753
            /* we're past the existing list - add the new metaclass */
754
            add_meta(buf, len, 0);
755
        }
756
    }
757
}
758
759
760
/* ------------------------------------------------------------------------ */
761
/*
762
 *   Write the source file list to an object file 
763
 */
764
void CTcGenTarg::write_sources_to_object_file(CVmFile *fp)
765
{
766
    CTcTokFileDesc *desc;
767
768
    /* write the number of entries */
769
    fp->write_int2(G_tok->get_filedesc_count());
770
771
    /* write the entries */
772
    for (desc = G_tok->get_first_filedesc() ; desc != 0 ;
773
         desc = desc->get_next())
774
    {
775
        size_t len;
776
        const char *fname;
777
778
        /* get the filename - use the resolved local filename */
779
        fname = desc->get_fname();
780
781
        /* write the length of the filename */
782
        len = strlen(fname);
783
        fp->write_int2(len);
784
785
        /* write the filename */
786
        fp->write_bytes(fname, len);
787
    }
788
}
789
790
/*
791
 *   Read a source file list from an object file 
792
 */
793
void CTcGenTarg::read_sources_from_object_file(CVmFile *fp)
794
{
795
    uint cnt;
796
    uint i;
797
798
    /* read the number of entries */
799
    cnt = fp->read_uint2();
800
801
    /* read the entries */
802
    for (i = 0 ; i < cnt ; ++i)
803
    {
804
        size_t len;
805
        char fname[OSFNMAX];
806
807
        /* read the length of the entry */
808
        len = fp->read_uint2();
809
810
        /* see if it fits in our buffer */
811
        if (len <= sizeof(fname))
812
        {
813
            /* read it */
814
            fp->read_bytes(fname, len);
815
        }
816
        else
817
        {
818
            /* it's too long - truncate to the buffer size */
819
            fp->read_bytes(fname, sizeof(fname));
820
821
            /* skip the rest */
822
            fp->set_pos(fp->get_pos() + len - sizeof(fname));
823
824
            /* note the truncated length */
825
            len = sizeof(fname);
826
        }
827
828
        /* 
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).  
839
         */
840
        G_tok->create_file_desc(fname, len);
841
    }
842
}
843
844
/* ------------------------------------------------------------------------ */
845
/*
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.  
849
 */
850
void CTcGenTarg::calc_pool_layouts(size_t *first_static_page)
851
{
852
    size_t max_str;
853
    size_t max_list;
854
    size_t max_item;
855
856
    /*
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.
862
     *   
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
870
     *   page).
871
     *   
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
876
     *   it and try again.  
877
     */
878
879
    /* 
880
     *   find the length in bytes of the longest string - we require the
881
     *   length prefix in addition to the bytes of the string 
882
     */
883
    max_str = max_str_len_ + VMB_LEN;
884
885
    /* 
886
     *   find the length in bytes of the longest list - we require one
887
     *   data holder per element, plus the length prefix 
888
     */
889
    max_list = (max_list_cnt_ * VMB_DATAHOLDER) + VMB_LEN;
890
891
    /* get the larger of the two - this will be our minimum size */
892
    max_item = max_str;
893
    if (max_list > max_item)
894
        max_item = max_list;
895
896
    /* 
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 
901
     */
902
    if (max_item < 16*1024)
903
        max_item <<= 1;
904
905
    /* calculate the constant pool layout */
906
    const_layout_.calc_layout(G_ds, max_item, TRUE);
907
908
    /* calculate the main code pool layout */
909
    code_layout_.calc_layout(G_cs_main, max_bytecode_len_, TRUE);
910
911
    /* note the number of pages of regular code */
912
    *first_static_page = code_layout_.page_cnt_;
913
914
    /* 
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 
917
     */
918
    code_layout_.calc_layout(G_cs_static, max_bytecode_len_, FALSE);
919
}
920
921
922
/* ------------------------------------------------------------------------ */
923
/*
924
 *   Write the image file
925
 */
926
void CTcGenTarg::write_to_image(CVmFile *fp, uchar data_xor_mask,
927
                                const char tool_data[4])
928
{
929
    tc_meta_entry *meta;
930
    CTcSymbol *sym;
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;
937
    int bignum_idx;
938
    int int_class_idx;
939
    CTcPrsExport *exp;
940
    CTcDataStream *cs_list[2];
941
    size_t first_static_code_page;
942
943
    /* 
944
     *   if we have any BigNumber data, get the BigNumber metaclass index
945
     *   (or define it, if the program didn't do so itself) 
946
     */
947
    if (G_bignum_stream->get_ofs() != 0)
948
        bignum_idx = find_or_add_meta("bignumber", 9, 0);
949
950
    /* apply internal object/property ID fixups in the symbol table */
951
    G_prs->apply_internal_fixups();
952
953
    /* build the grammar productions */
954
    G_prs->build_grammar_productions();
955
956
    /* 
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.  
963
     */
964
    G_prs->build_dictionaries();
965
966
    /* 
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
970
     *   initializers. 
971
     */
972
    build_multimethod_initializers();
973
974
    /* make sure the the IntrinsicClass intrinsic class is itself defined */
975
    int_class_idx = find_or_add_meta("intrinsic-class", 15, 0);
976
977
    /* build the IntrinsicClass objects */
978
    build_intrinsic_class_objs(G_int_class_stream);
979
980
    /* calculate the final pool layouts */
981
    calc_pool_layouts(&first_static_code_page);
982
983
    /* build the source line location maps, if debugging */
984
    if (G_debug)
985
        build_source_line_maps();
986
987
    /* look up the "_main" symbol in the global symbol table */
988
    sym = G_prs->get_global_symtab()->find("_main");
989
990
    /* 
991
     *   if there's no "_main" symbol, or it's not a function, it's an
992
     *   error 
993
     */
994
    if (sym == 0)
995
    {
996
        /* "_main" isn't defined - log an error and abort */
997
        G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MAIN_NOT_DEFINED);
998
        return;
999
    }
1000
    else if (sym->get_type() != TC_SYM_FUNC)
1001
    {
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);
1004
        return;
1005
    }
1006
    else
1007
    {
1008
        /* 
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.  
1014
         */
1015
        main_ofs = ((CTcSymFunc *)sym)->get_code_pool_addr();
1016
    }
1017
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();
1022
1023
    /* create our image writer */
1024
    image_writer = new CVmImageWriter(fp);
1025
1026
    /* prepare the image file - use file format version 1 */
1027
    image_writer->prepare(1, tool_data);
1028
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,
1033
                                TCT3_DBG_FMT_VSN);
1034
1035
    /* begin writing the symbolic items */
1036
    image_writer->begin_sym_block();
1037
1038
    /* run through the list of exports in the parser */
1039
    for (exp = G_prs->get_exp_head() ; exp != 0 ; exp = exp->get_next())
1040
    {
1041
        CTcPrsExport *exp2;
1042
        int dup_err_cnt;
1043
        
1044
        /* 
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 
1048
         */
1049
        if (exp->get_ext_name() == 0)
1050
            continue;
1051
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"))
1057
        {
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());
1063
        }
1064
            
1065
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);
1069
1070
        /*
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
1073
         *   an error. 
1074
         */
1075
        for (dup_err_cnt = 0, exp2 = exp->get_next() ; exp2 != 0 ;
1076
             exp2 = exp2->get_next())
1077
        {
1078
            /* if this one has already been marked as a duplicate, skip it */
1079
            if (exp2->get_ext_name() == 0)
1080
                continue;
1081
            
1082
            /* check for a match of the external name */
1083
            if (exp->ext_name_matches(exp2))
1084
            {
1085
                /* 
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.
1091
                 */
1092
                if (!exp->sym_matches(exp2))
1093
                {
1094
                    /* 
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.
1098
                     */
1099
                    ++dup_err_cnt;
1100
                    if (dup_err_cnt == 1)
1101
                    {
1102
                        /* it's the first error - show the long form */
1103
                        G_tcmain->log_error(0, 0, TC_SEV_ERROR,
1104
                                            TCERR_DUP_EXPORT,
1105
                                            (int)exp->get_ext_len(),
1106
                                            exp->get_ext_name(),
1107
                                            (int)exp->get_sym_len(),
1108
                                            exp->get_sym(),
1109
                                            (int)exp2->get_sym_len(),
1110
                                            exp2->get_sym());
1111
                    }
1112
                    else
1113
                    {
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(),
1120
                                            exp2->get_sym());
1121
                    }
1122
                }
1123
1124
                /* 
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
1128
                 *   once. 
1129
                 */
1130
                exp2->set_extern_name(0, 0);
1131
            }
1132
        }
1133
1134
        /* write it out according to its type */
1135
        switch(sym->get_type())
1136
        {
1137
        case TC_SYM_OBJ:
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());
1142
            break;
1143
1144
        case TC_SYM_PROP:
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());
1149
            break;
1150
1151
        case TC_SYM_FUNC:
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());
1156
            break;
1157
1158
        default:
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());
1163
            break;
1164
        }
1165
    }
1166
1167
    /* 
1168
     *   write the last property ID - this is a special synthetic export
1169
     *   that we provide automatically 
1170
     */
1171
    image_writer->write_sym_item_propid("LastProp", next_prop_);
1172
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);
1178
1179
    /* 
1180
     *   write the special property ID for calling properties of anonymous
1181
     *   function objects 
1182
     */
1183
    if (objcall_prop != VM_INVALID_PROP)
1184
        image_writer->write_sym_item_propid("ObjectCallProp", objcall_prop);
1185
1186
    /* done with the symbolic names */
1187
    image_writer->end_sym_block();
1188
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();
1194
1195
    /* start the metaclass dependency table */
1196
    image_writer->begin_meta_dep(meta_cnt_);
1197
1198
    /* write the metaclass dependency items */
1199
    for (meta = meta_head_ ; meta != 0 ; meta = meta->nxt)
1200
    {
1201
        /* write the dependency item */
1202
        image_writer->write_meta_dep_item(meta->nm);
1203
1204
        /* if there's an associated symbol, write the property list */
1205
        if (meta->sym != 0)
1206
        {
1207
            CTcSymMetaProp *prop;
1208
1209
            /* scan the list of properties and write each one */
1210
            for (prop = meta->sym->get_prop_head() ; prop != 0 ;
1211
                 prop = prop->nxt_)
1212
            {
1213
                /* write this item's property */
1214
                image_writer->write_meta_item_prop(prop->prop_->get_prop());
1215
            }
1216
        }
1217
    }
1218
1219
    /* end the metaclass dependency table */
1220
    image_writer->end_meta_dep();
1221
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);
1226
1227
    /* 
1228
     *   write the constant pool (applying the constant pool data mask to
1229
     *   obscure any text strings in the data) 
1230
     */
1231
    const_layout_.write_to_image(&G_ds, 1, image_writer, 2, data_xor_mask);
1232
1233
    /* write the "TADS object" data */
1234
    write_tads_objects_to_image(G_os, image_writer, TCT3_METAID_TADSOBJ);
1235
1236
    /* write the intrinsic class modifier object data */
1237
    write_tads_objects_to_image(G_icmod_stream, image_writer,
1238
                                TCT3_METAID_ICMOD);
1239
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);
1243
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);
1247
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);
1252
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);
1257
1258
    /* write the static initializer list */
1259
    write_static_init_list(image_writer,
1260
                           first_static_code_page * code_layout_.page_size_);
1261
1262
    /* write debug information if desired */
1263
    if (G_debug)
1264
    {
1265
        /* write the source file table */
1266
        write_sources_to_image(image_writer);
1267
1268
        /* write the global symbol table to the image file */
1269
        write_global_symbols_to_image(image_writer);
1270
1271
        /* write the method header list */
1272
        write_method_list_to_image(image_writer);
1273
1274
        /* write the macro records */
1275
        write_macros_to_image(image_writer);
1276
    }
1277
1278
    /* finish the image file */
1279
    image_writer->finish();
1280
1281
    /* delete our image writer */
1282
    delete image_writer;
1283
    image_writer = 0;
1284
}
1285
1286
/* ------------------------------------------------------------------------ */
1287
/*
1288
 *   Write the static initializer ID list 
1289
 */
1290
void CTcGenTarg::write_static_init_list(CVmImageWriter *image_writer,
1291
                                        ulong main_cs_size)
1292
{
1293
    ulong rem;
1294
    ulong ofs;
1295
    ulong init_cnt;
1296
1297
    /* 
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) 
1301
     */
1302
    init_cnt = G_static_init_id_stream->get_ofs() / 6;
1303
1304
    /* add the multi-method initializer object, if there is one */
1305
    if (mminit_obj_ != VM_INVALID_OBJ)
1306
        init_cnt += 1;
1307
    
1308
    /* start the block */
1309
    image_writer->begin_sini_block(main_cs_size, init_cnt);
1310
1311
    /* write the multi-method initializer object, if applicable */
1312
    if (mminit_obj_ != VM_INVALID_OBJ)
1313
    {
1314
        /* write the object data */
1315
        char buf[6];
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);
1319
    }
1320
1321
    /* write the bytes */
1322
    for (ofs = 0, rem = G_static_init_id_stream->get_ofs() ; rem != 0 ; )
1323
    {
1324
        const char *ptr;
1325
        ulong cur;
1326
        
1327
        /* get the next chunk */
1328
        ptr = G_static_init_id_stream->get_block_ptr(ofs, rem, &cur);
1329
1330
        /* write this chunk */
1331
        image_writer->write_bytes(ptr, cur);
1332
1333
        /* advance past this chunk */
1334
        ofs += cur;
1335
        rem -= cur;
1336
    }
1337
1338
    /* end the block */
1339
    image_writer->end_sini_block();
1340
}
1341
1342
/* ------------------------------------------------------------------------ */
1343
/*
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.  
1348
 */
1349
struct mmstub_ctx
1350
{
1351
    mmstub_ctx()
1352
    {
1353
        mmc = 0;
1354
        cnt = 0;
1355
    }
1356
    
1357
    /* _multiMethodCall function symbol */
1358
    CTcSymFunc *mmc;
1359
1360
    /* number of multi-method stubs we generated */
1361
    int cnt;
1362
};
1363
1364
void CTcGenTarg::build_synthesized_code()
1365
{
1366
    mmstub_ctx ctx;
1367
    
1368
    /* look up the _multiMethodCall function */
1369
    ctx.mmc = (CTcSymFunc *)G_prs->get_global_symtab()->find(
1370
        "_multiMethodCall", 16);
1371
1372
    /* 
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 
1376
     */
1377
    G_cs_static->set_object_file_start_ofs();
1378
    G_os->set_object_file_start_ofs();
1379
1380
    /* build out the stubs for the multi-method base functions */
1381
    G_prs->get_global_symtab()->enum_entries(&multimethod_stub_cb, &ctx);
1382
1383
    /* 
1384
     *   if we generated any stubs, we definitely need _multiMethodCall to be
1385
     *   defined - if it's not, it's an error 
1386
     */
1387
    if (ctx.cnt != 0 && (ctx.mmc == 0 || ctx.mmc->get_type() != TC_SYM_FUNC))
1388
    {
1389
        G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MISSING_MMREG,
1390
                            "_multiMethodCall");
1391
    }
1392
}
1393
1394
/* callback context - build multi-method base function stubs */
1395
void CTcGenTarg::multimethod_stub_cb(void *ctx0, CTcSymbol *sym)
1396
{
1397
    mmstub_ctx *ctx = (mmstub_ctx *)ctx0;
1398
1399
    /* if this is a function, check to see if it's a multi-method stub */
1400
    if (sym->get_type() == TC_SYM_FUNC)
1401
    {
1402
        CTcSymFunc *fsym = (CTcSymFunc *)sym;
1403
1404
        /* 
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.)  
1408
         */
1409
        if (fsym->is_multimethod())
1410
        {
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) ;
1415
            if (rem == 0)
1416
            {
1417
                tct3_method_gen_ctx gen_ctx;
1418
1419
                /* 
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
1424
                 *   function.  
1425
                 */
1426
                G_cg->open_method(G_cs_main,
1427
                                  fsym, fsym->get_fixup_list_anchor(),
1428
                                  0, 0, 0, TRUE, FALSE, FALSE, &gen_ctx);
1429
1430
                /* set the anchor in the function symbol */
1431
                fsym->set_anchor(gen_ctx.anchor);
1432
1433
                /* 
1434
                 *   turn the arguments into a list, leaving this on the
1435
                 *   stack as the second argument for _multiMethodCall 
1436
                 */
1437
                G_cg->write_op(OPC_PUSHPARLST);
1438
                G_cs->write(0);
1439
                G_cg->note_push();
1440
1441
                /* push the function address argument */
1442
                CTcConstVal funcval;
1443
                funcval.set_funcptr(fsym);
1444
                CTPNConst cfunc(&funcval);
1445
                cfunc.gen_code(FALSE, FALSE);
1446
                G_cg->note_push();
1447
1448
                /* 
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) 
1452
                 */
1453
                if (ctx->mmc != 0)
1454
                    ctx->mmc->gen_code_call(FALSE, 2, FALSE);
1455
1456
                /* return the result */
1457
                G_cg->write_op(OPC_RETVAL);
1458
                G_cg->note_pop();
1459
1460
                /* finish the method */
1461
                G_cg->close_method(0, 0, 0, &gen_ctx);
1462
                G_cg->close_method_cleanup(&gen_ctx);
1463
1464
                /* the stub symbol now has a definition */
1465
                fsym->set_extern(FALSE);
1466
1467
                /* count it */
1468
                ctx->cnt += 1;
1469
            }
1470
        }
1471
    }
1472
}
1473
1474
/* ------------------------------------------------------------------------ */
1475
/*
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.  
1480
 */
1481
void CTcGenTarg::open_tadsobj(tct3_tadsobj_ctx *ctx,
1482
                              CTcDataStream *stream,
1483
                              vm_obj_id_t obj_id,
1484
                              int sc_cnt, int prop_cnt,
1485
                              unsigned int internal_flags,
1486
                              unsigned int vm_flags)
1487
{
1488
    /* remember the stream in the context */
1489
    ctx->stream = stream;
1490
1491
    /* write the internal header */
1492
    stream->write2(internal_flags);
1493
    
1494
    /* note the start of the VM object data */
1495
    ctx->obj_ofs = stream->get_ofs();
1496
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 */
1503
}
1504
1505
/*
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
1508
 *   actual data size. 
1509
 */
1510
void CTcGenTarg::close_tadsobj(tct3_tadsobj_ctx *ctx)
1511
{
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);
1515
}
1516
1517
1518
/* ------------------------------------------------------------------------ */
1519
/*
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.
1523
 *   
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
1528
 *   later linking.  
1529
 */
1530
void CTcGenTarg::linker_ensure_mod_obj(CTcSymMetaclass *mc)
1531
{
1532
    /* if there's no modifier object, create one */
1533
    if (mc->get_mod_obj() == 0)
1534
    {
1535
        /* create a modifier object */
1536
        CTcSymObj *mod_sym = CTcSymObj::synthesize_modified_obj_sym(FALSE);
1537
1538
        /* set it to be an IntrinsicClassModifier object */
1539
        mod_sym->set_metaclass(TC_META_ICMOD);
1540
1541
        /* link the modifier to the metaclass */
1542
        mc->set_mod_obj(mod_sym);
1543
        
1544
        /* 
1545
         *   generate the object data - this is simply an empty object with
1546
         *   no superclasses, and it goes in the intrinsic class modifier
1547
         *   stream 
1548
         */
1549
        tct3_tadsobj_ctx obj_ctx;
1550
        G_cg->open_tadsobj(
1551
            &obj_ctx, G_icmod_stream,
1552
            mod_sym->get_obj_id(), 0, 0, 0, 0);
1553
        G_cg->close_tadsobj(&obj_ctx);
1554
    }
1555
}
1556
1557
/*
1558
 *   Ensure that the given intrinsic class has a modifier object, by name. 
1559
 */
1560
void CTcGenTarg::linker_ensure_mod_obj(const char *name, size_t len)
1561
{
1562
    /* look up the symbol */
1563
    CTcSymMetaclass *mc = (CTcSymMetaclass *)G_prs->get_global_symtab()
1564
                          ->find(name, len);
1565
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);
1569
}
1570
1571
1572
/* ------------------------------------------------------------------------ */
1573
/*
1574
 *   Build the multi-method initializers 
1575
 */
1576
1577
/* enumerator callback context */
1578
struct mminit_ctx
1579
{
1580
    mminit_ctx()
1581
    {
1582
        mmr = 0;
1583
        cnt = 0;
1584
    }
1585
    
1586
    /* _multiMethodRegister function symbol */
1587
    CTcSymFunc *mmr;
1588
1589
    /* number of multi-method registrations we generated */
1590
    int cnt;
1591
};
1592
1593
/* main initializer builder */
1594
void CTcGenTarg::build_multimethod_initializers()
1595
{
1596
    tct3_method_gen_ctx genctx;
1597
    mminit_ctx ctx;
1598
1599
    /* look up the _multiMethodRegister function */
1600
    ctx.mmr = (CTcSymFunc *)G_prs->get_global_symtab()->find(
1601
        "_multiMethodRegister", 20);
1602
1603
    /* 
1604
     *   open the method - it's a static initializer, so write it to the
1605
     *   static stream 
1606
     */
1607
    G_cg->open_method(G_cs_static, 0, 0, 0, 0, 0, FALSE, FALSE, FALSE,
1608
                      &genctx);
1609
1610
    /* scan the symbol table for multimethods and generate initializers */
1611
    G_prs->get_global_symtab()->enum_entries(&multimethod_init_cb, &ctx);
1612
1613
    /* 
1614
     *   if we found any multi-methods, generate a call to
1615
     *   _multiMethodBuildBindings 
1616
     */
1617
    if (ctx.cnt != 0)
1618
    {
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)
1623
        {
1624
            G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MISSING_MMREG,
1625
                                "_multiMethodBuildBindings");
1626
            return;
1627
        }
1628
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 */
1634
    }
1635
1636
    /* close the method and clean up */
1637
    G_cg->close_method(0, 0, 0, &genctx);
1638
    G_cg->close_method_cleanup(&genctx);
1639
1640
    /* 
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 
1644
     */
1645
    if (ctx.cnt != 0)
1646
    {
1647
        /* we have multi-methods, so we definitely need _multiMethodRegister */
1648
        if (ctx.mmr == 0 || ctx.mmr->get_type() != TC_SYM_FUNC)
1649
        {
1650
            G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MISSING_MMREG,
1651
                                "_multiMethodRegister");
1652
            return;
1653
        }
1654
1655
        /* create an anonymous object to hold the initializer code */
1656
        mminit_obj_ = G_cg->new_obj_id();
1657
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);
1661
        
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 */
1669
        
1670
        /* fix up the object size data */
1671
        close_tadsobj(&obj_ctx);
1672
    }
1673
1674
    /* switch back to the main stream */
1675
    G_cs = G_cs_main;
1676
}
1677
1678
/* callback context - build multi-method registration calls */
1679
void CTcGenTarg::multimethod_init_cb(void *ctx0, CTcSymbol *sym)
1680
{
1681
    mminit_ctx *ctx = (mminit_ctx *)ctx0;
1682
    
1683
    /* if this is a function, check to see if it's a multi-method instance */
1684
    if (sym->get_type() == TC_SYM_FUNC)
1685
    {
1686
        CTcSymFunc *fsym = (CTcSymFunc *)sym;
1687
        
1688
        /* 
1689
         *   multi-method instances have names of the form
1690
         *   "name*type1,type2", so check the name to see if it fits the
1691
         *   pattern 
1692
         */
1693
        const char *p = sym->getstr();
1694
        size_t rem = sym->getlen();
1695
        int is_mm = FALSE;
1696
        for ( ; rem != 0 ; ++p, --rem)
1697
        {
1698
            /* 
1699
             *   if we found a '*', it's a multimethod; otherwise, if it's
1700
             *   anything other than a symbol character, it's not a
1701
             *   multimethod 
1702
             */
1703
            if (*p == '*')
1704
            {
1705
                is_mm = TRUE;
1706
                break;
1707
            }
1708
            else if (!is_sym(*p))
1709
                break;
1710
        }
1711
1712
        /* 
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
1715
         *   function. 
1716
         */
1717
        if (is_mm)
1718
        {
1719
            int argc;
1720
            
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);
1724
1725
            /* look up the base function symbol */
1726
            CTcSymFunc *base_sym = (CTcSymFunc *)G_prs->get_global_symtab()
1727
                                   ->find(funcname, funclen);
1728
1729
            /* if it's not defined as a function, ignore it */
1730
            if (base_sym == 0 || base_sym->get_type() != TC_SYM_FUNC)
1731
                return;
1732
1733
            /* 
1734
             *   skip to the end of the string, and remove the '*' from the
1735
             *   length count 
1736
             */
1737
            p += rem;
1738
            --rem;
1739
1740
            /* 
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.  
1744
             */
1745
            for (argc = 0 ; rem != 0 ; ++argc)
1746
            {
1747
                /* remember where the current name starts */
1748
                size_t plen;
1749
1750
                /* skip the terminator for this item */
1751
                --p, --rem;
1752
1753
                /* scan backwards to the previous delimiter */
1754
                for (plen = 0 ; rem != 0 && *(p-1) != ';' ;
1755
                     --p, --rem, ++plen) ;
1756
1757
                /* look up this name */
1758
                if (plen == 0)
1759
                {
1760
                    /* 
1761
                     *   empty name - this slot accepts any type; represent
1762
                     *   this in the run-time formal list with 'nil' 
1763
                     */
1764
                    G_cg->write_op(OPC_PUSHNIL);
1765
1766
                    /* 
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
1770
                     *   modifier object. 
1771
                     */
1772
                    G_cg->linker_ensure_mod_obj("Object", 6);
1773
                }
1774
                else if (plen == 3 && memcmp(p, "...", 3) == 0)
1775
                {
1776
                    /* 
1777
                     *   varargs indicator - represent this in the list with
1778
                     *   the literal string '...' 
1779
                     */
1780
                    CTcConstVal val;
1781
                    val.set_sstr("...", 3);
1782
                    CTPNConst cval(&val);
1783
                    cval.gen_code(FALSE, FALSE);
1784
1785
                    /* 
1786
                     *   a varargs slot is implicitly an Object slot, so make
1787
                     *   sure Object has a modifier object
1788
                     */
1789
                    G_cg->linker_ensure_mod_obj("Object", 6);
1790
                }
1791
                else
1792
                {
1793
                    /* class name - look it up */
1794
                    CTcSymbol *cl = G_prs->get_global_symtab()->find(p, plen);
1795
                    CTcConstVal val;
1796
1797
                    /* 
1798
                     *   if it's missing, unresolved, or not an object, flag
1799
                     *   an error 
1800
                     */
1801
                    if (cl == 0
1802
                        || (cl->get_type() == TC_SYM_OBJ
1803
                            && ((CTcSymObj *)cl)->is_extern()))
1804
                    {
1805
                        G_tcmain->log_error(
1806
                            0, 0, TC_SEV_ERROR, TCERR_UNDEF_SYM,
1807
                            (int)plen, p);
1808
                        return;
1809
                    }
1810
                    else if (cl->get_type() == TC_SYM_OBJ)
1811
                    {
1812
                        /* get the object information */
1813
                        CTcSymObj *co = (CTcSymObj *)cl;
1814
                        val.set_obj(co->get_obj_id(), co->get_metaclass());
1815
                    }
1816
                    else if (cl->get_type() == TC_SYM_METACLASS)
1817
                    {
1818
                        /* get the metaclass information */
1819
                        CTcSymMetaclass *cm = (CTcSymMetaclass *)cl;
1820
                        val.set_obj(cm->get_class_obj(), TC_META_UNKNOWN);
1821
1822
                        /*
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.
1833
                         *   
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.  
1839
                         */
1840
                        G_cg->linker_ensure_mod_obj(cm);
1841
                    }
1842
                    else
1843
                    {
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);
1848
                        return;
1849
                    }
1850
1851
                    /* 
1852
                     *   represent the object or class in the parameter list
1853
                     *   with the object reference
1854
                     */
1855
                    CTPNConst cval(&val);
1856
                    cval.gen_code(FALSE, FALSE);
1857
                }
1858
1859
                /* note the value we pushed */
1860
                G_cg->note_push();
1861
            }
1862
1863
            /* build and push the list from the parameters */
1864
            if (argc <= 255)
1865
            {
1866
                G_cg->write_op(OPC_NEW1);
1867
                G_cs->write((char)argc);
1868
            }
1869
            else
1870
            {
1871
                G_cg->write_op(OPC_NEW2);
1872
                G_cs->write2(argc);
1873
            }
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);
1877
            G_cg->note_push();
1878
1879
            /* push the function pointer argument */
1880
            G_cg->write_op(OPC_PUSHFNPTR);
1881
            fsym->add_abs_fixup(G_cs);
1882
            G_cs->write4(0);
1883
            G_cg->note_push();
1884
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);
1890
            G_cg->note_push();
1891
1892
            /* 
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
1895
             *   generation here) 
1896
             */
1897
            if (ctx->mmr != 0)
1898
            {
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 */
1903
            }
1904
1905
            /* the 3 arguments will be gone on return */
1906
            G_cg->note_pop(3);
1907
1908
            /* count the registration */
1909
            ctx->cnt += 1;
1910
        }
1911
    }
1912
}
1913
1914
/* ------------------------------------------------------------------------ */
1915
/*
1916
 *   Build the IntrinsicClass objects 
1917
 */
1918
void CTcGenTarg::build_intrinsic_class_objs(CTcDataStream *str)
1919
{
1920
    tc_meta_entry *meta;
1921
    uint idx;
1922
    
1923
    /* 
1924
     *   run through the dependency table, and create an IntrinsicClass
1925
     *   object for each entry 
1926
     */
1927
    for (idx = 0, meta = meta_head_ ; meta != 0 ; meta = meta->nxt, ++idx)
1928
    {
1929
        /* 
1930
         *   if we have a symbol for this class, add the object to the
1931
         *   intrinsic class stream 
1932
         */
1933
        if (meta->sym != 0)
1934
        {
1935
            /* write the OBJS header */
1936
            str->write4(meta->sym->get_class_obj());
1937
            str->write2(8);
1938
1939
            /* 
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
1943
             */
1944
            str->write2(8);
1945
            str->write2(idx);
1946
            str->write4(meta->sym->get_mod_obj() == 0
1947
                        ? VM_INVALID_OBJ
1948
                        : meta->sym->get_mod_obj()->get_obj_id());
1949
1950
            /* 
1951
             *   fix up the inheritance chain in the modifier objects, if
1952
             *   necessary 
1953
             */
1954
            meta->sym->fix_mod_obj_sc_list();
1955
        }
1956
    }
1957
}
1958
1959
/* ------------------------------------------------------------------------ */
1960
/*
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.  
1964
 */
1965
void CTcGenTarg::build_source_line_maps()
1966
{
1967
    CTcStreamAnchor *anchor;
1968
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_)
1972
    {
1973
        ulong start_ofs;
1974
        ulong start_addr;
1975
        ulong dbg_ofs;
1976
        uint cnt;
1977
        ulong ofs;
1978
1979
        /* get the anchor's stream offset */
1980
        start_ofs = anchor->get_ofs();
1981
1982
        /* get the anchor's absolute address in the image file */
1983
        start_addr = anchor->get_addr();
1984
1985
        /* read the debug table offset from the method header */
1986
        dbg_ofs = start_ofs + G_cs->readu2_at(start_ofs + 8);
1987
1988
        /* if there's no debug table for this method, go on to the next */
1989
        if (dbg_ofs == start_ofs)
1990
            continue;
1991
1992
        /* read the number of line entries */
1993
        cnt = G_cs->readu2_at(dbg_ofs + TCT3_DBG_HDR_SIZE);
1994
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)
1998
        {
1999
            uint file_id;
2000
            ulong linenum;
2001
            uint method_ofs;
2002
            ulong line_addr;
2003
            CTcTokFileDesc *file_desc;
2004
            
2005
            /* 
2006
             *   get the file position, and the byte-code offset from the
2007
             *   start of the method of the executable code for the line 
2008
             */
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);
2012
2013
            /* calculate the absolute address of the line in the image file */
2014
            line_addr = start_addr + method_ofs;
2015
2016
            /* find the given file descriptor */
2017
            file_desc = G_tok->get_filedesc(file_id);
2018
2019
            /* 
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
2024
             *   single index)
2025
             */
2026
            if (file_desc->get_orig() != 0)
2027
                file_desc = file_desc->get_orig();
2028
2029
            /* add this line to the file descriptor */
2030
            file_desc->add_source_line(linenum, line_addr);
2031
        }
2032
    }
2033
}
2034
2035
2036
/* ------------------------------------------------------------------------ */
2037
/*
2038
 *   Callback to write enumerated source lines to an image file 
2039
 */
2040
static void write_source_lines_cb(void *ctx, ulong linenum, ulong code_addr)
2041
{
2042
    CVmImageWriter *image_writer;
2043
2044
    /* get the image writer */
2045
    image_writer = (CVmImageWriter *)ctx;
2046
2047
    /* write the data */
2048
    image_writer->write_srcf_line_entry(linenum, code_addr);
2049
}
2050
2051
/*
2052
 *   Write the list of source file descriptors to an image file 
2053
 */
2054
void CTcGenTarg::write_sources_to_image(CVmImageWriter *image_writer)
2055
{
2056
    CTcTokFileDesc *desc;
2057
2058
    /* write the block prefix */
2059
    image_writer->begin_srcf_block(G_tok->get_filedesc_count());
2060
2061
    /* write the entries */
2062
    for (desc = G_tok->get_first_filedesc() ; desc != 0 ;
2063
         desc = desc->get_next())
2064
    {
2065
        const char *fname;
2066
2067
        /* 
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
2073
         *   debugger.  
2074
         */
2075
        fname = desc->get_fname();
2076
2077
        /* 
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 
2082
         */
2083
        if (G_tcmain->get_test_report_mode())
2084
            fname = os_get_root_name((char *)fname);
2085
        
2086
        /* begin this entry */
2087
        image_writer->begin_srcf_entry(desc->get_orig_index(), fname);
2088
2089
        /* write the source lines */
2090
        desc->enum_source_lines(write_source_lines_cb, image_writer);
2091
2092
        /* end this entry */
2093
        image_writer->end_srcf_entry();
2094
    }
2095
2096
    /* end the block */
2097
    image_writer->end_srcf_block();
2098
}
2099
2100
/*
2101
 *   Write the method header list to the image file 
2102
 */
2103
void CTcGenTarg::write_method_list_to_image(CVmImageWriter *image_writer)
2104
{
2105
    CTcStreamAnchor *anchor;
2106
2107
    /* begin the method header list block in the image file */
2108
    image_writer->begin_mhls_block();
2109
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_)
2113
    {
2114
        /* write this entry's code pool address */
2115
        image_writer->write_mhls_entry(anchor->get_addr());
2116
    }
2117
2118
    /* end the block */
2119
    image_writer->end_mhls_block();
2120
}
2121
2122
/*
2123
 *   Write the preprocessor macros to the image file, for debugger use 
2124
 */
2125
void CTcGenTarg::write_macros_to_image(CVmImageWriter *image_writer)
2126
{
2127
    /* begin the macro block */
2128
    image_writer->begin_macr_block();
2129
2130
    /* 
2131
     *   ask the tokenizer to dump the data directly to the file underlying
2132
     *   the image writer 
2133
     */
2134
    G_tok->write_macros_to_file_for_debug(image_writer->get_fp());
2135
2136
    /* end the macro block */
2137
    image_writer->end_macr_block();
2138
}
2139
2140
/* ------------------------------------------------------------------------ */
2141
/*
2142
 *   Callback context for global symbol table writer 
2143
 */
2144
struct write_sym_to_image_cb
2145
{
2146
    /* number of symbols written */
2147
    ulong count;
2148
    
2149
    /* the image writer */
2150
    CVmImageWriter *image_writer;
2151
};
2152
2153
/*
2154
 *   Callback for writing the global symbol table to an object file 
2155
 */
2156
static void write_sym_to_image(void *ctx0, CTcSymbol *sym)
2157
{
2158
    write_sym_to_image_cb *ctx;
2159
2160
    /* cast the context */
2161
    ctx = (write_sym_to_image_cb *)ctx0;
2162
2163
    /* 
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. 
2169
     */
2170
    if (sym->get_sym()[0] == '.')
2171
        return;
2172
2173
    /* ask the symbol to do the work */
2174
    if (sym->write_to_image_file_global(ctx->image_writer))
2175
    {
2176
        /* we wrote the symbol - count it */
2177
        ++(ctx->count);
2178
    }
2179
}
2180
2181
/*
2182
 *   Write the global symbol table to an object file 
2183
 */
2184
void CTcGenTarg::write_global_symbols_to_image(CVmImageWriter *image_writer)
2185
{
2186
    write_sym_to_image_cb ctx;
2187
2188
    /* set up the callback context */
2189
    ctx.count = 0;
2190
    ctx.image_writer = image_writer;
2191
2192
    /* start the block */
2193
    image_writer->begin_gsym_block();
2194
    
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);
2197
2198
    /* end the block */
2199
    image_writer->end_gsym_block(ctx.count);
2200
}
2201
2202
/* ------------------------------------------------------------------------ */
2203
/*
2204
 *   Look up a property 
2205
 */
2206
vm_prop_id_t CTcGenTarg::look_up_prop(const char *propname, int required,
2207
                                      int err_if_undef, int err_if_not_prop)
2208
{
2209
    CTcSymbol *sym;
2210
    
2211
    /* look up the symbol */
2212
    sym = G_prs->get_global_symtab()->find(propname);
2213
2214
    /* check to see if it's defined and of the proper type */
2215
    if (sym == 0)
2216
    {
2217
        /* log the 'undefined' error */
2218
        G_tcmain->log_error(0, 0, required ? TC_SEV_ERROR : TC_SEV_PEDANTIC,
2219
                            err_if_undef);
2220
    }
2221
    else if (sym->get_type() != TC_SYM_PROP)
2222
    {
2223
        /* log the 'not a property' error */
2224
        G_tcmain->log_error(0, 0, required ? TC_SEV_ERROR : TC_SEV_PEDANTIC,
2225
                            err_if_not_prop);
2226
    }
2227
    else
2228
    {
2229
        /* return the property ID */
2230
        return ((CTcSymProp *)sym)->get_prop();
2231
    }
2232
2233
    /* if we got here, we didn't find a valid property */
2234
    return VM_INVALID_PROP;
2235
}
2236
2237
2238
/* ------------------------------------------------------------------------ */
2239
/*
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
2242
 *   16-bit machines.  
2243
 */
2244
void CTcGenTarg::write_tads_objects_to_image(CTcDataStream *os,
2245
                                             CVmImageWriter *image_writer,
2246
                                             int meta_idx)
2247
{
2248
    /* write the persistent (non-transient) objects */
2249
    write_tads_objects_to_image(os, image_writer, meta_idx, FALSE);
2250
2251
    /* write the transient objects */
2252
    write_tads_objects_to_image(os, image_writer, meta_idx, TRUE);
2253
}
2254
2255
/*
2256
 *   Write the TADS object stream to the image file, writing only persistent
2257
 *   or transient objects. 
2258
 */
2259
void CTcGenTarg::write_tads_objects_to_image(CTcDataStream *os,
2260
                                             CVmImageWriter *image_writer,
2261
                                             int meta_idx, int trans)
2262
{    
2263
    ulong start_ofs;
2264
    
2265
    /* keep going until we've written the whole file */
2266
    for (start_ofs = 0 ; start_ofs < os->get_ofs() ; )
2267
    {
2268
        ulong ofs;
2269
        uint siz;
2270
        uint cnt;
2271
        uint block_size;
2272
2273
        /* 
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
2279
         *   64k limit.  
2280
         */
2281
        for (block_size = 0, ofs = start_ofs, cnt = 0 ; ; )
2282
        {
2283
            uint flags;
2284
            ulong rem_len;
2285
            size_t orig_prop_cnt;
2286
            size_t write_prop_cnt;
2287
            size_t write_size;
2288
            ulong next_ofs;
2289
            ulong orig_ofs;
2290
2291
            /* if we've reached the end of the stream, we're done */
2292
            if (ofs >= os->get_ofs())
2293
                break;
2294
2295
            /* remember the starting offset */
2296
            orig_ofs = ofs;
2297
2298
            /* read our internal flags */
2299
            flags = os->readu2_at(ofs + TCT3_OBJ_INTERNHDR_FLAGS_OFS);
2300
2301
            /* 
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 
2306
             */
2307
            siz = TCT3_OBJ_INTERNHDR_SIZE
2308
                  + TCT3_META_HEADER_SIZE
2309
                  + os->readu2_at(ofs + TCT3_META_HEADER_OFS + 4);
2310
2311
            /* 
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.  
2317
             */
2318
            next_ofs = ofs + siz;
2319
2320
            /* if this object was deleted, skip it */
2321
            if ((flags & TCT3_OBJ_REPLACED) != 0)
2322
            {
2323
                ofs = next_ofs;
2324
                continue;
2325
            }
2326
2327
            /* 
2328
             *   if this object is of the wrong persistent/transient type,
2329
             *   skip it 
2330
             */
2331
            if (((flags & TCT3_OBJ_TRANSIENT) != 0) != (trans != 0))
2332
            {
2333
                ofs = next_ofs;
2334
                continue;
2335
            }
2336
            
2337
            /* 
2338
             *   if this would push us over the limit, stop here and start a
2339
             *   new block 
2340
             */
2341
            if (block_size + siz > 64000L)
2342
                break;
2343
                
2344
            /*
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.
2349
             */
2350
            write_prop_cnt = sort_object_prop_table(os, ofs);
2351
            
2352
            /* note the original property count */
2353
            orig_prop_cnt = CTPNStmObject::get_stream_prop_cnt(os, ofs);
2354
            
2355
            /* 
2356
             *   Then temporarily pdate the property count in the stream, in
2357
             *   case we changed it in the sorting process.
2358
             *   
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.  
2362
             */
2363
            write_size =
2364
                CTPNStmObject::set_stream_prop_cnt(os, ofs, write_prop_cnt)
2365
                + TCT3_META_HEADER_SIZE;
2366
2367
            /* 
2368
             *   if this is the first object in this block, write the
2369
             *   block header 
2370
             */
2371
            if (cnt == 0)
2372
                image_writer->begin_objs_block(meta_idx, FALSE, trans);
2373
2374
            /* 
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 
2378
             */
2379
            ofs += TCT3_OBJ_INTERNHDR_SIZE;
2380
2381
            /* 
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 
2386
             */
2387
            for (rem_len = write_size ; rem_len != 0 ; )
2388
            {
2389
                const char *p;
2390
                ulong avail_len;
2391
                
2392
                /* get the next block */
2393
                p = os->get_block_ptr(ofs, rem_len, &avail_len);
2394
                
2395
                /* write it out */
2396
                image_writer->write_objs_bytes(p, avail_len);
2397
                
2398
                /* move past this block */
2399
                ofs += avail_len;
2400
                rem_len -= avail_len;
2401
            }
2402
                
2403
            /* count the object */
2404
            ++cnt;
2405
2406
            /* restore the original stream property count */
2407
            CTPNStmObject::set_stream_prop_cnt(os, orig_ofs, orig_prop_cnt);
2408
2409
            /* move on to the next block */
2410
            ofs = next_ofs;
2411
        }
2412
2413
        /* if we wrote any objects, end the block */
2414
        if (cnt != 0)
2415
            image_writer->end_objs_block(cnt);
2416
2417
        /* move on to the next block */
2418
        start_ofs = ofs;
2419
    }
2420
}
2421
2422
/* ------------------------------------------------------------------------ */
2423
/*
2424
 *   Write an object stream of non-TADS objects to the image file 
2425
 */
2426
void CTcGenTarg::write_nontads_objs_to_image(CTcDataStream *os,
2427
                                             CVmImageWriter *image_writer,
2428
                                             int meta_idx, int large_objs)
2429
{
2430
    ulong start_ofs;
2431
2432
    /* keep going until we've written the whole file */
2433
    for (start_ofs = 0 ; start_ofs < os->get_ofs() ; )
2434
    {
2435
        ulong ofs;
2436
        uint siz;
2437
        uint cnt;
2438
        uint block_size;
2439
2440
        /* 
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.
2446
         *   
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.  
2450
         */
2451
        for (block_size = 0, ofs = start_ofs, cnt = 0 ; ; )
2452
        {
2453
            ulong rem_len;
2454
            ulong next_ofs;
2455
2456
            /* if we've reached the end of the stream, we're done */
2457
            if (ofs >= os->get_ofs())
2458
                break;
2459
2460
            /* 
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
2464
             *   header 
2465
             */
2466
            if (large_objs)
2467
            {
2468
                /* 
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.  
2472
                 */
2473
                siz = (ulong)os->readu4_at(ofs + 4)
2474
                      + TCT3_LARGE_META_HEADER_SIZE;
2475
            }
2476
            else
2477
            {
2478
                /* get the 16-bit size value */
2479
                siz = (ulong)os->read2_at(ofs + 4)
2480
                      + TCT3_META_HEADER_SIZE;
2481
2482
                /* 
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.  
2487
                 */
2488
                if (cnt != 0 && block_size + siz > 64000L)
2489
                    break;
2490
            }
2491
2492
            /* 
2493
             *   if this is the first object in this block, write the
2494
             *   block header - the dictionary uses large object headers,
2495
             *   so note that 
2496
             */
2497
            if (cnt == 0)
2498
                image_writer->begin_objs_block(meta_idx, large_objs, FALSE);
2499
2500
            /* calculate the offset of the next block */
2501
            next_ofs = ofs + siz;
2502
2503
            /* write the object data */
2504
            for (rem_len = siz ; rem_len != 0 ; )
2505
            {
2506
                const char *p;
2507
                ulong avail_len;
2508
2509
                /* get the next block */
2510
                p = os->get_block_ptr(ofs, rem_len, &avail_len);
2511
2512
                /* write it out */
2513
                image_writer->write_objs_bytes(p, avail_len);
2514
                
2515
                /* move past this block */
2516
                ofs += avail_len;
2517
                rem_len -= avail_len;
2518
            }
2519
                
2520
            /* count the object */
2521
            ++cnt;
2522
2523
            /* move on to the next block */
2524
            ofs = next_ofs;
2525
        }
2526
2527
        /* if we wrote any objects, end the block */
2528
        if (cnt != 0)
2529
            image_writer->end_objs_block(cnt);
2530
2531
        /* move on to the next block */
2532
        start_ofs = ofs;
2533
    }
2534
}
2535
2536
2537
/* ------------------------------------------------------------------------ */
2538
/*
2539
 *   Property comparison callback function for qsort() when invoked from
2540
 *   sort_object_prop_table() 
2541
 */
2542
//extern "C" int prop_compare(const void *p1, const void *p2);
2543
extern "C" {
2544
    static int prop_compare(const void *p1, const void *p2)
2545
    {
2546
        uint id1, id2;
2547
2548
        /* get the ID's */
2549
        id1 = osrp2(p1);
2550
        id2 = osrp2(p2);
2551
2552
        /* compare them and return the result */
2553
        return (id1 < id2 ? -1 : id1 == id2 ? 0 : 1);
2554
    }
2555
}
2556
2557
/*
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
2560
 *   the table.
2561
 *   
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.  
2565
 */
2566
size_t CTcGenTarg::sort_object_prop_table(CTcDataStream *os, ulong start_ofs)
2567
{
2568
    uint prop_table_size;
2569
    ulong orig_prop_cnt;
2570
    uint prop_cnt;
2571
    ulong prop_ofs;
2572
    size_t src, dst;
2573
2574
    /* read the number of properties from the header */
2575
    prop_cnt = CTPNStmObject::get_stream_prop_cnt(os, start_ofs);
2576
2577
    /* remember the original property count, in case we delete unused slots */
2578
    orig_prop_cnt = prop_cnt;
2579
2580
    /* calculate the property table size */
2581
    prop_table_size = prop_cnt * TCT3_TADSOBJ_PROP_SIZE;
2582
2583
    /* get the offset of the first property */
2584
    prop_ofs = CTPNStmObject::get_stream_first_prop_ofs(os, start_ofs);
2585
2586
    /* reallocate the sort buffer if necessary */
2587
    if (prop_table_size > sort_buf_size_)
2588
    {
2589
        /* increase the sort buffer size to the next 4k increment */
2590
        sort_buf_size_ = (prop_table_size + 4095) & ~4096;
2591
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);
2596
    }
2597
2598
    /* extract the table into our buffer */
2599
    os->copy_to_buf(sort_buf_, prop_ofs, prop_table_size);
2600
2601
    /* 
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
2607
     *   them now.  
2608
     */
2609
    for (src = dst = 0, prop_cnt = 0 ; src < prop_table_size ;
2610
         src += TCT3_TADSOBJ_PROP_SIZE)
2611
    {
2612
        /* if this property isn't marked for deletion, keep it */
2613
        if (osrp2(sort_buf_ + src) != VM_INVALID_PROP)
2614
        {
2615
            /* 
2616
             *   we're keeping it - if we can move it to a lower table
2617
             *   position, copy the data to the new position, otherwise
2618
             *   leave it alone 
2619
             */
2620
            if (src != dst)
2621
                memcpy(sort_buf_ + dst, sort_buf_ + src,
2622
                       TCT3_TADSOBJ_PROP_SIZE);
2623
2624
            /* 
2625
             *   advance the destination pointer past this slot, since
2626
             *   we're going to keep the data in the slot 
2627
             */
2628
            dst += TCT3_TADSOBJ_PROP_SIZE;
2629
2630
            /* count this property, since we're keeping it */
2631
            ++prop_cnt;
2632
        }
2633
    }
2634
2635
    /* sort the table */
2636
    qsort(sort_buf_, prop_cnt, TCT3_TADSOBJ_PROP_SIZE, &prop_compare);
2637
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);
2641
2642
    /* put the sorted table back in the buffer */
2643
    os->write_at(prop_ofs, sort_buf_, prop_table_size);
2644
2645
    /* return the (possibly reduced) number of properties */
2646
    return prop_cnt;
2647
}
2648
2649
2650
/*
2651
 *   callback context for enumerating a dictionary 
2652
 */
2653
struct enum_dict_ctx
2654
{
2655
    /* number of entries written so far */
2656
    uint cnt;
2657
};
2658
2659
/*
2660
 *   Generate code for a dictionary object
2661
 */
2662
void CTcGenTarg::gen_code_for_dict(CTcDictEntry *dict)
2663
{
2664
    long size_ofs;
2665
    long entry_cnt_ofs;
2666
    long end_ofs;
2667
    enum_dict_ctx ctx;
2668
2669
    /* 
2670
     *   Write the OBJS header - object ID plus byte count for
2671
     *   metaclass-specific data (use a placeholder length for now) 
2672
     */
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);
2676
2677
    /*
2678
     *   Write the metaclass-specific data for the 'dictionary' metaclass 
2679
     */
2680
2681
    /* write a nil comparator object initially */
2682
    G_dict_stream->write4(0);
2683
2684
    /* write a placeholder for the entry count */
2685
    entry_cnt_ofs = G_dict_stream->get_ofs();
2686
    G_dict_stream->write2(0);
2687
2688
    /* write the dictionary entries */
2689
    ctx.cnt = 0;
2690
    dict->get_hash_table()->enum_entries(&enum_dict_gen_cb, &ctx);
2691
2692
    /* remember the ending offset of the table */
2693
    end_ofs = G_dict_stream->get_ofs();
2694
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);
2697
2698
    /* fix up the dictionary entry count */
2699
    G_dict_stream->write2_at(entry_cnt_ofs, ctx.cnt);
2700
}
2701
2702
/*
2703
 *   Callback - enumerate dictionary entries for code generation 
2704
 */
2705
void CTcGenTarg::enum_dict_gen_cb(void *ctx0, CVmHashEntry *entry0)
2706
{
2707
    enum_dict_ctx *ctx = (enum_dict_ctx *)ctx0;
2708
    CVmHashEntryPrsDict *entry = (CVmHashEntryPrsDict *)entry0;
2709
    char buf[255];
2710
    size_t len;
2711
    char *p;
2712
    size_t rem;
2713
    uint cnt;
2714
    CTcPrsDictItem *item;
2715
2716
    /* count this entry */
2717
    ++(ctx->cnt);
2718
2719
    /* limit the key length to 255 bytes */
2720
    len = entry->getlen();
2721
    if (len > 255)
2722
        len = 255;
2723
2724
    /* copy the entry to our buffer */
2725
    memcpy(buf, entry->getstr(), len);
2726
2727
    /* apply the XOR obfuscation to the key text */
2728
    for (p = buf, rem = len ; rem != 0 ; ++p, --rem)
2729
        *p ^= 0xBD;
2730
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);
2734
2735
    /* count the items in this entry */
2736
    for (cnt = 0, item = entry->get_list() ; item != 0 ;
2737
         ++cnt, item = item->nxt_) ;
2738
2739
    /* write the number of entries */
2740
    G_dict_stream->write2(cnt);
2741
2742
    /* write the entries */
2743
    for (item = entry->get_list() ; item != 0 ; item = item->nxt_)
2744
    {
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_);
2748
    }
2749
}
2750
2751
/*
2752
 *   Generate code for a grammar production 
2753
 */
2754
void CTcGenTarg::gen_code_for_gramprod(CTcGramProdEntry *prod)
2755
{
2756
    long size_ofs;
2757
    long end_ofs;
2758
    uint cnt;
2759
    CTcGramProdAlt *alt;
2760
    CTcDataStream *str = G_gramprod_stream;
2761
    
2762
    /* 
2763
     *   write the OBJS header - object ID plus byte count for
2764
     *   metaclass-specific data (use a placeholder length for now) 
2765
     */
2766
    str->write4(prod->get_prod_sym()->get_obj_id());
2767
    size_ofs = str->get_ofs();
2768
    str->write4(0);
2769
2770
    /*
2771
     *   Write the metaclass-specific data for the 'grammar-production'
2772
     *   metaclass 
2773
     */
2774
2775
    /* count the alternatives */
2776
    for (cnt = 0, alt = prod->get_alt_head() ; alt != 0 ;
2777
         ++cnt, alt = alt->get_next()) ;
2778
2779
    /* 
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
2786
     *   indicated.  
2787
     */
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());
2793
2794
    /* 
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.  
2802
     */
2803
    if (cnt > 65535)
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());
2808
2809
    /* write the number of alternatives */
2810
    str->write2(cnt);
2811
2812
    /* write the alternatives */
2813
    for (alt = prod->get_alt_head() ; alt != 0 ; alt = alt->get_next())
2814
    {
2815
        CTcGramProdTok *tok;
2816
2817
        /* write the score and badness for the alternative */
2818
        str->write2(alt->get_score());
2819
        str->write2(alt->get_badness());
2820
        
2821
        /* write the processor object ID for this alternative */
2822
        str->write4(alt->get_processor_obj()->get_obj_id());
2823
2824
        /* count the tokens in this alternative */
2825
        for (cnt = 0, tok = alt->get_tok_head() ; tok != 0 ;
2826
             ++cnt, tok = tok->get_next()) ;
2827
2828
        /* write the token count */
2829
        str->write2(cnt);
2830
2831
        /* write the tokens */
2832
        for (tok = alt->get_tok_head() ; tok != 0 ; tok = tok->get_next())
2833
        {
2834
            size_t idx;
2835
2836
            /* write the property association */
2837
            str->write2((uint)tok->get_prop_assoc());
2838
            
2839
            /* write the token data */
2840
            switch(tok->get_type())
2841
            {
2842
            case TCGRAM_PROD:
2843
                /* write the type */
2844
                str->write((uchar)VMGRAM_MATCH_PROD);
2845
2846
                /* write the sub-production object ID */
2847
                str->write4((ulong)tok->getval_prod()->get_obj_id());
2848
                break;
2849
2850
            case TCGRAM_PART_OF_SPEECH:
2851
                /* write the type */
2852
                str->write((uchar)VMGRAM_MATCH_SPEECH);
2853
2854
                /* write the part-of-speech property */
2855
                str->write2((uint)tok->getval_part_of_speech());
2856
                break;
2857
2858
            case TCGRAM_PART_OF_SPEECH_LIST:
2859
                /* write the type */
2860
                str->write((uchar)VMGRAM_MATCH_NSPEECH);
2861
2862
                /* write the number of elements in the property list */
2863
                str->write2((uint)tok->getval_part_list_len());
2864
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));
2868
2869
                /* done */
2870
                break;
2871
2872
            case TCGRAM_LITERAL:
2873
                /* write the type */
2874
                str->write((uchar)VMGRAM_MATCH_LITERAL);
2875
2876
                /* write the string length prefix */
2877
                str->write2(tok->getval_literal_len());
2878
2879
                /* write the string text */
2880
                str->write(tok->getval_literal_txt(),
2881
                           tok->getval_literal_len());
2882
2883
                /* 
2884
                 *   add the word to the dictionary that was active when the
2885
                 *   alternative was defined 
2886
                 */
2887
                if (alt->get_dict() != 0)
2888
                {
2889
                    /* 
2890
                     *   there's a dictionary - add the word, associating it
2891
                     *   with the production object and with the parser's
2892
                     *   miscVocab property 
2893
                     */
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());
2898
                }
2899
                break;
2900
2901
            case TCGRAM_TOKEN_TYPE:
2902
                /* write the type */
2903
                str->write((uchar)VMGRAM_MATCH_TOKTYPE);
2904
2905
                /* write the enum ID of the token */
2906
                str->write4(tok->getval_token_type());
2907
                break;
2908
2909
            case TCGRAM_STAR:
2910
                /* write the type - there's no additional data */
2911
                str->write((uchar)VMGRAM_MATCH_STAR);
2912
                break;
2913
2914
            default:
2915
                assert(FALSE);
2916
                break;
2917
            }
2918
        }
2919
    }
2920
2921
    /* remember the ending offset of the object data */
2922
    end_ofs = str->get_ofs();
2923
2924
    /* go back and fix up the total size of the object data */
2925
    str->write4_at(size_ofs, end_ofs - size_ofs - 4);
2926
}
2927
2928
2929
/* ------------------------------------------------------------------------ */
2930
/*
2931
 *   Data Stream Layout Manager 
2932
 */
2933
2934
/*
2935
 *   calculate the size of the pool pages, given the size of the largest
2936
 *   single item 
2937
 */
2938
void CTcStreamLayout::calc_layout(CTcDataStream *ds, ulong max_len,
2939
                                  int is_first)
2940
{
2941
    ulong rem;
2942
    ulong free_ofs;
2943
    CTcStreamAnchor *anchor;
2944
2945
    /* if this is the first page, handle some things specially */
2946
    if (is_first)
2947
    {
2948
        ulong pgsiz;
2949
2950
        /* 
2951
         *   Starting at 2k, look for a page size that will fit the
2952
         *   desired minimum size.  
2953
         */
2954
        for (pgsiz = 2048 ; pgsiz < max_len ; pgsiz <<= 1) ;
2955
2956
        /* remember our selected page size */
2957
        page_size_ = pgsiz;
2958
2959
        /* start at the bottom of the first page */
2960
        rem = pgsiz;
2961
        free_ofs = 0;
2962
        page_cnt_ = 1;
2963
    }
2964
    else
2965
    {
2966
        /* 
2967
         *   this isn't the first page - if there are no anchors, don't
2968
         *   bother adding anything 
2969
         */
2970
        if (ds->get_first_anchor() == 0)
2971
            return;
2972
2973
        /* 
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 
2977
         */
2978
        rem = 0;
2979
        free_ofs = page_size_ * page_cnt_;
2980
    }
2981
    
2982
    /*
2983
     *   Run through the list of stream anchors and calculate the layout.
2984
     *   For each item, assign its final pool address and apply its
2985
     *   fixups.  
2986
     */
2987
    for (anchor = ds->get_first_anchor() ; anchor != 0 ;
2988
         anchor = anchor->nxt_)
2989
    {
2990
        ulong len;
2991
2992
        /* 
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 
2996
         */
2997
        if (anchor->is_replaced())
2998
            continue;
2999
        
3000
        /* 
3001
         *   if this item fits on the current page, assign it the next
3002
         *   sequential address; otherwise, go to the next page
3003
         *   
3004
         *   if this anchor is at the dividing point, put it on the next
3005
         *   page, unless we just started a new page 
3006
         */
3007
        len = anchor->get_len(ds);
3008
        if (len > rem)
3009
        {
3010
            /* 
3011
             *   we must start the next page - skip to the next page by
3012
             *   moving past the remaining free space on this page 
3013
             */
3014
            free_ofs += rem;
3015
3016
            /* count the new page */
3017
            ++page_cnt_;
3018
3019
            /* the whole next page is available to us now */
3020
            rem = page_size_;
3021
        }
3022
3023
        /* 
3024
         *   set the anchor's final address, which will apply fixups for
3025
         *   the object's fixup list 
3026
         */
3027
        anchor->set_addr(free_ofs);
3028
3029
        /* advance past this block */
3030
        free_ofs += len;
3031
        rem -= len;
3032
    }
3033
3034
    /* if there's no data at all, we have zero pages */
3035
    if (free_ofs == 0)
3036
        page_cnt_ = 0;
3037
}
3038
3039
3040
/*
3041
 *   Write our stream to an image file 
3042
 */
3043
void CTcStreamLayout::write_to_image(CTcDataStream **ds_arr, size_t ds_cnt,
3044
                                     CVmImageWriter *image_writer,
3045
                                     int pool_id, uchar xor_mask)
3046
{
3047
    CTcStreamAnchor *anchor;
3048
    ulong free_ofs;
3049
    ulong next_page_start;
3050
    int pgnum;
3051
    
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);
3054
3055
    /* 
3056
     *   start out before the first page - the next page starts with the
3057
     *   item at offset zero 
3058
     */
3059
    pgnum = 0;
3060
    next_page_start = 0;
3061
3062
    /* run through each stream */
3063
    for ( ; ds_cnt != 0 ; ++ds_arr, --ds_cnt)
3064
    {
3065
        CTcDataStream *ds;
3066
3067
        /* get the current stream */
3068
        ds = *ds_arr;
3069
3070
        /* run through the anchor list for this stream */
3071
        for (anchor = ds->get_first_anchor() ; anchor != 0 ;
3072
             anchor = anchor->nxt_)
3073
        {
3074
            ulong len;
3075
            ulong stream_ofs;
3076
            ulong addr;
3077
            
3078
            /* 
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 
3082
             */
3083
            if (anchor->is_replaced())
3084
                continue;
3085
            
3086
            /* 
3087
             *   if this item's assigned address is on the next page, move
3088
             *   to the next page 
3089
             */
3090
            len = anchor->get_len(ds);
3091
            addr = anchor->get_addr();
3092
            if (addr == next_page_start)
3093
            {
3094
                /* if this isn't the first page, close the previous page */
3095
                if (pgnum != 0)
3096
                    image_writer->end_pool_page();
3097
                
3098
                /* start the new page */
3099
                image_writer->begin_pool_page(pool_id, pgnum, TRUE, xor_mask);
3100
                
3101
                /* this item is at the start of the new page */
3102
                free_ofs = next_page_start;
3103
                
3104
                /* count the new page */
3105
                ++pgnum;
3106
                
3107
                /* calculate the address of the start of the next page */
3108
                next_page_start += page_size_;
3109
            }
3110
            
3111
            /* advance past this block */
3112
            free_ofs += len;
3113
            
3114
            /* 
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 
3119
             */
3120
            for (stream_ofs = anchor->get_ofs() ; len != 0 ; )
3121
            {
3122
                ulong cur;
3123
                const char *ptr;
3124
                
3125
                /* get the pointer to this chunk */
3126
                ptr = ds->get_block_ptr(stream_ofs, len, &cur);
3127
                
3128
                /* write this chunk */
3129
                image_writer->write_pool_page_bytes(ptr, cur, xor_mask);
3130
                
3131
                /* advance our pointers past this chunk */
3132
                len -= cur;
3133
                stream_ofs += cur;
3134
            }
3135
        }
3136
    }
3137
3138
    /* if we started a page, end it */
3139
    if (pgnum != 0)
3140
        image_writer->end_pool_page();
3141
}
3142
3143
/* ------------------------------------------------------------------------ */
3144
/*
3145
 *   Object Symbol subclass - image-file functions 
3146
 */
3147
3148
/* 
3149
 *   mark the compiled data for the object as a 'class' object 
3150
 */
3151
void CTcSymObj::mark_compiled_as_class()
3152
{
3153
    uint flags;
3154
    CTcDataStream *str;
3155
3156
    /* get the appropriate stream for generating the data */
3157
    str = get_stream();
3158
    
3159
    /* get my original object flags */
3160
    flags = CTPNStmObject::get_stream_obj_flags(str, stream_ofs_);
3161
3162
    /* add in the 'class' flag */
3163
    flags |= TCT3_OBJFLG_CLASS;
3164
3165
    /* set the updated flags */
3166
    CTPNStmObject::set_stream_obj_flags(str, stream_ofs_, flags);
3167
}
3168
3169
/*
3170
 *   Delete a property from our modified base classes 
3171
 */
3172
void CTcSymObj::delete_prop_from_mod_base(tctarg_prop_id_t prop_id)
3173
{
3174
    uint prop_cnt;
3175
    uint i;
3176
    CTcDataStream *str;
3177
3178
    /* get the correct data stream */
3179
    str = get_stream();
3180
3181
    /* get the number of properties in the object */
3182
    prop_cnt = CTPNStmObject::get_stream_prop_cnt(str, stream_ofs_);
3183
3184
    /* find the property in our property table */
3185
    for (i = 0 ; i < prop_cnt ; ++i)
3186
    {
3187
        /* if this property ID matches, delete it */
3188
        if (CTPNStmObject::get_stream_prop_id(str, stream_ofs_, i)
3189
            == prop_id)
3190
        {
3191
            /* delete the object by setting its ID to 'invalid' */
3192
            CTPNStmObject::set_stream_prop_id(str, stream_ofs_, i,
3193
                                              VM_INVALID_PROP);
3194
3195
            /* 
3196
             *   there's no need to look any further - a property can
3197
             *   occur only once in an object 
3198
             */
3199
            break;
3200
        }
3201
    }
3202
}
3203
3204
/*
3205
 *   Build the dictionary
3206
 */
3207
void CTcSymObj::build_dictionary()
3208
{
3209
    uint prop_cnt;
3210
    uint i;
3211
3212
    /* 
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.  
3217
     */
3218
    CTcSymObjBase::build_dictionary();
3219
3220
    /* if I'm not a regular tads object, there's nothing to do here */
3221
    if (metaclass_ != TC_META_TADSOBJ)
3222
        return;
3223
3224
    /* 
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.  
3228
     */
3229
3230
    /* get the number of properties in the object */
3231
    prop_cnt = CTPNStmObject::get_stream_prop_cnt(G_os, stream_ofs_);
3232
3233
    /* find the property in our property table */
3234
    for (i = 0 ; i < prop_cnt ; ++i)
3235
    {
3236
        CTcConstVal val;
3237
        vm_datatype_t prop_type;
3238
        
3239
        /* get this property value */
3240
        prop_type = CTPNStmObject::get_stream_prop_type(G_os, stream_ofs_, i);
3241
3242
        /* 
3243
         *   if it's a vocabulary list placeholder, replace it with the
3244
         *   actual list of vocabulary strings 
3245
         */
3246
        if (prop_type == VM_VOCAB_LIST)
3247
        {
3248
            vm_prop_id_t prop_id;
3249
            CTcVocabEntry *entry;
3250
            CTPNList *lst;
3251
            ulong prop_val_ofs;
3252
3253
            /* get the property ID */
3254
            prop_id = CTPNStmObject::get_stream_prop_id(G_os, stream_ofs_, i);
3255
3256
            /* get the value offset of this property */
3257
            prop_val_ofs = CTPNStmObject::
3258
                           get_stream_prop_val_ofs(G_os, stream_ofs_, i);
3259
3260
            /* create a list */
3261
            lst = new CTPNList();
3262
3263
            /* 
3264
             *   scan my internal vocabulary list and add the entries
3265
             *   associated with this property 
3266
             */
3267
            for (entry = vocab_ ; entry != 0 ; entry = entry->nxt_)
3268
            {
3269
                /* if this one matches our property, add it */
3270
                if (entry->prop_ == prop_id)
3271
                {
3272
                    CTcConstVal str_val;
3273
                    CTcPrsNode *ele;
3274
                    
3275
                    /* create a string element */
3276
                    str_val.set_sstr(entry->txt_, entry->len_);
3277
                    ele = new CTPNConst(&str_val);
3278
3279
                    /* add it to the list */
3280
                    lst->add_element(ele);
3281
                }
3282
            }
3283
3284
            /* 
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. 
3289
             */
3290
            if (lst->get_count() == 0)
3291
            {
3292
                /* 
3293
                 *   delete the property from the object by setting its
3294
                 *   property ID to 'invalid' 
3295
                 */
3296
                CTPNStmObject::
3297
                    set_stream_prop_id(G_os, stream_ofs_, i, VM_INVALID_PROP);
3298
            }
3299
            else
3300
            {
3301
                /* write the list value to the property */
3302
                val.set_list(lst);
3303
                G_cg->write_const_as_dh(G_os, prop_val_ofs, &val);
3304
            }
3305
        }
3306
    }
3307
}
3308
3309
3310
/* ------------------------------------------------------------------------ */
3311
/*
3312
 *   Symbol table entry routines for writing a symbol to the global symbol
3313
 *   table in the debug records in the image file 
3314
 */
3315
3316
/* 
3317
 *   write the symbol to an image file's global symbol table 
3318
 */
3319
int CTcSymFunc::write_to_image_file_global(class CVmImageWriter *image_writer)
3320
{
3321
    char buf[128];
3322
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);
3328
    
3329
    /* write the data */
3330
    image_writer->write_gsym_entry(get_sym(), get_sym_len(),
3331
                                   (int)TC_SYM_FUNC, buf, 8);
3332
3333
    /* we wrote the symbol */
3334
    return TRUE;
3335
}
3336
3337
/* 
3338
 *   write the symbol to an image file's global symbol table 
3339
 */
3340
int CTcSymObj::write_to_image_file_global(class CVmImageWriter *image_writer)
3341
{
3342
    char buf[128];
3343
3344
    /* store our object ID in the extra data buffer */
3345
    oswp4(buf, obj_id_);
3346
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());
3350
    else
3351
        oswp4(buf + 4, 0);
3352
3353
    /* write the data */
3354
    image_writer->write_gsym_entry(get_sym(), get_sym_len(),
3355
                                   (int)TC_SYM_OBJ, buf, 8);
3356
3357
    /* we wrote the symbol */
3358
    return TRUE;
3359
}
3360
3361
/* 
3362
 *   write the symbol to an image file's global symbol table 
3363
 */
3364
int CTcSymProp::write_to_image_file_global(class CVmImageWriter *image_writer)
3365
{
3366
    char buf[128];
3367
3368
    /* build our extra data buffer */
3369
    oswp2(buf, (uint)get_prop());
3370
3371
    /* write the data */
3372
    image_writer->write_gsym_entry(get_sym(), get_sym_len(),
3373
                                   (int)TC_SYM_PROP, buf, 2);
3374
3375
    /* we wrote the symbol */
3376
    return TRUE;
3377
}
3378
3379
/* 
3380
 *   write the symbol to an image file's global symbol table 
3381
 */
3382
int CTcSymEnum::write_to_image_file_global(class CVmImageWriter *image_writer)
3383
{
3384
    char buf[128];
3385
3386
    /* build our extra data buffer */
3387
    oswp4(buf, get_enum_id());
3388
3389
    /* build our flags */
3390
    buf[4] = 0;
3391
    if (is_token_)
3392
        buf[4] |= 1;
3393
3394
    /* write the data */
3395
    image_writer->write_gsym_entry(get_sym(), get_sym_len(),
3396
                                   (int)TC_SYM_ENUM, buf, 5);
3397
3398
    /* we wrote the symbol */
3399
    return TRUE;
3400
}
3401
3402
/* 
3403
 *   write the symbol to an image file's global symbol table 
3404
 */
3405
int CTcSymMetaclass::
3406
   write_to_image_file_global(class CVmImageWriter *image_writer)
3407
{
3408
    char buf[128];
3409
3410
    /* build our extra data buffer */
3411
    oswp2(buf, meta_idx_);
3412
    oswp4(buf + 2, class_obj_);
3413
3414
    /* write the data */
3415
    image_writer->write_gsym_entry(get_sym(), get_sym_len(),
3416
                                   (int)TC_SYM_METACLASS, buf, 6);
3417
3418
    /* we wrote the symbol */
3419
    return TRUE;
3420
}
3421
3422
/*
3423
 *   Fix up the inheritance chain in the modifier objects 
3424
 */
3425
void CTcSymMetaclass::fix_mod_obj_sc_list()
3426
{
3427
    CTcSymObj *obj;
3428
    CTcSymObj *obj_base;
3429
    
3430
    /* 
3431
     *   go through our chain of modifier objects, and make sure the
3432
     *   stream data for each object points to its correct superclass 
3433
     */
3434
    for (obj = mod_obj_ ; obj != 0 ; obj = obj_base)
3435
    {
3436
        CTcDataStream *str;
3437
3438
        /* get the correct data stream */
3439
        str = obj->get_stream();
3440
3441
        /* get the base object for this symbol */
3442
        obj_base = obj->get_mod_base_sym();
3443
3444
        /* 
3445
         *   if there's no base object, there's no superclass entry to
3446
         *   adjust for this object 
3447
         */
3448
        if (obj_base == 0)
3449
            break;
3450
3451
        /* 
3452
         *   set the superclass in this object to point to this base
3453
         *   object 
3454
         */
3455
        CTPNStmObject::set_stream_sc(str, obj->get_stream_ofs(),
3456
                                     0, obj_base->get_obj_id());
3457
    }
3458
}
3459
3460
/* 
3461
 *   write the symbol to an image file's global symbol table 
3462
 */
3463
int CTcSymBif::write_to_image_file_global(class CVmImageWriter *image_writer)
3464
{
3465
    char buf[128];
3466
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);
3474
3475
    /* write the data */
3476
    image_writer->write_gsym_entry(get_sym(), get_sym_len(),
3477
                                   (int)TC_SYM_BIF, buf, 10);
3478
3479
    /* we wrote the symbol */
3480
    return TRUE;
3481
}
3482
3483
/* 
3484
 *   write the symbol to an image file's global symbol table 
3485
 */
3486
int CTcSymExtfn::write_to_image_file_global(class CVmImageWriter *iw)
3487
{
3488
    //$$$ to be implemented
3489
    assert(FALSE);
3490
    return FALSE;
3491
}
3492