cfad47cfa3/t3compiler/tads3/tcgen.cpp

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#ifdef RCSID
2
static char RCSid[] =
3
"$Header: d:/cvsroot/tads/tads3/tcgen.cpp,v 1.4 1999/07/11 00:46:58 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
  tcgen.cpp - TADS 3 Compiler code generator support classes
15
Function
16
  
17
Notes
18
  
19
Modified
20
  05/09/99 MJRoberts  - Creation
21
*/
22
23
#include <stdlib.h>
24
#include <string.h>
25
26
#include "t3std.h"
27
#include "os.h"
28
#include "tcglob.h"
29
#include "tcgen.h"
30
#include "vmerr.h"
31
#include "tcerrnum.h"
32
#include "tctok.h"
33
#include "tcprs.h"
34
#include "tcmain.h"
35
#include "vmfile.h"
36
#include "tctarg.h"
37
38
39
/* ------------------------------------------------------------------------ */
40
/*
41
 *   Data/Code Stream Parser-Allocated Object 
42
 */
43
44
/* 
45
 *   allocate via a parser memory allocator
46
 */
47
void *CTcCSPrsAllocObj::operator new(size_t siz, CTcPrsMem *allocator)
48
{
49
    /* allocate via the allocator */
50
    return allocator->alloc(siz);
51
}
52
53
/* ------------------------------------------------------------------------ */
54
/*
55
 *   Data Stream 
56
 */
57
58
/*
59
 *   initialize 
60
 */
61
CTcDataStream::CTcDataStream(char stream_id)
62
{
63
    /* remember my ID */
64
    stream_id_ = stream_id;
65
66
    /* nothing is allocated yet */
67
    ofs_ = 0;
68
    obj_file_start_ofs_ = 0;
69
    pages_ = 0;
70
    page_slots_ = 0;
71
    page_cnt_ = 0;
72
    page_cur_ = 0;
73
    rem_ = 0;
74
    wp_ = 0;
75
76
    /* we have no anchors yet */
77
    first_anchor_ = last_anchor_ = 0;
78
79
    /* create our parser memory allocator */
80
    allocator_ = new CTcPrsMem();
81
}
82
83
/*
84
 *   delete 
85
 */
86
CTcDataStream::~CTcDataStream()
87
{
88
    size_t i;
89
90
    /* delete the page slots if we allocated any */
91
    for (i = 0 ; i < page_cnt_ ; ++i)
92
        t3free(pages_[i]);
93
94
    /* delete the page slot array if we allocated it */
95
    if (pages_ != 0)
96
        t3free(pages_);
97
98
    /* delete our label/fixup allocator */
99
    delete allocator_;
100
}
101
102
/*
103
 *   Reset 
104
 */
105
void CTcDataStream::reset()
106
{
107
    /* move the write pointer back to the start */
108
    ofs_ = 0;
109
    obj_file_start_ofs_ = 0;
110
111
    /* back to the first page */
112
    page_cur_ = 0;
113
114
    /* set up to write to the first page, if we have any pages at all */
115
    if (pages_ != 0)
116
    {
117
        /* we have all of the first page available again */
118
        wp_ = calc_addr(0);
119
        rem_ = TCCS_PAGE_SIZE;
120
    }
121
122
    /* reset the allocator */
123
    allocator_->reset();
124
125
    /* 
126
     *   forget all of the anchors (no need to delete them explicitly -
127
     *   they were allocated from our allocator pool, which we've reset to
128
     *   completely discard everything it contained)
129
     */
130
    first_anchor_ = last_anchor_ = 0;
131
}
132
133
/*
134
 *   Decrement the write offset 
135
 */
136
void CTcDataStream::dec_ofs(int amount)
137
{
138
    /* adjust the offset */
139
    ofs_ -= amount;
140
141
    /* 
142
     *   calculate the new page we're on, since this may take us to a
143
     *   different page 
144
     */
145
    page_cur_ = ofs_ / TCCS_PAGE_SIZE;
146
147
    /* calculate the remaining size in this page */
148
    rem_ = TCCS_PAGE_SIZE - (ofs_ % TCCS_PAGE_SIZE);
149
150
    /* calculate the current write pointer */
151
    wp_ = calc_addr(ofs_);
152
}
153
154
/*
155
 *   Get a pointer to a block at a given offset and a given length.  
156
 */
157
const char *CTcDataStream::get_block_ptr(ulong ofs,
158
                                         ulong requested_len,
159
                                         ulong *available_len)
160
{
161
    size_t page_rem;
162
163
    /* 
164
     *   determine how much is left on the page containing the offset
165
     *   after the given offset 
166
     */
167
    page_rem = TCCS_PAGE_SIZE - (ofs % TCCS_PAGE_SIZE);
168
169
    /* 
170
     *   if the amount remaining on the page is greater than the request
171
     *   length, the available length is the entire request; otherwise,
172
     *   the available length is the amount remaining on the page 
173
     */
174
    if (page_rem >= requested_len)
175
        *available_len = requested_len;
176
    else
177
        *available_len = page_rem;
178
179
    /* return the address at this offset */
180
    return calc_addr(ofs);
181
}
182
183
184
/*
185
 *   Write bytes to the stream at an earlier offset 
186
 */
187
void CTcDataStream::write_at(ulong ofs, const char *buf, size_t len)
188
{
189
    /* if we're writing to the current offset, use the normal writer */
190
    if (ofs == ofs_)
191
        write(buf, len);
192
    
193
    /* 
194
     *   log an internal error, and skip writing anything, if the desired
195
     *   range of offsets has not been previously written 
196
     */
197
    if (ofs + len > ofs_)
198
        G_tok->throw_internal_error(TCERR_WRITEAT_PAST_END);
199
200
    /* write the data to each page it spans */
201
    while (len != 0)
202
    {
203
        size_t cur;
204
205
        /* 
206
         *   determine how much is left on the page containing the current
207
         *   starting offset 
208
         */
209
        cur = TCCS_PAGE_SIZE - (ofs % TCCS_PAGE_SIZE);
210
211
        /* 
212
         *   figure out how much we can copy - copy the whole remaining
213
         *   size, but no more than the amount remaining on this page 
214
         */
215
        if (cur > len)
216
            cur = len;
217
218
        /* copy the data */
219
        memcpy(calc_addr(ofs), buf, cur);
220
221
        /* advance past this chunk */
222
        len -= cur;
223
        ofs += cur;
224
        buf += cur;
225
    }
226
}
227
228
/*
229
 *   Copy a chunk of the stream to the given buffer 
230
 */
