~ubuntu-branches/ubuntu/wily/gargoyle-free/wily-proposed

« back to all changes in this revision

Viewing changes to tads/tads3/tct3img.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Sylvain Beucler
  • Date: 2009-09-11 20:09:43 UTC
  • Revision ID: james.westby@ubuntu.com-20090911200943-idgzoyupq6650zpn
Tags: upstream-2009-08-25
ImportĀ upstreamĀ versionĀ 2009-08-25

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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