231
void CTcDataStream::copy_to_buf(char *buf, ulong start_ofs, ulong len)
232
{
233
    /* read the data from each page that the block spans */
234
    while (len != 0)
235
    {
236
        size_t cur;
237
        
238
        /* 
239
         *   determine how much is left on the page containing the current
240
         *   starting offset 
241
         */
242
        cur = TCCS_PAGE_SIZE - (start_ofs % TCCS_PAGE_SIZE);
243
        
244
        /* 
245
         *   figure out how much we can copy - copy the whole remaining
246
         *   size, but no more than the amount remaining on this page 
247
         */
248
        if (cur > len)
249
            cur = (size_t)len;
250
251
        /* copy the data */
252
        memcpy(buf, calc_addr(start_ofs), cur);
253
254
        /* advance past this chunk */
255
        len -= cur;
256
        start_ofs += cur;
257
        buf += cur;
258
    }
259
}
260
261
/*
262
 *   Reserve space 
263
 */
264
ulong CTcDataStream::reserve(size_t len)
265
{
266
    ulong ret;
267
268
    /* we'll always return the offset current before the call */
269
    ret = ofs_;
270
    
271
    /* if we have space on the current page, it's easy */
272
    if (len <= rem_)
273
    {
274
        /* advance the output pointers */
275
        ofs_ += len;
276
        wp_ += len;
277
        rem_ -= len;
278
    }
279
    else
280
    {
281
        /* keep going until we satisfy the request */
282
        do
283
        {
284
            size_t cur;
285
286
            /* if necessary, allocate more memory */
287
            if (rem_ == 0)
288
                alloc_page();
289
290
            /* limit this chunk to the space remaining on the current page */
291
            cur = len;
292
            if (cur > rem_)
293
                cur = rem_;
294
295
            /* skip past this chunk */
296
            ofs_ += cur;
297
            wp_ += cur;
298
            rem_ -= cur;
299
            len -= cur;
300
301
        } while (len != 0);
302
    }
303
304
    /* return the starting offset */
305
    return ret;
306
}
307
308
/*
309
 *   Append data from another stream.  The source stream is permanently
310
 *   moved to the new stream, destroying the original stream.  
311
 */
312
void CTcDataStream::append_stream(CTcDataStream *stream)
313
{
314
    ulong rem;
315
    ulong ofs;
316
    ulong start_ofs;
317
    CTcStreamAnchor *anchor;
318
    CTcStreamAnchor *nxt;
319
320
    /* remember the starting offset of the copy in my stream */
321
    start_ofs = get_ofs();
322
    
323
    /* copy all data from the other stream */
324
    for (ofs = 0, rem = stream->get_ofs() ; rem != 0 ; )
325
    {
326
        ulong request;
327
        const char *ptr;
328
        ulong actual;
329
330
        /* 
331
         *   request as much as possible from the other stream, up to the
332
         *   remaining length or 64k, whichever is smaller 
333
         */
334
        request = 65535;
335
        if (rem < request)
336
            request = rem;
337
338
        /* get the chunk from the source stream */
339
        ptr = stream->get_block_ptr(ofs, request, &actual);
340
341
        /* 
342
         *   write this chunk (which we know is less than 64k and can thus
343
         *   be safely cast to size_t, even on 16-bit machines) 
344
         */
345
        write(ptr, (size_t)actual);
346
347
        /* advance our counters */
348
        rem -= actual;
349
        ofs += actual;
350
    }
351
352
    /*
353
     *   Now copy all of the anchors from the source stream to our stream.
354
     *   This will ensure that fixups in the other stream have
355
     *   corresponding fixups in this stream.  Note that we must adjust
356
     *   the offset of each copied anchor by the offset of the start of
357
     *   the copied data in our stream.  
358
     */
359
    for (anchor = stream->get_first_anchor() ; anchor != 0 ; anchor = nxt)
360
    {
361
        /* 
362
         *   remember the old link to the next anchor, since we're going
363
         *   to move the anchor to my list and thus forget about its
364
         *   position in the old list 
365
         */
366
        nxt = anchor->nxt_;
367
368
        /* adjust the anchor's offset */
369
        anchor->ofs_ += start_ofs;
370
371
        /* unlink the anchor from its old stream */
372
        anchor->nxt_ = 0;
373
374
        /* link it in to my anchor list */
375
        if (last_anchor_ != 0)
376
            last_anchor_->nxt_ = anchor;
377
        else
378
            first_anchor_ = anchor;
379
        last_anchor_ = anchor;
380
    }
381
}
382
383
/*
384
 *   Write bytes to the stream 
385
 */
386
void CTcDataStream::write(const char *buf, size_t len)
387
{
388
    /* 
389
     *   if possible, write it in one go (this is for efficiency, so that
390
     *   we can avoid making a few comparisons in the most common case) 
391
     */
392
    if (len <= rem_)
393
    {
394
        /* write the data */
395
        memcpy(wp_, buf, len);
396
397
        /* advance the output pointers */
398
        ofs_ += len;
399
        wp_ += len;
400
        rem_ -= len;
401
    }
402
    else
403
    {
404
        /* keep going until we satisfy the request */
405
        do
406
        {
407
            size_t cur;
408
409
            /* if necessary, allocate more memory */
410
            if (rem_ == 0)
411
                alloc_page();
412
413
            /* limit this chunk to the space remaining on the current page */
414
            cur = len;
415
            if (cur > rem_)
416
                cur = rem_;
417
418
            /* copy it to the page */
419
            memcpy(wp_, buf, cur);
420
421
            /* skip past the space written in the destination */
422
            ofs_ += cur;
423
            wp_ += cur;
424
            rem_ -= cur;
425
426
            /* advance past the space in the source */
427
            buf += cur;
428
            len -= cur;
429
430
        } while (len != 0);
431
    }
432
}
433
434
/*
435
 *   allocate a new page 
436
 */
437
void CTcDataStream::alloc_page()
438
{
439
    /* 
440
     *   if we're coming back to a page that was previously allocated, we
441
     *   need merely re-establish the existing page 
442
     */
443
    if (page_cur_ + 1 < page_cnt_)
444
    {
445
        /* move to the next page */
446
        ++page_cur_;
447
448
        /* start writing at the start of the page */
449
        wp_ = pages_[page_cur_];
450
        rem_ = TCCS_PAGE_SIZE;
451
452
        /* we're done */
453
        return;
454
    }
455
456
    /* 
457
     *   if we don't have room for a new page in the page array, expand
458
     *   the page array 
459
     */
460
    if (page_cnt_ >= page_slots_)
461
    {
462
        /* increase the page slot count */
463
        page_slots_ += 100;
464
465
        /* allocate or reallocate the page array */
466
        if (pages_ == 0)
467
            pages_ = (char **)t3malloc(page_slots_ * sizeof(pages_[0]));
468
        else
469
            pages_ = (char **)t3realloc(pages_,
470
                                        page_slots_ * sizeof(pages_[0]));
471
472
        /* if that failed, throw an error */
473
        if (pages_ == 0)
474
            err_throw(TCERR_CODEGEN_NO_MEM);
475
    }
476
477
    /* allocate the new page */
478
    pages_[page_cnt_] = (char *)t3malloc(TCCS_PAGE_SIZE);
479
480
    /* throw an error if we couldn't allocate the page */
481
    if (pages_[page_cnt_] == 0)
482
        err_throw(TCERR_CODEGEN_NO_MEM);
483
484
    /* start writing at the start of the new page */
485
    wp_ = pages_[page_cnt_];
486
487
    /* the entire page is free */
488
    rem_ = TCCS_PAGE_SIZE;
489
490
    /* make the new page the current page */
491
    page_cur_ = page_cnt_;
492
493
    /* count the new page */
494
    ++page_cnt_;
495
}
496
497
/*
498
 *   Add an absolute fixup for this stream at the current write offset. 
499
 */
500
void CTcDataStream::add_abs_fixup(CTcAbsFixup **list_head)
501
{
502
    /* add the fixup to the list at my current write location */
503
    CTcAbsFixup::add_abs_fixup(list_head, this, get_ofs());
504
}
505
506
507
/*
508
 *   Add an anchor at the current offset.
509
 */
510
CTcStreamAnchor *CTcDataStream::add_anchor(CTcSymbol *owner_sym,
511
                                           CTcAbsFixup **fixup_list_head,
512
                                           ulong ofs)
513
{
514
    CTcStreamAnchor *anchor;
515
    
516
    /* allocate the anchor, giving it our current offset */
517
    anchor = new (allocator_) CTcStreamAnchor(owner_sym,
518
                                              fixup_list_head, ofs);
519
520
    /* append it to our list */
521
    if (last_anchor_ != 0)
522
        last_anchor_->nxt_ = anchor;
523
    else
524
        first_anchor_ = anchor;
525
    last_anchor_ = anchor;
526
527
    /* return the new anchor */
528
    return anchor;
529
}
530
531
/*
532
 *   Find an anchor with the given stream offset 
533
 */
534
CTcStreamAnchor *CTcDataStream::find_anchor(ulong ofs) const
535
{
536
    CTcStreamAnchor *cur;
537
538
    /* scan the anchor list */
539
    for (cur = first_anchor_ ; cur != 0 ; cur = cur->nxt_)
540
    {
541
        /* if this one has the desired offset, return it */
542
        if (cur->get_ofs() == ofs)
543
            return cur;
544
    }
545
546
    /* didn't find it */
547
    return 0;
548
}
549
550
/*
551
 *   Write an object ID 
552
 */
553
void CTcDataStream::write_obj_id(ulong obj_id)
554
{
555
    /* 
556
     *   if there's an object ID fixup list, and this is a valid object
557
     *   reference (not a 'nil' reference), add this reference 
558
     */
559
    if (G_keep_objfixups && obj_id != TCTARG_INVALID_OBJ)
560
        CTcIdFixup::add_fixup(&G_objfixup, this, get_ofs(), obj_id);
561
562
    /* write the ID */
563
    write4(obj_id);
564
}
565
566
/*
567
 *   Write an object ID self-reference 
568
 */
569
void CTcDataStream::write_obj_id_selfref(CTcSymObj *obj_sym)
570
{
571
    /* 
572
     *   Add a fixup list entry to the symbol.  This type of reference
573
     *   must be kept with the symbol rather than in the global list,
574
     *   because we must apply this type of fixup each time we renumber
575
     *   the symbol. 
576
     */
577
    obj_sym->add_self_ref_fixup(this, get_ofs());
578
579
    /* write the ID to the stream */
580
    write4(obj_sym->get_obj_id());
581
}
582
583
584
/*
585
 *   Write a property ID 
586
 */
587
void CTcDataStream::write_prop_id(uint prop_id)
588
{
589
    /* if there's an object ID fixup list, add this reference */
590
    if (G_keep_propfixups)
591
        CTcIdFixup::add_fixup(&G_propfixup, this, get_ofs(), prop_id);
592
593
    /* write the ID */
594
    write2(prop_id);
595
}
596
597
/*
598
 *   Write an enumerator ID 
599
 */
600
void CTcDataStream::write_enum_id(ulong enum_id)
601
{
602
    /* if there's a fixup list, add this reference */
603
    if (G_keep_enumfixups)
604
        CTcIdFixup::add_fixup(&G_enumfixup, this, get_ofs(), enum_id);
605
606
    /* write the ID */
607
    write4(enum_id);
608
}
609
610
/*
611
 *   Write the stream, its anchors, and its fixups to an object file. 
612
 */
613
void CTcDataStream::write_to_object_file(CVmFile *fp)
614
{
615
    ulong ofs;
616
    CTcStreamAnchor *anchor;
617
    long cnt;
618
    
619
    /* 
620
     *   First, write the data stream bytes.  Write the length prefix
621
     *   followed by the data.  Just blast the whole thing out in one huge
622
     *   byte stream, one page at a time.  
623
     */
624
    fp->write_int4(ofs_);
625
626
    /* write the data one page at a time */
627
    for (ofs = 0 ; ofs < ofs_ ; )
628
    {
629
        size_t cur;
630
631
        /* 
632
         *   write out one whole page, or the balance of the current page
633
         *   if we have less than a whole page remaining 
634
         */
635
        cur = TCCS_PAGE_SIZE;
636
        if (ofs + cur > ofs_)
637
            cur = (size_t)(ofs_ - ofs);
638
639
        /* write out this chunk */
640
        fp->write_bytes(calc_addr(ofs), cur);
641
642
        /* move to the next page's offset */
643
        ofs += cur;
644
    }
645
646
    /* count the anchors */
647
    for (cnt = 0, anchor = first_anchor_ ; anchor != 0 ;
648
         anchor = anchor->nxt_, ++cnt) ;
649
650
    /* write the count */
651
    fp->write_int4(cnt);
652
653
    /*
654
     *   Write all of the anchors, and all of their fixups.  (We have the
655
     *   code to write the anchor and fixup information in-line here for
656
     *   efficiency - there will normally be a large number of these tiny
657
     *   objects, so anything we can do to improve the speed of this loop
658
     *   will help quite a lot in the overall write performance.)
659
     */
660
    for (anchor = first_anchor_ ; anchor != 0 ; anchor = anchor->nxt_)
661
    {
662
        char buf[6];
663
664
        /* write the stream offset */
665
        oswp4(buf, anchor->get_ofs());
666
667
        /* 
668
         *   If the anchor has an external fixup list, write the symbol's
669
         *   name to the file; otherwise write a zero length to indicate
670
         *   that we have an internal fixup list.  
671
         */
672
        if (anchor->get_fixup_owner_sym() == 0)
673
        {
674
            /* no external list - indicate with a zero symbol length */
675
            oswp2(buf+4, 0);
676
            fp->write_bytes(buf, 6);
677
        }
678
        else
679
        {
680
            /* external list - write the symbol length and name */
681
            oswp2(buf+4, anchor->get_fixup_owner_sym()->get_sym_len());
682
            fp->write_bytes(buf, 6);
683
            fp->write_bytes(anchor->get_fixup_owner_sym()->get_sym(),
684
                            anchor->get_fixup_owner_sym()->get_sym_len());
685
        }
686
687
        /* write the fixup list */
688
        CTcAbsFixup::
689
            write_fixup_list_to_object_file(fp, *anchor->fixup_list_head_);
690
    }
691
}
692
693
/*
694
 *   Read a stream from an object file 
695
 */
696
void CTcDataStream::load_object_file(CVmFile *fp,
697
                                     const textchar_t *fname)
698
{
699
    ulong stream_len;
700
    ulong rem;
701
    char buf[1024];
702
    ulong start_ofs;
703
    ulong anchor_cnt;
704
705
    /* read the length of the stream */
706
    stream_len = fp->read_uint4();
707
708
    /* remember my starting offset */
709
    start_ofs = get_ofs();
710
711
    /* read the stream bytes */
712
    for (rem = stream_len ; rem != 0 ; )
713
    {
714
        size_t cur;
715
716
        /* read up to a buffer-full, or however much is left */
717
        cur = sizeof(buf);
718
        if (cur > rem)
719
            cur = rem;
720
721
        /* read this chunk */
722
        fp->read_bytes(buf, cur);
723
724
        /* add this chunk to the stream */
725
        write(buf, cur);
726
727
        /* deduct the amount we've read from the amount remaining */
728
        rem -= cur;
729
    }
730
731
    /*
732
     *   Read the anchors.  For each anchor, we must fix up the anchor by
733
     *   adding the base address of the stream we just read - the original
734
     *   anchor offsets in the object file reflect a base stream offset of
735
     *   zero, but we could be loading the stream after a bunch of other
736
     *   data have already been loaded into the stream.
737
     *   
738
     *   First, read the number of anchors, then loop through the anchors
739
     *   and read each one.  
740
     */
741
    for (anchor_cnt = fp->read_uint4() ; anchor_cnt != 0 ;
742
         --anchor_cnt)
743
    {
744
        ulong anchor_ofs;
745
        size_t sym_len;
746
        CTcStreamAnchor *anchor;
747
        
748
        /* read this anchor */
749
        fp->read_bytes(buf, 6);
750
751
        /* get the offset, and adjust for the new stream base offset */
752
        anchor_ofs = t3rp4u(buf) + start_ofs;
753
754
        /* get the length of the owning symbol's name, if any */
755
        sym_len = osrp2(buf+4);
756
757
        /* if there's a symbol name, read it */
758
        if (sym_len != 0)
759
        {
760
            CTcSymbol *owner_sym;
761
            
762
            /* read the symbol name */
763
            fp->read_bytes(buf, sym_len);
764
765
            /* look it up in the global symbol table */
766
            owner_sym = G_prs->get_global_symtab()->find(buf, sym_len);
767
            if (owner_sym == 0)
768
            {
769
                /* 
770
                 *   the owner symbol doesn't exist - this is an internal
771
                 *   inconsistency in the object file, because the anchor
772
                 *   symbol must always be defined in the same file and
773
                 *   hence should have been loaded already; complain and
774
                 *   go on 
775
                 */
776
                G_tcmain->log_error(0, 0, TC_SEV_ERROR,
777
                                    TCERR_OBJFILE_INT_SYM_MISSING,
778
                                    (int)sym_len, buf, fname);
779
780
                /* we can't create the anchor */
781
                anchor = 0;
782
            }
783
            else
784
            {
785
                /* create the anchor based on the symbol */
786
                anchor = add_anchor(owner_sym,
787
                                    owner_sym->get_fixup_list_anchor(),
788
                                    anchor_ofs);
789
790
                /* set the anchor in the symbol */
791
                owner_sym->set_anchor(anchor);
792
            }
793
        }
794
        else
795
        {
796
            /* create the anchor with no external references */
797
            anchor = add_anchor(0, 0, anchor_ofs);
798
        }
799
800
        /* load the fixup list */
801
        CTcAbsFixup::
802
            load_fixup_list_from_object_file(fp, fname,
803
                                             anchor->fixup_list_head_);
804
    }
805
}
806
807
/*
808
 *   Given a stream ID, get the stream 
809
 */
810
CTcDataStream *CTcDataStream::
811
   get_stream_from_id(char stream_id, const textchar_t *obj_fname)
812
{
813
    switch(stream_id)
814
    {
815
    case TCGEN_DATA_STREAM:
816
        return G_ds;
817
818
    case TCGEN_CODE_STREAM:
819
        return G_cs_main;
820
821
    case TCGEN_STATIC_CODE_STREAM:
822
        return G_cs_static;
823
824
    case TCGEN_OBJECT_STREAM:
825
        return G_os;
826
827
    case TCGEN_ICMOD_STREAM:
828
        return G_icmod_stream;
829
830
    case TCGEN_BIGNUM_STREAM:
831
        return G_bignum_stream;
832
833
    case TCGEN_STATIC_INIT_ID_STREAM:
834
        return G_static_init_id_stream;
835
836
    default:
837
        G_tcmain->log_error(0, 0, TC_SEV_ERROR,
838
                            TCERR_OBJFILE_INVAL_STREAM_ID, obj_fname);
839
        return 0;
840
    }
841
}
842
843
844
/* ------------------------------------------------------------------------ */
845
/*
846
 *   Code Stream 
847
 */
848
849
/*
850
 *   create the code stream 
851
 */
852
CTcCodeStream::CTcCodeStream(char stream_id)
853
    : CTcDataStream(stream_id)
854
{
855
    /* no switch yet */
856
    cur_switch_ = 0;
857
858
    /* no enclosing statement yet */
859
    enclosing_ = 0;
860
861
    /* no code body being generated yet */
862
    code_body_ = 0;
863
864
    /* start writing at offset zero */
865
    ofs_ = 0;
866
867
    /* no symbol tables yet */
868
    symtab_ = 0;
869
    goto_symtab_ = 0;
870
871
    /* no labels yet */
872
    active_lbl_ = 0;
873
    free_lbl_ = 0;
874
875
    /* no fixups yet */
876
    free_fixup_ = 0;
877
878
    /* allocate an initial set of line record pages */
879
    line_pages_alloc_ = 0;
880
    line_pages_ = 0;
881
    alloc_line_pages(5);
882
883
    /* no line records in use yet */
884
    line_cnt_ = 0;
885
}
886
887
888
/*
889
 *   destroy the code stream 
890
 */
891
CTcCodeStream::~CTcCodeStream()
892
{
893
    size_t i;
894
895
    /* release all active labels */
896
    release_labels();
897
898
    /* delete the line records pages */
899
    for (i = 0 ; i < line_pages_alloc_ ; ++i)
900
        t3free(line_pages_[i]);
901
902
    /* delete the master list of pages */
903
    t3free(line_pages_);
904
}
905
906
/*
907
 *   Reset 
908
 */
909
void CTcCodeStream::reset()
910
{
911
    /* inherit default */
912
    CTcDataStream::reset();
913
914
    /* clear the line records */
915
    clear_line_recs();
916
917
    /* 
918
     *   forget all of the labels and fixups - they're allocated from the
919
     *   allocator pool, which we've completely reset now 
920
     */
921
    free_lbl_ = active_lbl_ = 0;
922
    free_fixup_ = 0;
923
924
    /* forget all of the symbol tables */
925
    symtab_ = 0;
926
    goto_symtab_ = 0;
927
928
    /* forget the frame list */
929
    frame_head_ = frame_tail_ = 0;
930
    cur_frame_ = 0;
931
    frame_cnt_ = 0;
932
933
    /* forget all of the statement settings */
934
    cur_switch_ = 0;
935
    enclosing_ = 0;
936
    code_body_ = 0;
937
938
    /* presume 'self' is not available */
939
    self_available_ = FALSE;
940
}
941
942
/*
943
 *   Allocate line record pages 
944
 */
945
void CTcCodeStream::alloc_line_pages(size_t number_to_add)
946
{
947
    size_t siz;
948
    size_t i;
949
950
    /* create or expand the master page array */
951
    siz = (line_pages_alloc_ + number_to_add) * sizeof(tcgen_line_page_t *);
952
    if (line_pages_ == 0)
953
        line_pages_ = (tcgen_line_page_t **)t3malloc(siz);
954
    else
955
        line_pages_ = (tcgen_line_page_t **)t3realloc(line_pages_, siz);
956
957
    /* allocate the new pages */
958
    for (i = line_pages_alloc_ ; i < line_pages_alloc_ + number_to_add ; ++i)
959
    {
960
        /* allocate this page */
961
        line_pages_[i] = (tcgen_line_page_t *)
962
                         t3malloc(sizeof(tcgen_line_page_t));
963
    }
964
965
    /* remember the new allocation */
966
    line_pages_alloc_ += number_to_add;
967
}
968
969
/*
970
 *   Allocate a new label object 
971
 */
972
CTcCodeLabel *CTcCodeStream::alloc_label()
973
{
974
    CTcCodeLabel *ret;
975
    
976
    /* if there's anything in the free list, use it */
977
    if (free_lbl_ != 0)
978
    {
979
        /* take the first one off the free list */
980
        ret = free_lbl_;
981
982
        /* unlink it from the list */
983
        free_lbl_ = free_lbl_->nxt;
984
    }
985
    else
986
    {
987
        /* allocate a new label */
988
        ret = new (allocator_) CTcCodeLabel;
989
990
        /* throw an error if allocation failed */
991
        if (ret == 0)
992
            err_throw(TCERR_CODEGEN_NO_MEM);
993
    }
994
995
    /* add the label to the active list */
996
    ret->nxt = active_lbl_;
997
    active_lbl_ = ret;
998
999
    /* return the allocated label */
1000
    return ret;
1001
}
1002
1003
/*
1004
 *   Allocate a new fixup object
1005
 */
1006
CTcLabelFixup *CTcCodeStream::alloc_fixup()
1007
{
1008
    CTcLabelFixup *ret;
1009
1010
    /* if there's anything in the free list, use it */
1011
    if (free_fixup_ != 0)
1012
    {
1013
        /* take the first one off the free list */
1014
        ret = free_fixup_;
1015
1016
        /* unlink it from the list */
1017
        free_fixup_ = free_fixup_->nxt;
1018
    }
1019
    else
1020
    {
1021
        /* allocate a new fixup */
1022
        ret = new (allocator_) CTcLabelFixup;
1023
1024
        /* throw an error if allocation failed */
1025
        if (ret == 0)
1026
            err_throw(TCERR_CODEGEN_NO_MEM);
1027
    }
1028
1029
    /* return the allocated fixup */
1030
    return ret;
1031
}
1032
1033
/*
1034
 *   Release all active labels.  If any labels are undefined, log an
1035
 *   internal error.
1036
 */
1037
void CTcCodeStream::release_labels()
1038
{
1039
    int err_cnt;
1040
1041
    /* we haven't found any errors yet */
1042
    err_cnt = 0;
1043
    
1044
    /* run through the list of active labels */
1045
    while (active_lbl_ != 0)
1046
    {
1047
        CTcCodeLabel *lbl;
1048
1049
        /* pull this label off of the active list */
1050
        lbl = active_lbl_;
1051
        active_lbl_ = active_lbl_->nxt;
1052
1053
        /* put this label on the free list */
1054
        lbl->nxt = free_lbl_;
1055
        free_lbl_ = lbl;
1056
1057
        /* check for unresolved fixups */
1058
        while (lbl->fhead != 0)
1059
        {
1060
            CTcLabelFixup *fixup;
1061
            
1062
            /* pull this fixup off of the active list */
1063
            fixup = lbl->fhead;
1064
            lbl->fhead = lbl->fhead->nxt;
1065
1066
            /* put this fixup on the free list */
1067
            fixup->nxt = free_fixup_;
1068
            free_fixup_ = fixup;
1069
            
1070
            /* count the unresolved label */
1071
            ++err_cnt;
1072
        }
1073
    }
1074
1075
    /* 
1076
     *   if we found any unresolved fixups, log the error; there's not
1077
     *   much point in logging each error individually, since this is an
1078
     *   internal compiler error that the user can't do anything about,
1079
     *   but at least give the user a count for compiler diagnostic
1080
     *   purposes 
1081
     */
1082
    if (err_cnt != 0)
1083
        G_tcmain->log_error(0, 0, TC_SEV_INTERNAL,
1084
                          TCERR_UNRES_TMP_FIXUP, err_cnt);
1085
}
1086
1087
/*
1088
 *   Allocate a new label at the current code offset
1089
 */
1090
CTcCodeLabel *CTcCodeStream::new_label_here()
1091
{
1092
    CTcCodeLabel *lbl;
1093
    
1094
    /* allocate a new label */
1095
    lbl = alloc_label();
1096
1097
    /* set the label's location to the current write position */
1098
    lbl->ofs = ofs_;
1099
    lbl->is_known = TRUE;
1100
1101
    /* return the new label */
1102
    return lbl;
1103
}
1104
1105
/*
1106
 *   Allocate a new forward-reference label 
1107
 */
1108
CTcCodeLabel *CTcCodeStream::new_label_fwd()
1109
{
1110
    CTcCodeLabel *lbl;
1111
1112
    /* allocate a new label */
1113
    lbl = alloc_label();
1114
1115
    /* the label's location is not yet known */
1116
    lbl->ofs = 0;
1117
    lbl->is_known = FALSE;
1118
1119
    /* return the new label */
1120
    return lbl;
1121
}
1122
1123
/*
1124
 *   Define the position of a label, resolving any fixups associated with
1125
 *   the label. 
1126
 */
1127
void CTcCodeStream::def_label_pos(CTcCodeLabel *lbl)
1128
{
1129
    /* set the label's position */
1130
    lbl->ofs = ofs_;
1131
    lbl->is_known = TRUE;
1132
    
1133
    /* resolve each fixup */
1134
    while (lbl->fhead != 0)
1135
    {
1136
        CTcLabelFixup *fixup;
1137
        long diff;
1138
        char buf[4];
1139
1140
        /* pull this fixup off of the active list */
1141
        fixup = lbl->fhead;
1142
        lbl->fhead = lbl->fhead->nxt;
1143
1144
        /* 
1145
         *   calculate the offset from the fixup position to the label
1146
         *   position, applying the bias to the fixup position 
1147
         */
1148
        diff = lbl->ofs - (fixup->ofs + fixup->bias);
1149
1150
        /* convert the offset to the correct format and write it out */
1151
        if (fixup->is_long)
1152
        {
1153
            /* write an INT4 offset value */
1154
            oswp4(buf, diff);
1155
            write_at(fixup->ofs, buf, 4);
1156
        }
1157
        else
1158
        {
1159
            /* write an INT2 offset value */
1160
            oswp2(buf, diff);
1161
            write_at(fixup->ofs, buf, 2);
1162
        }
1163
1164
        /* add this fixup to the free list, since we're finished with it */
1165
        fixup->nxt = free_fixup_;
1166
        free_fixup_ = fixup;
1167
    }
1168
}
1169
1170
/*
1171
 *   Determine if a label has a fixup at a particular offset 
1172
 */
1173
int CTcCodeStream::has_fixup_at_ofs(CTcCodeLabel *lbl, ulong ofs)
1174
{
1175
    CTcLabelFixup *fixup;
1176
    
1177
    /* scan for a match */
1178
    for (fixup = lbl->fhead ; fixup != 0 ; fixup = fixup->nxt)
1179
    {
1180
        /* if this is a match, indicate success */
1181
        if (fixup->ofs == ofs)
1182
            return TRUE;
1183
    }
1184
1185
    /* we didn't find a match */
1186
    return FALSE;
1187
}
1188
1189
/*
1190
 *   Remove a label's fixup at a particular offset 
1191
 */
1192
void CTcCodeStream::remove_fixup_at_ofs(CTcCodeLabel *lbl, ulong ofs)
1193
{
1194
    CTcLabelFixup *fixup;
1195
    CTcLabelFixup *prv;
1196
    CTcLabelFixup *nxt;
1197
1198
    /* scan for a match */
1199
    for (prv = 0, fixup = lbl->fhead ; fixup != 0 ; prv = fixup, fixup = nxt)
1200
    {
1201
        /* remember the next one */
1202
        nxt = fixup->nxt;
1203
        
1204
        /* if this is a match, remove it */
1205
        if (fixup->ofs == ofs)
1206
        {
1207
            /* unlink this fixup from the list */
1208
            if (prv == 0)
1209
                lbl->fhead = nxt;
1210
            else
1211
                prv->nxt = nxt;
1212
1213
            /* move it to the free list */
1214
            fixup->nxt = free_fixup_;
1215
            free_fixup_ = fixup;
1216
        }
1217
    }
1218
}
1219
1220
/*
1221
 *   Write an offset value to the given label 
1222
 */
1223
void CTcCodeStream::write_ofs(CTcCodeLabel *lbl, int bias, int is_long)
1224
{
1225
    /* if the label is known, write it; otherwise, generate a fixup */
1226
    if (lbl->is_known)
1227
    {
1228
        long diff;
1229
        
1230
        /* 
1231
         *   calculate the branch offset from the current position,
1232
         *   applying the bias to the current position 
1233
         */
1234
        diff = lbl->ofs - (ofs_ + bias);
1235
1236
        /* convert the offset to the correct format and write it out */
1237
        if (is_long)
1238
            write4(diff);
1239
        else
1240
            write2(diff);
1241
    }
1242
    else
1243
    {
1244
        CTcLabelFixup *fixup;
1245
1246
        /* allocate a fixup */
1247
        fixup = alloc_fixup();
1248
1249
        /* set up the fixup data */
1250
        fixup->ofs = ofs_;
1251
        fixup->bias = bias;
1252
        fixup->is_long = is_long;
1253
1254
        /* link the fixup into the label's fixup list */
1255
        fixup->nxt = lbl->fhead;
1256
        lbl->fhead = fixup;
1257
1258
        /* write a placeholder to the code stream */
1259
        if (is_long)
1260
            write4(0);
1261
        else
1262
            write2(0);
1263
    }
1264
}
1265
1266
/*
1267
 *   Add a new line record at the current code offset 
1268
 */
1269
void CTcCodeStream::add_line_rec(CTcTokFileDesc *file, long linenum)
1270
{
1271
    tcgen_line_t *rec;
1272
    ulong cur_ofs;
1273
    int reuse;
1274
1275
    /* if there's no file descriptor, there's nothing to add */
1276
    if (file == 0)
1277
        return;
1278
1279
    /* compute the current offset, relative to the start of the method */
1280
    cur_ofs = G_cs->get_ofs() - method_ofs_;
1281
1282
    /* presume we won't re-use the previous record */
1283
    reuse = FALSE;
1284
1285
    /* 
1286
     *   If we haven't added any code since the previous line record,
1287
     *   overwrite the previous record - it doesn't refer to any
1288
     *   executable code, so it's an unnecessary record.  Similarly, if
1289
     *   the previous record is at the same source position, don't add a
1290
     *   separate line record for it, since the debugger won't be able to
1291
     *   treat the two lines separately.  
1292
     */
1293
    if (line_cnt_ > 0)
1294
    {
1295
        /* get the previous record */
1296
        rec = get_line_rec(line_cnt_ - 1);
1297
1298
        /* 
1299
         *   if it refers to the same code offset, replace the old record
1300
         *   with one at this location 
1301
         */
1302
        if (rec->ofs == cur_ofs)
1303
            reuse = TRUE;
1304
1305
        /* 
1306
         *   if it has the identical source file location, don't bother
1307
         *   adding a new record at all
1308
         */
1309
        if (rec->source_id == file->get_index()
1310
            && rec->source_line == linenum)
1311
            return;
1312
    }
1313
1314
    /* if we're not re-using the previous record, allocate a new one */
1315
    if (!reuse)
1316
    {
1317
        /* 
1318
         *   we need a new record - if we've used all the allocated space,
1319
         *   allocate more 
1320
         */
1321
        if (line_cnt_ >= line_pages_alloc_ * TCGEN_LINE_PAGE_SIZE)
1322
            alloc_line_pages(5);
1323
1324
        /* get a pointer to the next available entry */
1325
        rec = get_line_rec(line_cnt_);
1326
        
1327
        /* count the new record */
1328
        ++line_cnt_;
1329
    }
1330
1331
    /* store the code offset relative to the start of the current method */
1332
    rec->ofs = cur_ofs;
1333
1334
    /* store the file information */
1335
    rec->source_id = file->get_index();
1336
    rec->source_line = linenum;
1337
1338
    /* store the frame information */
1339
    rec->frame = cur_frame_;
1340
}
1341
1342
/*
1343
 *   Get the nth line record 
1344
 */
1345
tcgen_line_t *CTcCodeStream::get_line_rec(size_t n)
1346
{
1347
    return &(line_pages_[n / TCGEN_LINE_PAGE_SIZE]
1348
             ->lines[n % TCGEN_LINE_PAGE_SIZE]);
1349
}
1350
1351
/*
1352
 *   Add a frame to the list of local frames in the method 
1353
 */
1354
void CTcCodeStream::add_local_frame(CTcPrsSymtab *symtab)
1355
{
1356
    /* 
1357
     *   If this is the global symbol table, or it's null, or it's already
1358
     *   in a list, ignore it.  Note that we can tell if the item is in a
1359
     *   list by checking its index value - a value of zero is never a
1360
     *   valid index and thus indicates that the item isn't in a list yet.
1361
     */
1362
    if (symtab == G_prs->get_global_symtab()
1363
        || symtab == 0
1364
        || symtab->get_list_index() != 0)
1365
        return;
1366
    
1367
    /* link the frame in at the tail of our list */
1368
    symtab->set_list_next(0);
1369
    if (frame_tail_ == 0)
1370
        frame_head_ = symtab;
1371
    else
1372
        frame_tail_->set_list_next(symtab);
1373
    frame_tail_ = symtab;
1374
1375
    /* count the new entry in the list */
1376
    ++frame_cnt_;
1377
1378
    /* 
1379
     *   Set this frame's index in the list.  Note that we've already
1380
     *   incremented the index value, so the first frame in the list will
1381
     *   have index 1, as is required. 
1382
     */
1383
    symtab->set_list_index(frame_cnt_);
1384
}
1385
1386
/* ------------------------------------------------------------------------ */
1387
/*
1388
 *   Data stream anchor 
1389
 */
1390
1391
/*
1392
 *   Get the length.  We can deduce the length by subtracting our offset
1393
 *   from the next item's offset, or, if we're the last item, from the
1394
 *   length of the stream.  
1395
 */
1396
ulong CTcStreamAnchor::get_len(CTcDataStream *ds) const
1397
{
1398
    if (nxt_ != 0)
1399
    {
1400
        /* 
1401
         *   there's another item after me - my length is the difference
1402
         *   between the next item's offset and my offset 
1403
         */
1404
        return (nxt_->ofs_ - ofs_);
1405
    }
1406
    else
1407
    {
1408
        /* I'm the last item - my length is whatever is left in the stream */
1409
        return (ds->get_ofs() - ofs_);
1410
    }
1411
}
1412
1413
/*
1414
 *   Set the finaly absoluate address, and apply fixups.  The code
1415
 *   generator must invoke this during the link phase once this object's
1416
 *   final address is known.  
1417
 */
1418
void CTcStreamAnchor::set_addr(ulong addr)
1419
{
1420
    /* remember my address */
1421
    addr_ = addr;
1422
    
1423
    /* apply all outstanding fixups for this object */
1424
    CTcAbsFixup::fix_abs_fixup(*fixup_list_head_, addr);
1425
}
1426
1427
/* ------------------------------------------------------------------------ */
1428
/*
1429
 *   Absolute Fixup Object 
1430
 */
1431
1432
/*
1433
 *   Add an absolute fixup at the current stream location to a given fixup
1434
 *   list.  
1435
 */
1436
void CTcAbsFixup::add_abs_fixup(CTcAbsFixup **list_head,
1437
                                CTcDataStream *ds, ulong ofs)
1438
{
1439
    CTcAbsFixup *fixup;
1440
1441
    /* 
1442
     *   create the fixup object - allocate it out of our fixup allocator
1443
     *   pool, since this fixup object has the same attributes (small and
1444
     *   long-lived) as other fixup objects 
1445
     */
1446
    fixup = (CTcAbsFixup *)G_prsmem->alloc(sizeof(CTcAbsFixup));
1447
1448
    /* set the fixup location to the current offset */
1449
    fixup->ds = ds;
1450
    fixup->ofs = ofs;
1451
1452
    /* link it in to the caller's list */
1453
    fixup->nxt = *list_head;
1454
    *list_head = fixup;
1455
}
1456
1457
/*
1458
 *   Fix up a fix-up list 
1459
 */
1460
void CTcAbsFixup::fix_abs_fixup(CTcAbsFixup *list_head, ulong final_ofs)
1461
{
1462
    CTcAbsFixup *cur;
1463
1464
    /* scan the list and fix up each entry */
1465
    for (cur = list_head ; cur != 0 ; cur = cur->nxt)
1466
    {
1467
        /* 
1468
         *   fix this entry by writing the final offset in UINT4 format to
1469
         *   the target stream at the target offset 
1470
         */
1471
        cur->ds->write4_at(cur->ofs, final_ofs);
1472
    }
1473
}
1474
1475
/*
1476
 *   Write a fixup list to an object file 
1477
 */
1478
void CTcAbsFixup::
1479
   write_fixup_list_to_object_file(CVmFile *fp, CTcAbsFixup *list_head)
1480
{
1481
    int cnt;
1482
    CTcAbsFixup *fixup;
1483
1484
    /* count the fixups */
1485
    for (cnt = 0, fixup = list_head ; fixup != 0 ;
1486
         fixup = fixup->nxt, ++cnt) ;
1487
1488
    /* write the count */
1489
    fp->write_int2(cnt);
1490
1491
        /* write the fixup list */
1492
    for (fixup = list_head ; fixup != 0 ; fixup = fixup->nxt)
1493
    {
1494
        char buf[5];
1495
1496
        /* write the data stream ID */
1497
        buf[0] = fixup->ds->get_stream_id();
1498
1499
        /* write the data stream offset of the reference */
1500
        oswp4(buf+1, fixup->ofs);
1501
1502
        /* write the data */
1503
        fp->write_bytes(buf, 5);
1504
    }
1505
}
1506
1507
/*
1508
 *   Read a fixup list from an object file 
1509
 */
1510
void CTcAbsFixup::
1511
   load_fixup_list_from_object_file(CVmFile *fp,
1512
                                    const textchar_t *obj_fname,
1513
                                    CTcAbsFixup **list_head)
1514
{
1515
    uint fixup_cnt;
1516
1517
    /* read the fixups */
1518
    for (fixup_cnt = fp->read_uint2() ; fixup_cnt != 0 ; --fixup_cnt)
1519
    {
1520
        char buf[5];
1521
        char stream_id;
1522
        ulong fixup_ofs;
1523
        CTcDataStream *stream;
1524
1525
        /* read the fixup data */
1526
        fp->read_bytes(buf, 5);
1527
        stream_id = buf[0];
1528
        fixup_ofs = t3rp4u(buf+1);
1529
1530
        /* find the stream for the ID */
1531
        stream = CTcDataStream::get_stream_from_id(stream_id, obj_fname);
1532
1533
        /* if the stream is invalid, ignore this record */
1534
        if (stream == 0)
1535
            continue;
1536
1537
        /* 
1538
         *   the fixup offset is relative to the starting offset of the
1539
         *   stream for the current object file - adjust it accordingly 
1540
         */
1541
        fixup_ofs += stream->get_object_file_start_ofs();
1542
1543
        /* create the fixup and add it to the list */
1544
        add_abs_fixup(list_head, stream, fixup_ofs);
1545
    }
1546
}
1547
1548
/* ------------------------------------------------------------------------ */
1549
/*
1550
 *   Object/property ID fixups 
1551
 */
1552
1553
/*
1554
 *   add a fixup to a list 
1555
 */
1556
void CTcIdFixup::add_fixup(CTcIdFixup **list_head, class CTcDataStream *ds,
1557
                           ulong ofs, ulong id)
1558
{
1559
    CTcIdFixup *fixup;
1560
    
1561
    /* create the new fixup object */
1562
    fixup = new (G_prsmem) CTcIdFixup(ds, ofs, id);
1563
1564
    /* link it in at the head of the list */
1565
    fixup->nxt_ = *list_head;
1566
    *list_head = fixup;
1567
}
1568
1569
/*
1570
 *   Apply this fixup
1571
 */
1572
void CTcIdFixup::apply_fixup(ulong final_id, size_t siz)
1573
{
1574
    /* write the fixup */
1575
    if (siz == 2)
1576
        ds_->write2_at(ofs_, (uint)final_id);
1577
    else
1578
        ds_->write4_at(ofs_, final_id);
1579
}
1580
1581
/*
1582
 *   Write a fixup list to an object file 
1583
 */
1584
void CTcIdFixup::write_to_object_file(CVmFile *fp, CTcIdFixup *head)
1585
{
1586
    ulong cnt;
1587
    CTcIdFixup *cur;
1588
    
1589
    /* count the elements in the list */
1590
    for (cur = head, cnt = 0 ; cur != 0 ; cur = cur->nxt_, ++cnt) ;
1591
1592
    /* write the count */
1593
    fp->write_int4(cnt);
1594
1595
    /* write the fixups */
1596
    for (cur = head ; cur != 0 ; cur = cur->nxt_)
1597
    {
1598
        char buf[9];
1599
        
1600
        /* prepare a buffer with the data in portable format */
1601
        buf[0] = cur->ds_->get_stream_id();
1602
        oswp4(buf+1, cur->ofs_);
1603
        oswp4(buf+5, cur->id_);
1604
1605
        /* write the data */
1606
        fp->write_bytes(buf, 9);
1607
    }
1608
}
1609
1610
/*
1611
 *   Read an object ID fixup list from an object file 
1612
 */
1613
void CTcIdFixup::load_object_file(CVmFile *fp,
1614
                                  const void *xlat,
1615
                                  ulong xlat_cnt,
1616
                                  tcgen_xlat_type xlat_type,
1617
                                  size_t stream_element_size,
1618
                                  const textchar_t *fname,
1619
                                  CTcIdFixup **fixup_list_head)
1620
{
1621
    ulong cnt;
1622
1623
    /* read the count, then read the fixups */
1624
    for (cnt = fp->read_uint4() ; cnt != 0 ; --cnt)
1625
    {
1626
        char buf[9];
1627
        ulong ofs;
1628
        ulong old_id;
1629
        ulong new_id;
1630
        CTcDataStream *stream;
1631
        
1632
        /* read the next fixup */
1633
        fp->read_bytes(buf, 9);
1634
        stream = CTcDataStream::get_stream_from_id(buf[0], fname);
1635
        ofs = t3rp4u(buf+1);
1636
        old_id = t3rp4u(buf+5);
1637
1638
        /* if the stream was invalid, ignore this record */
1639
        if (stream == 0)
1640
            continue;
1641
1642
        /* adjust the offset for the stream's start in this object file */
1643
        ofs += stream->get_object_file_start_ofs();
1644
1645
        /* apply the fixup if a translation table was provided */
1646
        if (xlat != 0)
1647
        {
1648
            /* make sure the count is in range - if it's not, ignore it */
1649
            if (old_id >= xlat_cnt)
1650
            {
1651
                /* note the problem */
1652
                G_tcmain->log_error(0, 0, TC_SEV_ERROR,
1653
                                    TCERR_OBJFILE_INVAL_OBJ_ID, fname);
1654
                
1655
                /* ignore the record */
1656
                continue;
1657
            }
1658
            
1659
            /* look up the new ID */
1660
            switch(xlat_type)
1661
            {
1662
            case TCGEN_XLAT_OBJ:
1663
                new_id = ((const tctarg_obj_id_t *)xlat)[old_id];
1664
                break;
1665
1666
            case TCGEN_XLAT_PROP:
1667
                new_id = ((const tctarg_prop_id_t *)xlat)[old_id];
1668
                break;
1669
1670
            case TCGEN_XLAT_ENUM:
1671
                new_id = ((const ulong *)xlat)[old_id];
1672
                break;
1673
            }
1674
            
1675
            /* apply the fixup */
1676
            if (stream_element_size == 2)
1677
                stream->write2_at(ofs, (uint)new_id);
1678
            else
1679
                stream->write4_at(ofs, new_id);
1680
        }
1681
        else
1682
        {
1683
            /* use the original ID for now */
1684
            new_id = old_id;
1685
        }
1686
1687
        /* 
1688
         *   If we're keeping object fixups in the new file, create a
1689
         *   fixup for the reference.  Note that the fixup is for the new
1690
         *   ID, not the old ID, because we've already translated the
1691
         *   value in the stream to the new ID.  
1692
         */
1693
        if (fixup_list_head != 0)
1694
            CTcIdFixup::add_fixup(fixup_list_head, stream, ofs, new_id);
1695
    }
1696
}
1697