cfad47cfa3/t3compiler/tads3/tct3.cpp

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#ifdef RCSID
2
static char RCSid[] =
3
"$Header: d:/cvsroot/tads/tads3/tct3.cpp,v 1.5 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
  tct3.cpp - TADS 3 Compiler - T3 VM Code Generator
15
Function
16
  Generate code for the T3 VM
17
Notes
18
  
19
Modified
20
  05/08/99 MJRoberts  - Creation
21
*/
22
23
#include <stdio.h>
24
#include <assert.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 "vmfile.h"
34
#include "tcmain.h"
35
#include "tcerr.h"
36
#include "vmbignum.h"
37
#include "vmrunsym.h"
38
#include "tct3unas.h"
39
40
41
/* ------------------------------------------------------------------------ */
42
/*
43
 *   T3 Target Code Generator class 
44
 */
45
46
/*
47
 *   initialize the code generator 
48
 */
49
CTcGenTarg::CTcGenTarg()
50
{
51
    int i;
52
    
53
    /* 
54
     *   we haven't written any instructions yet - fill the pipe with
55
     *   no-op instructions so that we don't think we can combine the
56
     *   first instruction with anything previous 
57
     */
58
    last_op_ = OPC_NOP;
59
    second_last_op_ = OPC_NOP;
60
61
    /* 
62
     *   we haven't seen any strings or lists yet - set the initial
63
     *   maximum lengths to zero 
64
     */
65
    max_str_len_ = 0;
66
    max_list_cnt_ = 0;
67
68
    /* we haven't generated any code yet */
69
    max_bytecode_len_ = 0;
70
71
    /* there are no metaclasses defined yet */
72
    meta_head_ = meta_tail_ = 0;
73
    meta_cnt_ = 0;
74
75
    /* no function sets defined yet */
76
    fnset_head_ = fnset_tail_ = 0;
77
    fnset_cnt_ = 0;
78
79
    /* 
80
     *   Add the built-in metaclass entries.  The order of these entries
81
     *   is fixed to match the TCT3_METAID_xxx constants - if this order
82
     *   changes, those constants must change to match.  
83
     */
84
    add_meta("tads-object");
85
    add_meta("list");
86
    add_meta("dictionary2/030000");
87
    add_meta("grammar-production/030000");
88
    add_meta("vector");
89
    add_meta("anon-func-ptr");
90
    add_meta("int-class-mod/030000");
91
92
    /*
93
     *   Set up the initial translation table for translating from our
94
     *   internal metaclass index values - TCT3_METAID_xxx - to the *actual*
95
     *   image file dependency index.  When we're compiling a program, these
96
     *   are simply in our own internal order - index N in our scheme is
97
     *   simply index N in the actual image file - because we get to lay out
98
     *   exactly what the dependency table looks like.  However, we need the
99
     *   translation table for when we're being used as part of the debugger
100
     *   - in those cases, we don't get to dictate the dependency table
101
     *   layout, because the image file (and thus the dependency table) might
102
     *   have been generated by a different version of the compiler.  In
103
     *   those cases, the image file loader will have to set up the
104
     *   translation information for us.  
105
     */
106
    for (i = 0 ; i <= TCT3_METAID_LAST ; ++i)
107
        predef_meta_idx_[i] = i;
108
    
109
    /* start at the first valid property ID */
110
    next_prop_ = 1;
111
112
    /* start at the first valid object ID */
113
    next_obj_ = 1;
114
115
    /* allocate an initial sort buffer */
116
    sort_buf_size_ = 4096;
117
    sort_buf_ = (char *)t3malloc(sort_buf_size_);
118
119
    /* not in a constructor */
120
    in_constructor_ = FALSE;
121
122
    /* no debug line record pointers yet */
123
    debug_line_cnt_ = 0;
124
    debug_line_head_ = debug_line_tail_ = 0;
125
126
    /* normal (non-debug) evaluation mode */
127
    eval_for_debug_ = FALSE;
128
    speculative_ = FALSE;
129
    debug_stack_level_ = 0;
130
131
    /* no multi-method initializer object yet */
132
    mminit_obj_ = VM_INVALID_OBJ;
133
}
134
135
/*
136
 *   delete the code generator 
137
 */
138
CTcGenTarg::~CTcGenTarg()
139
{
140
    /* delete all of the metaclass list entries */
141
    while (meta_head_ != 0)
142
    {
143
        tc_meta_entry *nxt;
144
        
145
        /* remember the next item */
146
        nxt = meta_head_->nxt;
147
148
        /* delete this item */
149
        t3free(meta_head_);
150
151
        /* move on */
152
        meta_head_ = nxt;
153
    }
154
155
    /* delete all of the function set list entries */
156
    while (fnset_head_ != 0)
157
    {
158
        tc_fnset_entry *nxt;
159
160
        /* remember the next item */
161
        nxt = fnset_head_->nxt;
162
163
        /* delete this item */
164
        t3free(fnset_head_);
165
166
        /* move on */
167
        fnset_head_ = nxt;
168
    }
169
170
    /* delete our sort buffer */
171
    t3free(sort_buf_);
172
173
    /* delete the debug line record pointers */
174
    while (debug_line_head_ != 0)
175
    {
176
        tct3_debug_line_page *nxt;
177
178
        /* remember the next one */
179
        nxt = debug_line_head_->nxt;
180
181
        /* delete this one */
182
        delete debug_line_head_;
183
184
        /* move on */
185
        debug_line_head_ = nxt;
186
    }
187
}
188
189
/* start loading the image file metaclass dependency table */
190
void CTcGenTarg::start_image_file_meta_table()
191
{
192
    int i;
193
    
194
    /* 
195
     *   clear out all of the entries - set them to -1 to indicate that
196
     *   they're invalid 
197
     */
198
    for (i = 0 ; i <= TCT3_METAID_LAST ; ++i)
199
        predef_meta_idx_[i] = -1;
200
}
201
202
/*
203
 *   Load an image file metaclass dependency table.  When we're being used in
204
 *   a debugger, the image loader must call this with the run-time dependency
205
 *   table to establish the translation from our internal metaclass
206
 *   identifiers (TCT3_METAID_xxx) to the actual run-time index values.  
207
 */
208
void CTcGenTarg::load_image_file_meta_table(
209
    const char *nm, size_t len, int idx)
210
{
211
    /* 
212
     *   Check the name against our known names.  If it matches one of the
213
     *   known types, store the actual index in our translation table under
214
     *   our internal index entry.  If it's not a known name, ignore it - we
215
     *   only care about the names we actually pre-define, because those are
216
     *   the only ones we need to generate our own code for.  
217
     */
218
    if (len == 11 && memcmp(nm, "tads-object", 11) == 0)
219
        predef_meta_idx_[TCT3_METAID_TADSOBJ] = idx;
220
    else if (len == 4 && memcmp(nm, "list", 4) == 0)
221
        predef_meta_idx_[TCT3_METAID_LIST] = idx;
222
    else if (len == 11 && memcmp(nm, "dictionary2", 11) == 0)
223
        predef_meta_idx_[TCT3_METAID_DICT] = idx;
224
    else if (len == 18 && memcmp(nm, "grammar-production", 18) == 0)
225
        predef_meta_idx_[TCT3_METAID_GRAMPROD] = idx;
226
    else if (len == 6 && memcmp(nm, "vector", 6) == 0)
227
        predef_meta_idx_[TCT3_METAID_VECTOR] = idx;
228
    else if (len == 13 && memcmp(nm, "anon-func-ptr", 13) == 0)
229
        predef_meta_idx_[TCT3_METAID_ANONFN] = idx;
230
    else if (len == 13 && memcmp(nm, "int-class-mod", 13) == 0)
231
        predef_meta_idx_[TCT3_METAID_ICMOD] = idx;
232
}
233
234
/*
235
 *   End the image file metaclass dependency table. 
236
 */
237
void CTcGenTarg::end_image_file_meta_table()
238
{
239
    int i;
240
    
241
    /* 
242
     *   Scan the metaclass translation table and make sure they've all been
243
     *   set.  If any are unset, it means that these metaclasses aren't
244
     *   available; since we depend upon these metaclasses, this means that
245
     *   we can't generate code interactively, so we can't act as a debugger.
246
     */
247
    for (i = 0 ; i <= TCT3_METAID_LAST ; ++i)
248
    {
249
        /* if this entry hasn't been set properly, abort */
250
        if (predef_meta_idx_[i] == -1)
251
            err_throw(VMERR_IMAGE_INCOMPAT_VSN_DBG);
252
    }
253
}
254
255
/*
256
 *   Add an entry to the metaclass dependency table 
257
 */
258
int CTcGenTarg::add_meta(const char *nm, size_t len,
259
                         CTcSymMetaclass *sym)
260
{
261
    tc_meta_entry *ent;
262
    size_t extra_len;
263
    const char *extra_ptr;
264
    const char *p;
265
    size_t rem;
266
267
    /* 
268
     *   if the name string doesn't contain a slash, allocate enough space
269
     *   to add an implied version suffix of "/000000" 
270
     */
271
    for (p = nm, rem = len ; rem != 0 && *p != '/' ; ++p, --rem) ;
272
    if (rem == 0)
273
    {
274
        /* we didn't find a version suffix - add space for one */
275
        extra_len = 7;
276
        extra_ptr = "/000000";
277
    }
278
    else
279
    {
280
        /* 
281
         *   there's already a version suffix - but make sure we have
282
         *   space for a six-character string 
283
         */
284
        if (rem < 7)
285
        {
286
            /* add zeroes to pad out to a six-place version string */
287
            extra_len = 7 - rem;
288
            extra_ptr = "000000";
289
        }
290
        else
291
        {
292
            /* we need nothing extra */
293
            extra_len = 0;
294
            extra_ptr = 0;
295
        }
296
    }
297
    
298
    /* allocate a new entry for the item */
299
    ent = (tc_meta_entry *)t3malloc(sizeof(tc_meta_entry) + len + extra_len);
300
    if (ent == 0)
301
        err_throw(TCERR_CODEGEN_NO_MEM);
302
303
    /* copy the name into the entry */
304
    memcpy(ent->nm, nm, len);
305
306
    /* add any extra version suffix information */
307
    if (extra_len != 0)
308
        memcpy(ent->nm + len, extra_ptr, extra_len);
309
310
    /* null-terminate the name string in the entry */
311
    ent->nm[len + extra_len] = '\0';
312
313
    /* remember the symbol */
314
    ent->sym = sym;
315
316
    /* link the entry in at the end of the list */
317
    ent->nxt = 0;
318
    if (meta_tail_ != 0)
319
        meta_tail_->nxt = ent;
320
    else
321
        meta_head_ = ent;
322
    meta_tail_ = ent;
323
324
    /* count the entry, returning the index of the entry in the list */
325
    return meta_cnt_++;
326
}
327
328
/*
329
 *   Find a metaclass index given the global identifier.
330
 */
331
tc_meta_entry *CTcGenTarg::find_meta_entry(const char *nm, size_t len,
332
                                           int update_vsn, int *entry_idx)
333
{
334
    tc_meta_entry *ent;
335
    int idx;
336
    size_t name_len;
337
    const char *p;
338
    const char *vsn;
339
    size_t vsn_len;
340
    size_t rem;
341
342
    /* find the version suffix, if any */
343
    for (rem = len, p = nm ; rem != 0 && *p != '/' ; --rem, ++p) ;
344
345
    /* note the length of the name portion (up to the '/') */
346
    name_len = len - rem;
347
348
    /* note the version string, if there is one */
349
    if (rem != 0)
350
    {
351
        vsn = p + 1;
352
        vsn_len = rem - 1;
353
    }
354
    else
355
    {
356
        vsn = 0;
357
        vsn_len = 0;
358
    }
359
360
    /* search the existing entries */
361
    for (idx = 0, ent = meta_head_ ; ent != 0 ; ent = ent->nxt, ++idx)
362
    {
363
        size_t ent_name_len;
364
        char *ent_vsn;
365
366
        /* find the version suffix in the entry */
367
        for (ent_vsn = ent->nm ; *ent_vsn != '\0' && *ent_vsn != '/' ;
368
             ++ent_vsn) ;
369
370
        /* the name is the part up to the '/' */
371
        ent_name_len = ent_vsn - ent->nm;
372
373
        /* note the length of the name and the version suffix */
374
        if (*ent_vsn == '/')
375
        {
376
            /* the version is what follows the '/' */
377
            ++ent_vsn;
378
        }
379
        else
380
        {
381
            /* there is no version suffix */
382
            ent_vsn = 0;
383
        }
384
385
        /* if this is the one, return it */
386
        if (ent_name_len == name_len && memcmp(ent->nm, nm, name_len) == 0)
387
        {
388
            /* 
389
             *   if this version number is higher than the version number
390
             *   we previously recorded, remember the new, higher version
391
             *   number 
392
             */
393
            if (update_vsn && ent_vsn != 0 && strlen(ent_vsn) == vsn_len
394
                && memcmp(vsn, ent_vsn, vsn_len) > 0)
395
            {
396
                /* store the new version string */
397
                memcpy(ent_vsn, vsn, vsn_len);
398
            }
399
400
            /* tell the caller the index, and return the entry */
401
            *entry_idx = idx;
402
            return ent;
403
        }
404
    }
405
406
    /* we didn't find it */
407
    return 0;
408
}
409
410
411
/*
412
 *   Find a metaclass symbol given the global identifier 
413
 */
414
class CTcSymMetaclass *CTcGenTarg::find_meta_sym(const char *nm, size_t len)
415
{
416
    tc_meta_entry *ent;
417
    int idx;
418
419
    /* find the entry */
420
    ent = find_meta_entry(nm, len, TRUE, &idx);
421
422
    /* 
423
     *   if we found it, return the associated metaclass symbol; if
424
     *   there's no entry, there's no symbol 
425
     */
426
    if (ent != 0)
427
        return ent->sym;
428
    else
429
        return 0;
430
}
431
432
433
/*
434
 *   Find or add a metaclass entry 
435
 */
436
int CTcGenTarg::find_or_add_meta(const char *nm, size_t len,
437
                                 CTcSymMetaclass *sym)
438
{
439
    tc_meta_entry *ent;
440
    int idx;
441
442
    /* find the entry */
443
    ent = find_meta_entry(nm, len, TRUE, &idx);
444
445
    /* if we found it, return the index */
446
    if (ent != 0)
447
    {
448
        /* 
449
         *   found it - if it didn't already have a symbol mapping, use
450
         *   the new symbol; if there is a symbol in the table entry
451
         *   already, however, do not change it 
452
         */
453
        if (ent->sym == 0)
454
            ent->sym = sym;
455
456
        /* return the index */
457
        return idx;
458
    }
459
460
    /* we didn't find an existing entry - add a new one */
461
    return add_meta(nm, len, sym);
462
}
463
464
/*
465
 *   Get the symbol for a given metaclass dependency table entry 
466
 */
467
CTcSymMetaclass *CTcGenTarg::get_meta_sym(int meta_idx)
468
{
469
    tc_meta_entry *ent;
470
471
    /* find the list entry at the given index */
472
    for (ent = meta_head_ ; ent != 0 && meta_idx != 0 ;
473
         ent = ent->nxt, --meta_idx) ;
474
475
    /* if we didn't find the entry, do nothing */
476
    if (ent == 0 || meta_idx != 0)
477
        return 0;
478
479
    /* return this entry's symbol */
480
    return ent->sym;
481
}
482
483
/*
484
 *   Get the external name for the given metaclass index
485
 */
486
const char *CTcGenTarg::get_meta_name(int meta_idx) const
487
{
488
    tc_meta_entry *ent;
489
490
    /* find the list entry at the given index */
491
    for (ent = meta_head_ ; ent != 0 && meta_idx != 0 ;
492
         ent = ent->nxt, --meta_idx) ;
493
494
    /* if we didn't find the entry, do nothing */
495
    if (ent == 0 || meta_idx != 0)
496
        return 0;
497
498
    /* return this entry's external name */
499
    return ent->nm;
500
}
501
502
/*
503
 *   Set the symbol for a given metaclass dependency table entry 
504
 */
505
void CTcGenTarg::set_meta_sym(int meta_idx, class CTcSymMetaclass *sym)
506
{
507
    tc_meta_entry *ent;
508
    
509
    /* find the list entry at the given index */
510
    for (ent = meta_head_ ; ent != 0 && meta_idx != 0 ;
511
         ent = ent->nxt, --meta_idx) ;
512
513
    /* if we didn't find the entry, do nothing */
514
    if (ent == 0 || meta_idx != 0)
515
        return;
516
517
    /* set this entry's symbol */
518
    ent->sym = sym;
519
}
520
521
/*
522
 *   Add an entry to the function set dependency table 
523
 */
524
int CTcGenTarg::add_fnset(const char *nm, size_t len)
525
{
526
    tc_fnset_entry *ent;
527
    const char *sl;
528
    size_t sl_len;
529
    size_t idx;
530
531
    /* find the version part of the new name, if present */
532
    for (sl = nm, sl_len = len ; sl_len != 0 && *sl != '/' ; ++sl, --sl_len) ;
533
534
    /* look for an existing entry with the same name prefix */
535
    for (idx = 0, ent = fnset_head_ ; ent != 0 ; ent = ent->nxt, ++idx)
536
    {
537
        char *ent_sl;
538
        
539
        /* find the version part of this entry's name, if present */
540
        for (ent_sl = ent->nm ; *ent_sl != '\0' && *ent_sl != '/' ;
541
             ++ent_sl) ;
542
543
        /* check to see if the prefixes match */
544
        if (ent_sl - ent->nm == sl - nm
545
            && memcmp(ent->nm, nm, sl - nm) == 0)
546
        {
547
            /*
548
             *   This one matches.  Keep the one with the higher version
549
             *   number.  If one has a version number and the other doesn't,
550
             *   keep the one with the version number.  
551
             */
552
            if (*ent_sl == '/' && sl_len != 0)
553
            {
554
                /* 
555
                 *   Both have version numbers - keep the higher version.
556
                 *   Limit the version length to 6 characters plus the
557
                 *   slash. 
558
                 */
559
                if (sl_len > 7)
560
                    sl_len = 7;
561
562
                /* check if the new version number is higher */
563
                if (memcmp(sl, ent_sl, sl_len) > 0)
564
                {
565
                    /* the new one is higher - copy it over the old one */
566
                    memcpy(ent_sl, sl, sl_len);
567
                }
568
569
                /* 
570
                 *   in any case, we're going to keep the existing entry, so
571
                 *   we're done - just return the existing entry's index 
572
                 */
573
                return idx;
574
            }
575
            else if (*ent_sl == '/')
576
            {
577
                /* 
578
                 *   only the old entry has a version number, so keep it and
579
                 *   ignore the new definition - this means we're done, so
580
                 *   just return the existing item's index 
581
                 */
582
                return idx;
583
            }
584
            else
585
            {
586
                /* 
587
                 *   Only the new entry has a version number, so store the
588
                 *   new version number.  To do this, simply copy the new
589
                 *   entry over the old entry, but limit the version number
590
                 *   field to 7 characters including the slash.  
591
                 */
592
                if (sl_len > 7)
593
                    len -= (sl_len - 7);
594
595
                /* copy the new value */
596
                memcpy(ent->nm, nm, len);
597
598
                /* done - return the existing item's index */
599
                return idx;
600
            }
601
        }
602
    }
603
604
    /* 
605
     *   Allocate a new entry for the item.  Always allocate space for a
606
     *   version number, even if the entry doesn't have a version number -
607
     *   if the part from the slash on is 7 characters or more, add nothing,
608
     *   else add enough to pad it out to seven characters.  
609
     */
610
    ent = (tc_fnset_entry *)t3malloc(sizeof(tc_fnset_entry) + len
611
                                     + (sl_len < 7 ? 7 - sl_len : 0));
612
    if (ent == 0)
613
        err_throw(TCERR_CODEGEN_NO_MEM);
614
615
    /* copy the name into the entry */
616
    memcpy(ent->nm, nm, len);
617
    ent->nm[len] = '\0';
618
    
619
    /* link the entry in at the end of the list */
620
    ent->nxt = 0;
621
    if (fnset_tail_ != 0)
622
        fnset_tail_->nxt = ent;
623
    else
624
        fnset_head_ = ent;
625
    fnset_tail_ = ent;
626
627
    /* count the entry, returning the index of the entry in the list */
628
    return fnset_cnt_++;
629
}
630
631
/*
632
 *   get a function set's name given its index 
633
 */
634
const char *CTcGenTarg::get_fnset_name(int idx) const
635
{
636
    tc_fnset_entry *ent;
637
638
    /* scan the linked list to find the given index */
639
    for (ent = fnset_head_ ; idx != 0 && ent != 0 ; ent = ent->nxt, --idx) ;
640
641
    /* return the one we found */
642
    return ent->nm;
643
}
644
645
/*
646
 *   Determine if we can skip an opcode because it is unreachable from the
647
 *   previous instruction.  
648
 */
649
int CTcGenTarg::can_skip_op()
650
{
651
    /* 
652
     *   if the previous instruction was a return or throw of some kind,
653
     *   we can skip any subsequent opcodes until a label is defined 
654
     */
655
    switch(last_op_)
656
    {
657
    case OPC_RET:
658
    case OPC_RETVAL:
659
    case OPC_RETTRUE:
660
    case OPC_RETNIL:
661
    case OPC_THROW:
662
    case OPC_JMP:
663
    case OPC_LRET:
664
        /* it's a return, throw, or jump - this new op is unreachable */
665
        return TRUE;
666
667
    default:
668
        /* this new op is reachable */
669
        return FALSE;
670
    }
671
}
672
673
/*
674
 *   Remove the last JMP instruction 
675
 */
676
void CTcGenTarg::remove_last_jmp()
677
{
678
    /* a JMP instruction is three bytes long, so back up three bytes */
679
    G_cs->dec_ofs(3);
680
}
681
682
/*
683
 *   Add a line record 
684
 */
685
void CTcGenTarg::add_line_rec(CTcTokFileDesc *file, long linenum)
686
{
687
    /* include line records only in debug mode */
688
    if (G_debug)
689
    {
690
        /* 
691
         *   clear the peephole, to ensure that the line boundary isn't
692
         *   blurred by code optimization 
693
         */
694
        clear_peephole();
695
696
        /* add the record to the code stream */
697
        G_cs->add_line_rec(file, linenum);
698
    }
699
}
700
701
/*
702
 *   Write an opcode to the output stream.  We'll watch for certain
703
 *   combinations of opcodes being generated, and apply peephole
704
 *   optimization when we see sequences that can be collapsed to more
705
 *   efficient single instructions.  
706
 */
707
void CTcGenTarg::write_op(uchar opc)
708
{
709
    int prv_len;
710
    int op_len;
711
712
    /* write the new opcode byte to the output stream */
713
    G_cs->write((char)opc);
714
715
    /* we've only written one byte so far for the current instruction */
716
    op_len = 1;
717
718
    /* presume the previous instruction length is just one byte */
719
    prv_len = 1;
720
721
    /* 
722
     *   check for pairs of instructions that we can reduce to more
723
     *   efficient single instructions 
724
     */
725
try_combine:
726
    switch(opc)
727
    {
728
    case OPC_JF:
729
        /* 
730
         *   if the last instruction was a comparison, we can use the
731
         *   opposite compare-and-jump instruction 
732
         */
733
        switch(last_op_)
734
        {
735
        case OPC_NOT:
736
            /* invert the sense of the test */
737
            opc = OPC_JT;
738
            goto combine;
739
740
        combine:
741
            /* 
742
             *   delete the new opcode we wrote, since we're going to combine
743
             *   it with the preceding opcode 
744
             */
745
            G_cs->dec_ofs(op_len);
746
747
            /* overwrite the preceding opcode with the new combined opcode */
748
            G_cs->write_at(G_cs->get_ofs() - prv_len, opc);
749
750
            /* roll back our internal peephole */
751
            last_op_ = second_last_op_;
752
            second_last_op_ = OPC_NOP;
753
754
            /* 
755
             *   we've deleted our own opcode, so the current (most recent)
756
             *   instruction in the output stream has the length of the
757
             *   current opcode 
758
             */
759
            op_len = prv_len;
760
761
            /* presume the previous opcode is one byte again */
762
            prv_len = 1;
763
764
            /* 
765
             *   go back for another try, since we may be able to do a
766
             *   three-way combination (for example, GT/NOT/JT would
767
             *   change to GT/JF, which would in turn change to JLE) 
768
             */
769
            goto try_combine;
770
771
        case OPC_EQ:
772
            opc = OPC_JNE;
773
            goto combine;
774
775
        case OPC_NE:
776
            opc = OPC_JE;
777
            goto combine;
778
            
779
        case OPC_LT:
780
            opc = OPC_JGE;
781
            goto combine;
782
            
783
        case OPC_LE:
784
            opc = OPC_JGT;
785
            goto combine;
786
            
787
        case OPC_GT:
788
            opc = OPC_JLE;
789
            goto combine;
790
            
791
        case OPC_GE:
792
            opc = OPC_JLT;
793
            goto combine;
794
795
        case OPC_GETR0:
796
            opc = OPC_JR0F;
797
            goto combine;
798
        }
799
        break;
800
801
    case OPC_JE:
802
        /* 
803
         *   if we just pushed nil, convert the PUSHNIL + JE to JNIL, since
804
         *   we simply want to jump if a value is nil 
805
         */
806
        if (last_op_ == OPC_PUSHNIL)
807
        {
808
            /* convert it to a jump-if-nil */
809
            opc = OPC_JNIL;
810
            goto combine;
811
        }
812
        break;
813
814
    case OPC_JNE:
815
        /* if we just pushed nil, convert to JNOTNIL */
816
        if (last_op_ == OPC_PUSHNIL)
817
        {
818
            /* convert to jump-if-not-nil */
819
            opc = OPC_JNOTNIL;
820
            goto combine;
821
        }
822
        break;
823
824
    case OPC_JT:
825
        /* 
826
         *   if the last instruction was a comparison, we can use a
827
         *   compare-and-jump instruction 
828
         */
829
        switch(last_op_)
830
        {
831
        case OPC_NOT:
832
            /* invert the sense of the test */
833
            opc = OPC_JF;
834
            goto combine;
835
            
836
        case OPC_EQ:
837
            opc = OPC_JE;
838
            goto combine;
839
840
        case OPC_NE:
841
            opc = OPC_JNE;
842
            goto combine;
843
844
        case OPC_LT:
845
            opc = OPC_JLT;
846
            goto combine;
847
848
        case OPC_LE:
849
            opc = OPC_JLE;
850
            goto combine;
851
852
        case OPC_GT:
853
            opc = OPC_JGT;
854
            goto combine;
855
856
        case OPC_GE:
857
            opc = OPC_JGE;
858
            goto combine;
859
860
        case OPC_GETR0:
861
            opc = OPC_JR0T;
862
            goto combine;
863
        }
864
        break;
865
866
    case OPC_NOT:
867
        /* 
868
         *   If the previous instruction was a comparison test of some
869
         *   kind, we can invert the sense of the test.  If the previous
870
         *   instruction was a BOOLIZE op, we can eliminate it entirely,
871
         *   because the NOT will perform the same conversion before
872
         *   negating the value.  If the previous was a NOT, we're
873
         *   inverting an inversion; we can simply perform a single
874
         *   BOOLIZE to get the same effect.  
875
         */
876
        switch(last_op_)
877
        {
878
        case OPC_EQ:
879
            opc = OPC_NE;
880
            goto combine;
881
882
        case OPC_NE:
883
            opc = OPC_EQ;
884
            goto combine;
885
886
        case OPC_GT:
887
            opc = OPC_LE;
888
            goto combine;
889
890
        case OPC_GE:
891
            opc = OPC_LT;
892
            goto combine;
893
894
        case OPC_LT:
895
            opc = OPC_GE;
896
            goto combine;
897
898
        case OPC_LE:
899
            opc = OPC_GT;
900
            goto combine;
901
902
        case OPC_BOOLIZE:
903
            opc = OPC_NOT;
904
            goto combine;
905
906
        case OPC_NOT:
907
            opc = OPC_BOOLIZE;
908
            goto combine;
909
        }
910
        break;
911
912
    case OPC_RET:
913
        /* 
914
         *   If we're writing a return instruction immediately after
915
         *   another return instruction, we can skip the additional
916
         *   instruction, since it will never be reached.  This case
917
         *   typically arises only when we generate the catch-all RET
918
         *   instruction at the end of a function. 
919
         */
920
        switch(last_op_)
921
        {
922
        case OPC_RET:
923
        case OPC_RETVAL:
924
        case OPC_RETNIL:
925
        case OPC_RETTRUE:
926
            /* simply suppress this additional RET instruction */
927
            return;
928
        }
929
        break;
930
931
    case OPC_RETNIL:
932
        /* we don't need to write two RETNIL's in a row */
933
        if (last_op_ == OPC_RETNIL)
934
            return;
935
        break;
936
937
    case OPC_RETTRUE:
938
        /* we don't need to write two RETTRUE's in a row */
939
        if (last_op_ == OPC_RETTRUE)
940
            return;
941
        break;
942
943
    case OPC_RETVAL:
944
        /* check the last opcode */
945
        switch(last_op_)
946
        {
947
        case OPC_GETR0:
948
            /* 
949
             *   if we just pushed R0 onto the stack, we can compress the
950
             *   GETR0 + RETVAL sequence into a simple RET, since RET leaves
951
             *   the R0 value unchanged 
952
             */
953
            opc = OPC_RET;
954
            goto combine;
955
956
        case OPC_PUSHNIL:
957
            /* PUSHNIL + RET can be converted to RETNIL */
958
            opc = OPC_RETNIL;
959
            goto combine;
960
961
        case OPC_PUSHTRUE:
962
            /* PUSHTRUE + RET can be converted to RETTRUE */
963
            opc = OPC_RETTRUE;
964
            goto combine;
965
        }
966
        break;
967
968
    case OPC_SETLCL1:
969
        /* we can combine this with a preceding GETR0 */
970
        if (last_op_ == OPC_GETR0)
971
        {
972
            /* generate a combined SETLCL1R0 */
973
            opc = OPC_SETLCL1R0;
974
            goto combine;
975
        }
976
        break;
977
978
    case OPC_GETPROP:
979
        /* check the previous instruction for combination possibilities */
980
        switch(last_op_)
981
        {
982
        case OPC_GETLCL1:
983
            /* get property of one-byte-addressable local */
984
            opc = OPC_GETPROPLCL1;
985
986
            /* overwrite the preceding two-byte instruction */
987
            prv_len = 2;
988
            goto combine;
989
990
        case OPC_GETR0:
991
            /* get property of R0 */
992
            opc = OPC_GETPROPR0;
993
            goto combine;
994
        }
995
        break;
996
997
    case OPC_CALLPROP:
998
        /* check the previous instruction */
999
        switch(last_op_)
1000
        {
1001
        case OPC_GETR0:
1002
            /* call property of R0 */
1003
            opc = OPC_CALLPROPR0;
1004
            goto combine;
1005
        }
1006
        break;
1007
1008
    case OPC_INDEX:
1009
        /* we can combine small integer constants with INDEX */
1010
        switch(last_op_)
1011
        {
1012
        case OPC_PUSH_0:
1013
        case OPC_PUSH_1:
1014
            /* 
1015
             *   We can combine these into IDXINT8, but we must write an
1016
             *   extra byte for the index value.  Go back and plug in the
1017
             *   extra index value byte, and add another byte at the end of
1018
             *   the stream to compensate for the insertion.  (We're just
1019
             *   going to remove and overwrite everything after the inserted
1020
             *   byte, so don't bother actually fixing up that part with real
1021
             *   data; we merely need to make sure we have the right number
1022
             *   of bytes in the stream.)  
1023
             */
1024
            G_cs->write_at(G_cs->get_ofs() - 1,
1025
                           last_op_ == OPC_PUSH_0 ? 0 : 1);
1026
            G_cs->write(0);
1027
1028
            /* combine the instructions */
1029
            opc = OPC_IDXINT8;
1030
            prv_len = 2;
1031
            goto combine;
1032
1033
        case OPC_PUSHINT8:
1034
            /* combine the PUSHINT8 + INDEX into IDXINT8 */
1035
            opc = OPC_IDXINT8;
1036
            prv_len = 2;
1037
            goto combine;
1038
        }
1039
        break;
1040
1041
    case OPC_IDXINT8:
1042
        /* we can replace GETLCL1 + IDXINT8 with IDXLCL1INT8 */
1043
        if (last_op_ == OPC_GETLCL1)
1044
        {
1045
            uchar idx;
1046
1047
            /* rewrite the GETLCL1 to add the index operand */
1048
            idx = G_cs->get_byte_at(G_cs->get_ofs() - 1);
1049
            G_cs->write_at(G_cs->get_ofs() - 2, idx);
1050
1051
            /* add another byte to compensate for the insertion */
1052
            G_cs->write(0);
1053
1054
            /* go back and combine into what's now a three-byte opcode */
1055
            opc = OPC_IDXLCL1INT8;
1056
            prv_len = 3;
1057
            goto combine;
1058
        }
1059
        break;
1060
1061
    case OPC_SETIND:
1062
        /* we can replace SETLCL1 + <small int> + SETIND with SETINDLCL1I8 */
1063
        if (second_last_op_ == OPC_SETLCL1)
1064
        {
1065
            uchar idx;
1066
1067
            /* check the middle opcode */
1068
            switch(last_op_)
1069
            {
1070
            case OPC_PUSHINT8:
1071
                /* 
1072
                 *   go back and put the index value in the right spot in the
1073
                 *   third instruction back 
1074
                 */
1075
                idx = G_cs->get_byte_at(G_cs->get_ofs() - 2);
1076
                G_cs->write_at(G_cs->get_ofs() - 3, idx);
1077
1078
                /* 
1079
                 *   Go back and combine into what's now a 3-byte
1080
                 *   instruction: we'll remove the SETIND and the
1081
                 *   PUSHINT8+val, for three bytes removed, but we're adding
1082
                 *   one byte, so we have a net current opcode length (to
1083
                 *   remove) of two bytes.  Since we're combining three
1084
                 *   instructions into one, we're losing our second-to-last
1085
                 *   opcode.  
1086
                 */
1087
                opc = OPC_SETINDLCL1I8;
1088
                second_last_op_ = OPC_NOP;
1089
                op_len = 2;
1090
                prv_len = 3;
1091
                goto combine;
1092
1093
            case OPC_PUSH_0:
1094
                idx = 0;
1095
                goto combine_setind;
1096
1097
            case OPC_PUSH_1:
1098
                idx = 1;
1099
1100
            combine_setind:
1101
                /* go back and add the index value */
1102
                G_cs->write_at(G_cs->get_ofs() - 2, idx);
1103
1104
                /* 
1105
                 *   go back and combine the instructions - we're removing a
1106
                 *   net of one byte, since we're removing two one-byte
1107
                 *   instructions and extending the old 2-byte instruction
1108
                 *   into a 3-byte instruction 
1109
                 */
1110
                opc = OPC_SETINDLCL1I8;
1111
                second_last_op_ = OPC_NOP;
1112
                op_len = 1;
1113
                prv_len = 3;
1114
                goto combine;
1115
            }
1116
        }
1117
        break;
1118
1119
    default:
1120
        /* write this instruction as-is */
1121
        break;
1122
    }
1123
    
1124
    /* remember the last opcode we wrote */
1125
    second_last_op_ = last_op_;
1126
    last_op_ = opc;
1127
}
1128
1129
/*
1130
 *   Write a CALLPROP instruction, combining with preceding opcodes if
1131
 *   possible.  
1132
 */
1133
void CTcGenTarg::write_callprop(int argc, int varargs, vm_prop_id_t prop)
1134
{
1135
    /* 
1136
     *   if the previous instruction was GETLCL1, combine it with the
1137
     *   CALLPROP to form a single CALLPROPLCL1 instruction 
1138
     */
1139
    if (last_op_ == OPC_GETLCL1)
1140
    {
1141
        uchar lcl;
1142
1143
        /* get the local variable ID from the GETLCL1 instruction */
1144
        lcl = G_cs->get_byte_at(G_cs->get_ofs() - 1);
1145
1146
        /* back up and delete the GETLCL1 instruction */
1147
        G_cs->dec_ofs(2);
1148
1149
        /* roll back the peephole for the instruction deletion */
1150
        last_op_ = second_last_op_;
1151
        second_last_op_ = OPC_NOP;
1152
1153
        /* write the varargs modifier if appropriate */
1154
        if (varargs)
1155
            write_op(OPC_VARARGC);
1156
1157
        /* write the CALLPROPLCL1 */
1158
        write_op(OPC_CALLPROPLCL1);
1159
        G_cs->write((char)argc);
1160
        G_cs->write(lcl);
1161
        G_cs->write_prop_id(prop);
1162
    }
1163
    else
1164
    {
1165
        /* generate the varargs modifier if appropriate */
1166
        if (varargs)
1167
            write_op(OPC_VARARGC);
1168
1169
        /* we have arguments - generate a CALLPROP */
1170
        write_op(OPC_CALLPROP);
1171
        G_cs->write((char)argc);
1172
        G_cs->write_prop_id(prop);
1173
    }
1174
1175
    /* callprop removes arguments and the object */
1176
    note_pop(argc + 1);
1177
}
1178
1179
/*
1180
 *   Note a string's length 
1181
 */
1182
void CTcGenTarg::note_str(size_t len)
1183
{
1184
    /* if it's the longest so far, remember it */
1185
    if (len > max_str_len_)
1186
    {
1187
        /* 
1188
         *   flag an warning the length plus overhead would exceed 32k
1189
         *   (only do this the first time we cross this limit) 
1190
         */
1191
        if (len > (32*1024 - VMB_LEN)
1192
            && max_str_len_ <= (32*1024 - VMB_LEN))
1193
            G_tok->log_warning(TCERR_CONST_POOL_OVER_32K);
1194
1195
        /* remember the length */
1196
        max_str_len_ = len;
1197
    }
1198
}
1199
1200
/*
1201
 *   note a list's length 
1202
 */
1203
void CTcGenTarg::note_list(size_t element_count)
1204
{
1205
    /* if it's the longest list so far, remember it */
1206
    if (element_count > max_list_cnt_)
1207
    {
1208
        /* flag a warning if the stored length would be over 32k */
1209
        if (element_count > ((32*1024 - VMB_LEN) / VMB_DATAHOLDER)
1210
            && max_list_cnt_ <= ((32*1024 - VMB_LEN) / VMB_DATAHOLDER))
1211
            G_tok->log_warning(TCERR_CONST_POOL_OVER_32K);
1212
1213
        /* remember the length */
1214
        max_list_cnt_ = element_count;
1215
    }
1216
}
1217
1218
/*
1219
 *   Note a bytecode block length 
1220
 */
1221
void CTcGenTarg::note_bytecode(ulong len)
1222
{
1223
    /* if it's the longest bytecode block yet, remember it */
1224
    if (len > max_bytecode_len_)
1225
    {
1226
        /* flag a warning the first time we go over 32k */
1227
        if (len >= 32*1024 && max_bytecode_len_ < 32*1024)
1228
            G_tok->log_warning(TCERR_CODE_POOL_OVER_32K);
1229
        
1230
        /* remember the new length */
1231
        max_bytecode_len_ = len;
1232
    }
1233
}
1234
1235
/*
1236
 *   Add a string to the constant pool 
1237
 */
1238
void CTcGenTarg::add_const_str(const char *str, size_t len,
1239
                               CTcDataStream *ds, ulong ofs)
1240
{
1241
    CTcStreamAnchor *anchor;
1242
    
1243
    /* 
1244
     *   Add an anchor for the item, and add a fixup for the reference
1245
     *   from ds@ofs to the item. 
1246
     */
1247
    anchor = G_ds->add_anchor(0, 0);
1248
    CTcAbsFixup::add_abs_fixup(anchor->fixup_list_head_, ds, ofs);
1249
1250
    /* write the length prefix */
1251
    G_ds->write2(len);
1252
1253
    /* write the string bytes */
1254
    G_ds->write(str, len);
1255
1256
    /* note the length of the string stored */
1257
    note_str(len);
1258
}
1259
1260
/*
1261
 *   Add a list to the constant pool 
1262
 */
1263
void CTcGenTarg::add_const_list(CTPNList *lst,
1264
                                CTcDataStream *ds, ulong ofs)
1265
{
1266
    int i;
1267
    CTPNListEle *cur;
1268
    ulong dst;
1269
    CTcStreamAnchor *anchor;
1270
1271
    /* 
1272
     *   Add an anchor for the item, and add a fixup for the reference
1273
     *   from ds@ofs to the item.  
1274
     */
1275
    anchor = G_ds->add_anchor(0, 0);
1276
    CTcAbsFixup::add_abs_fixup(anchor->fixup_list_head_, ds, ofs);
1277
1278
    /* 
1279
     *   Reserve space for the list.  We need to do this first, because
1280
     *   the list might contain elements which themselves must be written
1281
     *   to the data stream; we must therefore reserve space for the
1282
     *   entire list before we start writing its elements. 
1283
     */
1284
    dst = G_ds->reserve(2 + lst->get_count()*VMB_DATAHOLDER);
1285
1286
    /* set the length prefix */
1287
    G_ds->write2_at(dst, lst->get_count());
1288
    dst += 2;
1289
1290
    /* store the elements */
1291
    for (i = 0, cur = lst->get_head() ; cur != 0 ;
1292
         ++i, cur = cur->get_next(), dst += VMB_DATAHOLDER)
1293
    {
1294
        CTcConstVal *ele;
1295
1296
        /* get this element */
1297
        ele = cur->get_expr()->get_const_val();
1298
1299
        /* write it to the element buffer */
1300
        write_const_as_dh(G_ds, dst, ele);
1301
    }
1302
1303
    /* make sure the list wasn't corrupted */
1304
    if (i != lst->get_count())
1305
        G_tok->throw_internal_error(TCERR_CORRUPT_LIST);
1306
1307
    /* note the number of elements in the list */
1308
    note_list(lst->get_count());
1309
}
1310
1311
/*
1312
 *   Generate a BigNumber object 
1313
 */
1314
vm_obj_id_t CTcGenTarg::gen_bignum_obj(const char *txt, size_t len)
1315
{
1316
    vm_obj_id_t id;
1317
    long size_ofs;
1318
    long end_ofs;
1319
    long num_ofs;
1320
    CTcDataStream *str = G_bignum_stream;
1321
    int exp;
1322
    int decpt;
1323
    utf8_ptr p;
1324
    size_t rem;
1325
    int dig[2];
1326
    char dig_idx;
1327
    int sig;
1328
    size_t tot_digits;
1329
    size_t prec;
1330
    int neg;
1331
    int val_zero;
1332
    uchar flags;
1333
1334
    /* generate a new object ID for the BigNumber */
1335
    id = new_obj_id();
1336
1337
    /* 
1338
     *   add the object ID to the non-symbol object list - this is
1339
     *   necessary to ensure that the object ID is fixed up during linking 
1340
     */
1341
    G_prs->add_nonsym_obj(id);
1342
1343
    /* 
1344
     *   generate the object data to the BigNumber data stream
1345
     */
1346
1347
    /* 
1348
     *   write the OBJS header - object ID plus byte count for
1349
     *   metaclass-specific data 
1350
     */
1351
    str->write_obj_id(id);
1352
    size_ofs = str->get_ofs();
1353
    str->write2(0);
1354
1355
    /* 
1356
     *   write the metaclass-specific data for the BigNumber metaclass 
1357
     */
1358
1359
    /* remember where the number starts */
1360
    num_ofs = str->get_ofs();
1361
1362
    /* write placeholders for the precision, exponent, and flags */
1363
    str->write2(0);
1364
    str->write2(0);
1365
    str->write(0);
1366
1367
    /* start at the beginning of the number's text */
1368
    p.set((char *)txt);
1369
    rem = len;
1370
1371
    /* presume the value won't be zero */
1372
    val_zero = FALSE;
1373
1374
    /* check for leading sign indicators */
1375
    for (neg = FALSE ; rem != 0 ; p.inc(&rem))
1376
    {
1377
        /* if it's a sign, note it and keep scanning */
1378
        if (p.getch() == '-')
1379
        {
1380
            /* negative sign - note it and keep going */
1381
            neg = !neg;
1382
        }
1383
        else if (p.getch() == '+')
1384
        {
1385
            /* positive sign - ignore it and keep going */
1386
        }
1387
        else
1388
        {
1389
            /* not a sign character - stop scanning for signs */
1390
            break;
1391
        }
1392
    }
1393
1394
    /* scan the digits of the number */
1395
    for (exp = 0, sig = FALSE, decpt = FALSE, prec = 0, tot_digits = 0,
1396
         dig_idx = 0 ; rem != 0 ; p.inc(&rem))
1397
    {
1398
        wchar_t ch;
1399
        
1400
        /* get this character */
1401
        ch = p.getch();
1402
1403
        /* see what we have */
1404
        if (is_digit(ch))
1405
        {
1406
            /* 
1407
             *   if it's non-zero, it's definitely significant; otherwise,
1408
             *   it's significant only if we've seen a significant digit
1409
             *   already 
1410
             */
1411
            if (ch != '0')
1412
                sig = TRUE;
1413
1414
            /* count it in the total digits whether or not its significant */
1415
            ++tot_digits;
1416
            
1417
            /* if the digit is significant, add it to the number */
1418
            if (sig)
1419
            {
1420
                /* add another digit to our buffer */
1421
                dig[dig_idx++] = value_of_digit(ch);
1422
1423
                /* count the precision */
1424
                ++prec;
1425
1426
                /* 
1427
                 *   if we haven't found the decimal point yet, count the
1428
                 *   exponent change
1429
                 */
1430
                if (!decpt)
1431
                    ++exp;
1432
                
1433
                /* 
1434
                 *   if we have two digits now, write out another byte of
1435
                 *   the number 
1436
                 */
1437
                if (dig_idx == 2)
1438
                {
1439
                    /* write out this digit pair */
1440
                    str->write((char)((dig[0] << 4) + dig[1]));
1441
1442
                    /* the buffer is now empty */
1443
                    dig_idx = 0;
1444
                }
1445
            }
1446
            else if (decpt)
1447
            {
1448
                /* 
1449
                 *   we have a leading insignificant zero following the
1450
                 *   decimal point - decrease the exponent 
1451
                 */
1452
                --exp;
1453
            }
1454
        }
1455
        else if (!decpt && ch == '.')
1456
        {
1457
            /* we've found the decimal point - note it */
1458
            decpt = TRUE;
1459
        }
1460
        else if (ch == 'e' || ch == 'E')
1461
        {
1462
            int neg_exp = FALSE;
1463
            long acc;
1464
            
1465
            /* we've found our exponent - check for a sign */
1466
            p.inc(&rem);
1467
            if (rem != 0)
1468
            {
1469
                if (p.getch() == '-')
1470
                {
1471
                    /* note the sign and skip the '-' */
1472
                    neg_exp = TRUE;
1473
                    p.inc(&rem);
1474
                }
1475
                else if (p.getch() == '+')
1476
                {
1477
                    /* skip the '+' */
1478
                    p.inc(&rem);
1479
                }
1480
            }
1481
1482
            /* scan the digits */
1483
            for (acc = 0 ; rem != 0 ; p.inc(&rem))
1484
            {
1485
                long new_acc;
1486
                
1487
                /* if this isn't a digit, we're done */
1488
                if (!is_digit(p.getch()))
1489
                    break;
1490
1491
                /* add in this digit */
1492
                new_acc = acc*10 + value_of_digit(p.getch());
1493
                if ((!neg_exp && new_acc > 32767)
1494
                    || (neg_exp && new_acc > 32768))
1495
                    break;
1496
1497
                /* set the new accumulator */
1498
                acc = new_acc;
1499
            }
1500
1501
            /* set the sign */
1502
            if (neg_exp)
1503
                acc = -acc;
1504
1505
            /* 
1506
             *   add this exponent to the exponent we derived for the
1507
             *   number itself 
1508
             */
1509
            exp = (int)(exp + acc);
1510
1511
            /* 
1512
             *   since we scanned the string in our own loop, make sure we
1513
             *   haven't reached the end of the buffer - if we have, we're
1514
             *   done with the outer loop now 
1515
             */
1516
            if (rem == 0)
1517
                break;
1518
        }
1519
        else
1520
        {
1521
            /* 
1522
             *   anything else is invalid, so we've reached the end of the
1523
             *   number - stop scanning
1524
             */
1525
            break;
1526
        }
1527
    }
1528
1529
    /* if we have a pending digit, write it out */
1530
    if (dig_idx == 1)
1531
        str->write((char)(dig[0] << 4));
1532
1533
    /* 
1534
     *   if we had no significant digits, the number is all zeroes, so in
1535
     *   this special case treat all of the zeroes as significant 
1536
     */
1537
    if (prec == 0 && tot_digits != 0)
1538
    {
1539
        /* note that the value is zero */
1540
        val_zero = TRUE;
1541
        
1542
        /* use the zeroes as significant digits */
1543
        prec = tot_digits;
1544
        
1545
        /* write out the zeroes */
1546
        for ( ; tot_digits > 1 ; tot_digits -= 2)
1547
            str->write(0);
1548
        if (tot_digits > 0)
1549
            str->write(0);
1550
1551
        /* 
1552
         *   the exponent for the value zero is always 1 (this is a
1553
         *   normalization rule) 
1554
         */
1555
        exp = 1;
1556
    }
1557
1558
    /* construct the flags */
1559
    flags = 0;
1560
    if (neg)
1561
        flags |= VMBN_F_NEG;
1562
    if (val_zero)
1563
        flags |= VMBN_F_ZERO;
1564
1565
    /* go back and fix up the precision, exponent, and flags values */
1566
    str->write2_at(num_ofs, prec);
1567
    str->write2_at(num_ofs + 2, exp);
1568
    str->write_at(num_ofs + 4, flags);
1569
1570
    /* fix up the size */
1571
    end_ofs = str->get_ofs();
1572
    str->write2_at(size_ofs, end_ofs - size_ofs - 2);
1573
1574
    /* return the new object ID */
1575
    return id;
1576
}
1577
1578
/*
1579
 *   Convert a constant value from a CTcConstVal (compiler internal
1580
 *   representation) to a vm_val_t (interpreter representation). 
1581
 */
1582
void CTcGenTarg::write_const_as_dh(CTcDataStream *ds, ulong ofs,
1583
                                   const CTcConstVal *src)
1584
{
1585
    vm_val_t val;
1586
    char buf[VMB_DATAHOLDER];
1587
1588
    /* convert according to the value's type */
1589
    switch(src->get_type())
1590
    {
1591
    case TC_CVT_NIL:
1592
        val.set_nil();
1593
        break;
1594
1595
    case TC_CVT_TRUE:
1596
        val.set_true();
1597
        break;
1598
1599
    case TC_CVT_INT:
1600
        val.set_int(src->get_val_int());
1601
        break;
1602
1603
    case TC_CVT_FLOAT:
1604
        /* generate the BigNumber object */
1605
        val.set_obj(gen_bignum_obj(src->get_val_float(),
1606
                                   src->get_val_float_len()));
1607
1608
        /* add a fixup for the object ID */
1609
        if (G_keep_objfixups)
1610
            CTcIdFixup::add_fixup(&G_objfixup, ds, ofs + 1, val.val.obj);
1611
        break;
1612
1613
    case TC_CVT_SSTR:
1614
        /* 
1615
         *   Store the string in the constant pool.  Note that our fixup
1616
         *   is at the destination stream offset plus one, since the
1617
         *   DATAHOLDER has the type byte followed by the offset value.  
1618
         */
1619
        add_const_str(src->get_val_str(), src->get_val_str_len(),
1620
                      ds, ofs + 1);
1621
        
1622
        /* 
1623
         *   set the offset to zero for now - the fixup that
1624
         *   add_const_str() generates will take care of supplying the
1625
         *   real value 
1626
         */
1627
        val.set_sstring(0);
1628
        break;
1629
1630
    case TC_CVT_LIST:
1631
        /* 
1632
         *   Store the sublist in the constant pool.  Our fixup is at the
1633
         *   destination stream offset plus one, since the DATAHOLDER has
1634
         *   the type byte followed by the offset value.  
1635
         */
1636
        add_const_list(src->get_val_list(), ds, ofs + 1);
1637
1638
        /* 
1639
         *   set the offset to zero for now - the fixup that
1640
         *   add_const_list() generates will take care of supplying the
1641
         *   real value 
1642
         */
1643
        val.set_list(0);
1644
        break;
1645
1646
    case TC_CVT_OBJ:
1647
        /* set the object ID value */
1648
        val.set_obj((vm_obj_id_t)src->get_val_obj());
1649
1650
        /* 
1651
         *   add a fixup (at the current offset plus one, for the type
1652
         *   byte) if we're keeping object ID fixups 
1653
         */
1654
        if (G_keep_objfixups)
1655
            CTcIdFixup::add_fixup(&G_objfixup, ds, ofs + 1,
1656
                                  src->get_val_obj());
1657
        break;
1658
1659
    case TC_CVT_ENUM:
1660
        /* set the enum value */
1661
        val.set_enum(src->get_val_enum());
1662
1663
        /* add a fixup */
1664
        if (G_keep_enumfixups)
1665
            CTcIdFixup::add_fixup(&G_enumfixup, ds, ofs + 1,
1666
                                  src->get_val_enum());
1667
        break;
1668
1669
    case TC_CVT_PROP:
1670
        /* set the property ID value */
1671
        val.set_propid((vm_prop_id_t)src->get_val_prop());
1672
1673
        /* 
1674
         *   add a fixup (at the current offset plus one, for the type
1675
         *   byte) if we're keeping property ID fixups 
1676
         */
1677
        if (G_keep_propfixups)
1678
            CTcIdFixup::add_fixup(&G_propfixup, ds, ofs + 1,
1679
                                  src->get_val_prop());
1680
        break;
1681
1682
    case TC_CVT_FUNCPTR:
1683
        /* 
1684
         *   use a placeholder value of zero for now - a function's final
1685
         *   address is never known until after all code generation has
1686
         *   been completed (the fixup will take care of supplying the
1687
         *   correct value when the time comes) 
1688
         */
1689
        val.set_fnptr(0);
1690
1691
        /* 
1692
         *   Add a fixup.  The fixup is at the destination stream offset
1693
         *   plus one, because the DATAHOLDER has a type byte followed by
1694
         *   the function pointer value.  
1695
         */
1696
        src->get_val_funcptr_sym()->add_abs_fixup(ds, ofs + 1);
1697
        break;
1698
1699
    case TC_CVT_ANONFUNCPTR:
1700
        /* use a placeholder of zero for now, until we fix up the pointer */
1701
        val.set_fnptr(0);
1702
1703
        /* add a fixup for the code body */
1704
        src->get_val_anon_func_ptr()->add_abs_fixup(ds, ofs + 1);
1705
        break;
1706
1707
    case TC_CVT_VOCAB_LIST:
1708
        /* 
1709
         *   it's an internal vocabulary list type - this is used as a
1710
         *   placeholder only, and will be replaced during linking with an
1711
         *   actual vocabulary string list 
1712
         */
1713
        val.typ = VM_VOCAB_LIST;
1714
        break;
1715
1716
    case TC_CVT_UNK:
1717
        /* unknown - ignore it */
1718
        break;
1719
    }
1720
1721
    /* write the vm_val_t in DATA_HOLDER format into the stream */
1722
    vmb_put_dh(buf, &val);
1723
    ds->write_at(ofs, buf, VMB_DATAHOLDER);
1724
}
1725
1726
/*
1727
 *   Write a DATAHOLDER at the current offset in a stream
1728
 */
1729
void CTcGenTarg::write_const_as_dh(CTcDataStream *ds, 
1730
                                   const CTcConstVal *src)
1731
{
1732
    /* write to the current stream offset */
1733
    write_const_as_dh(ds, ds->get_ofs(), src);
1734
}
1735
1736
/*
1737
 *   Notify that parsing is finished 
1738
 */
1739
void CTcGenTarg::parsing_done()
1740
{
1741
    /* nothing special to do */
1742
}
1743
1744
1745
/*
1746
 *   notify the code generator that we're replacing an object 
1747
 */
1748
void CTcGenTarg::notify_replace_object(ulong stream_ofs)
1749
{
1750
    uint flags;
1751
1752
    /* set the 'replaced' flag in the flags prefix */
1753
    flags = G_os->read2_at(stream_ofs);
1754
    flags |= TCT3_OBJ_REPLACED;
1755
    G_os->write2_at(stream_ofs, flags);
1756
}
1757
1758
1759
/*
1760
 *   Set the starting offset of the current method 
1761
 */
1762
void CTcGenTarg::set_method_ofs(ulong ofs)
1763
{
1764
    /* tell the exception table object about it */
1765
    get_exc_table()->set_method_ofs(ofs);
1766
1767
    /* remember it in the code stream */
1768
    G_cs->set_method_ofs(ofs);
1769
}
1770
1771
/*
1772
 *   Add a debug line table to our list 
1773
 */
1774
void CTcGenTarg::add_debug_line_table(ulong ofs)
1775
{
1776
    size_t idx;
1777
    uchar *p;
1778
1779
    /* calculate the index of the next free entry on its page */
1780
    idx = (size_t)(debug_line_cnt_ % TCT3_DEBUG_LINE_PAGE_SIZE);
1781
    
1782
    /* 
1783
     *   if we've completely filled the last page, allocate a new one - we
1784
     *   know we've exhausted the page if we're at the start of a new page
1785
     *   (i.e., the index is zero) 
1786
     */
1787
    if (idx == 0)
1788
    {
1789
        tct3_debug_line_page *pg;
1790
        
1791
        /* allocate the new page */
1792
        pg = (tct3_debug_line_page *)t3malloc(sizeof(*pg));
1793
1794
        /* link it in at the end of the list */
1795
        pg->nxt = 0;
1796
        if (debug_line_tail_ == 0)
1797
            debug_line_head_ = pg;
1798
        else
1799
            debug_line_tail_->nxt = pg;
1800
        debug_line_tail_ = pg;
1801
    }
1802
1803
    /* get a pointer to the entry */
1804
    p = debug_line_tail_->line_ofs + (idx * TCT3_DEBUG_LINE_REC_SIZE);
1805
1806
    /* 
1807
     *   set this entry - one byte for the code stream ID, then a UINT4
1808
     *   with the offset in the stream
1809
     */
1810
    *p = G_cs->get_stream_id();
1811
    oswp4(p + 1, ofs);
1812
1813
    /* count it */
1814
    ++debug_line_cnt_;
1815
}
1816
1817
/* ------------------------------------------------------------------------ */
1818
/*
1819
 *   Method header generator
1820
 */
1821
1822
/*
1823
 *   Open a method 
1824
 */
1825
void CTcGenTarg::open_method(CTcCodeStream *stream,
1826
                             CTcSymbol *fixup_owner_sym,
1827
                             CTcAbsFixup **fixup_list_head,
1828
                             CTPNCodeBody *code_body,
1829
                             CTcPrsSymtab *goto_tab,
1830
                             int argc, int varargs,
1831
                             int is_constructor, int is_self_available,
1832
                             tct3_method_gen_ctx *ctx)
1833
{
1834
    /* set the code stream as the current code generator output stream */
1835
    G_cs = ctx->stream = stream;
1836
1837
    /* set the method properties in the code generator */
1838
    set_in_constructor(is_constructor);
1839
    stream->set_self_available(is_self_available);
1840
1841
    /* we obviously can't combine any past instructions */
1842
    clear_peephole();
1843
1844
    /* clear the old line records */
1845
    stream->clear_line_recs();
1846
1847
    /* clear the old frame list */
1848
    stream->clear_local_frames();
1849
1850
    /* clear the old exception table */
1851
    get_exc_table()->clear_table();
1852
1853
    /* reset the stack depth counters */
1854
    reset_sp_depth();
1855
1856
    /* there are no enclosing 'switch' or block statements yet */
1857
    stream->set_switch(0);
1858
    stream->set_enclosing(0);
1859
1860
    /* 
1861
     *   remember where the method header starts - we'll need to come back
1862
     *   and fix up some placeholder entries in "Close" 
1863
     */
1864
    ctx->method_ofs = stream->get_ofs();
1865
1866
    /* set the method start offset in the code generator */
1867
    set_method_ofs(ctx->method_ofs);
1868
1869
    /* set up a fixup anchor for the new method */
1870
    ctx->anchor = stream->add_anchor(fixup_owner_sym, fixup_list_head);
1871
1872
    /* set the anchor in the associated symbol, if applicable */
1873
    if (fixup_owner_sym != 0)
1874
        fixup_owner_sym->set_anchor(ctx->anchor);
1875
1876
    /* 
1877
     *   Generate the function header.  At the moment, we don't know the
1878
     *   stack usage, exception table offset, or debug record offset, since
1879
     *   these all come after the byte code; we won't know how big the byte
1880
     *   code is until after we generate it.  For now, write zero bytes as
1881
     *   placeholders for these slots; we'll come back and fix them up to
1882
     *   their real values after we've generated the byte code.  
1883
     */
1884
    stream->write(argc | (varargs ? 0x80 : 0));           /* argument count */
1885
    stream->write(0);                                 /* reserved zero byte */
1886
    stream->write2(0); /* number of locals - won't know until after codegen */
1887
    stream->write2(0);      /* total stack - won't know until after codegen */
1888
    stream->write2(0);             /* exception table offset - presume none */
1889
    stream->write2(0);    /* debug record offset - presume no debug records */
1890
1891
    /* remember the starting offset of the code */
1892
    ctx->code_start_ofs = stream->get_ofs();
1893
1894
    /* set the current code body being generated */
1895
    ctx->old_code_body = stream->set_code_body(code_body);
1896
1897
    /* get the 'goto' symbol table for this function */
1898
    stream->set_goto_symtab(goto_tab);
1899
}
1900
1901
/*
1902
 *   Close a method 
1903
 */
1904
void CTcGenTarg::close_method(int local_cnt,
1905
                              CTcTokFileDesc *end_desc, long end_linenum,
1906
                              tct3_method_gen_ctx *ctx)
1907
{
1908
    /* get the output code stream from the context */
1909
    CTcCodeStream *stream = ctx->stream;
1910
    
1911
    /* 
1912
     *   Generate a 'return' opcode with a default 'nil' return value - this
1913
     *   will ensure that code that reaches the end of the procedure returns
1914
     *   normally.  If this is a constructor, return the 'self' object rather
1915
     *   than nil.
1916
     *   
1917
     *   We only need to generate this epilogue if the next instruction would
1918
     *   be reachable.  If it's not reachable, then the code explicitly took
1919
     *   care of all types of exits.  
1920
     */
1921
    if (!can_skip_op())
1922
    {
1923
        /* 
1924
         *   add a line record for the implied return at the last source line
1925
         *   of the code body 
1926
         */
1927
        add_line_rec(end_desc, end_linenum);
1928
1929
        /* write the appropriate return */
1930
        if (is_in_constructor())
1931
        {
1932
            /* we're in a constructor - return 'self' */
1933
            write_op(OPC_PUSHSELF);
1934
            write_op(OPC_RETVAL);
1935
        }
1936
        else
1937
        {
1938
            /* 
1939
             *   Normal method/function - return without a value (explicitly
1940
             *   set R0 to nil, though, so we don't return something returned
1941
             *   from a called function).  
1942
             */
1943
            write_op(OPC_RETNIL);
1944
        }
1945
    }
1946
1947
    /* 
1948
     *   release labels allocated for the code block; this will log an error
1949
     *   if any labels are not defined 
1950
     */
1951
    stream->release_labels();
1952
1953
    /* 
1954
     *   Eliminate jump-to-jump sequences in the generated code.  Don't
1955
     *   bother if we've found any errors, as the generated code will not
1956
     *   necessarily be valid if this is the case.  
1957
     */
1958
    if (G_tcmain->get_error_count() == 0)
1959
        remove_jumps_to_jumps(stream, ctx->code_start_ofs);
1960
1961
    /* note the code block's end point */
1962
    ctx->code_end_ofs = stream->get_ofs();
1963
1964
    /*
1965
     *   Fix up the local variable count in the function header.  We might
1966
     *   allocate extra locals for internal use while generating code, so we
1967
     *   must wait until after generating our code before we know the final
1968
     *   local count.  
1969
     */
1970
    stream->write2_at(ctx->method_ofs + 2, local_cnt);
1971
1972
    /* 
1973
     *   Fix up the total stack space indicator in the function header.  The
1974
     *   total stack size must include the locals, as well as stack space
1975
     *   needed for intermediate computations.  
1976
     */
1977
    stream->write2_at(ctx->method_ofs + 4, get_max_sp_depth() + local_cnt);
1978
1979
    /* 
1980
     *   Generate the exception table, if we have one.  If we have no
1981
     *   exception records, leave the exception table offset set to zero to
1982
     *   indicate that there is no exception table for the method.  
1983
     */
1984
    if (get_exc_table()->get_entry_count() != 0)
1985
    {
1986
        /* 
1987
         *   write the exception table offset - it's at the current offset in
1988
         *   the code 
1989
         */
1990
        stream->write2_at(ctx->method_ofs + 6,
1991
                          stream->get_ofs() - ctx->method_ofs);
1992
1993
        /* write the table */
1994
        get_exc_table()->write_to_code_stream();
1995
    }
1996
}
1997
1998
/*
1999
 *   Clean up after generating a method 
2000
 */
2001
void CTcGenTarg::close_method_cleanup(tct3_method_gen_ctx *ctx)
2002
{
2003
    /*
2004
     *   Tell the code generator our code block byte length so that it can
2005
     *   keep track of the longest single byte-code block; it will use this
2006
     *   to choose the code pool page size when generating the image file.  
2007
     */
2008
    note_bytecode(ctx->anchor->get_len(ctx->stream));
2009
2010
    /* we're no longer in a constructor, if we ever were */
2011
    set_in_constructor(FALSE);
2012
2013
    /* clear the current code body */
2014
    ctx->stream->set_code_body(ctx->old_code_body);
2015
2016
    /* always leave the main code stream active by default */
2017
    G_cs = G_cs_main;
2018
}
2019
2020
/* ------------------------------------------------------------------------ */
2021
/*
2022
 *   Run through the generated code stream starting at the given offset (and
2023
 *   running up to the current offset), and eliminate jump-to-jump sequences:
2024
 *   
2025
 *   - whenever we find any jump instruction that points directly to an
2026
 *   unconditional jump, we'll change the first jump so that it points to
2027
 *   the target of the second jump, saving the unnecessary stop at the
2028
 *   intermediate jump
2029
 *   
2030
 *   - whenever we find an unconditional jump to any return or throw
2031
 *   instruction, we'll replace the jump with a copy of the target
2032
 *   return/throw instruction.  
2033
 */
2034
void CTcGenTarg::remove_jumps_to_jumps(CTcCodeStream *str, ulong start_ofs)
2035
{
2036
    ulong ofs;
2037
    ulong end_ofs;
2038
    uchar prv_op;
2039
    static const size_t op_siz[] =
2040
    {
2041
        0,                                                 /* 0x00 - unused */
2042
2043
        1,                                             /* 0x01 - OPC_PUSH_0 */
2044
        1,                                             /* 0x02 - OPC_PUSH_1 */
2045
        2,                                           /* 0x03 - OPC_PUSHINT8 */
2046
        5,                                            /* 0x04 - OPC_PUSHINT */
2047
        5,                                            /* 0x05 - OPC_PUSHSTR */
2048
        5,                                            /* 0x06 - OPC_PUSHLST */
2049
        5,                                            /* 0x07 - OPC_PUSHOBJ */
2050
        1,                                            /* 0x08 - OPC_PUSHNIL */
2051
        1,                                           /* 0x09 - OPC_PUSHTRUE */
2052
        3,                                         /* 0x0A - OPC_PUSHPROPID */
2053
        5,                                          /* 0x0B - OPC_PUSHFNPTR */
2054
        0,               /* 0x0C - OPC_PUSHSTRI - variable-size instruction */
2055
        2,                                         /* 0x0D - OPC_PUSHPARLST */
2056
        1,                                         /* 0x0E - OPC_MAKELSTPAR */
2057
        5,                                           /* 0x0F - OPC_PUSHENUM */
2058
2059
        1,                                                 /* 0x10 - unused */
2060
        1,                                                 /* 0x11 - unused */
2061
        1,                                                 /* 0x12 - unused */
2062
        1,                                                 /* 0x13 - unused */
2063
        1,                                                 /* 0x14 - unused */
2064
        1,                                                 /* 0x15 - unused */
2065
        1,                                                 /* 0x16 - unused */
2066
        1,                                                 /* 0x17 - unused */
2067
        1,                                                 /* 0x18 - unused */
2068
        1,                                                 /* 0x19 - unused */
2069
        1,                                                 /* 0x1A - unused */
2070
        1,                                                 /* 0x1B - unused */
2071
        1,                                                 /* 0x1C - unused */
2072
        1,                                                 /* 0x1D - unused */
2073
        1,                                                 /* 0x1E - unused */
2074
        1,                                                 /* 0x1F - unused */
2075
2076
        1,                                                /* 0x20 - OPC_NEG */
2077
        1,                                               /* 0x21 - OPC_BNOT */
2078
        1,                                                /* 0x22 - OPC_ADD */
2079
        1,                                                /* 0x23 - OPC_SUB */
2080
        1,                                                /* 0x24 - OPC_MUL */
2081
        1,                                               /* 0x25 - OPC_BAND */
2082
        1,                                                /* 0x26 - OPC_BOR */
2083
        1,                                                /* 0x27 - OPC_SHL */
2084
        1,                                                /* 0x28 - OPC_SHR */
2085
        1,                                                /* 0x29 - OPC_XOR */
2086
        1,                                                /* 0x2A - OPC_DIV */
2087
        1,                                                /* 0x2B - OPC_MOD */
2088
        1,                                                /* 0x2C - OPC_NOT */
2089
        1,                                            /* 0x2D - OPC_BOOLIZE */
2090
        1,                                                /* 0x2E - OPC_INC */
2091
        1,                                                /* 0x2F - OPC_DEC */
2092
2093
        1,                                                 /* 0x30 - unused */
2094
        1,                                                 /* 0x31 - unused */
2095
        1,                                                 /* 0x32 - unused */
2096
        1,                                                 /* 0x33 - unused */
2097
        1,                                                 /* 0x34 - unused */
2098
        1,                                                 /* 0x35 - unused */
2099
        1,                                                 /* 0x36 - unused */
2100
        1,                                                 /* 0x37 - unused */
2101
        1,                                                 /* 0x38 - unused */
2102
        1,                                                 /* 0x39 - unused */
2103
        1,                                                 /* 0x3A - unused */
2104
        1,                                                 /* 0x3B - unused */
2105
        1,                                                 /* 0x3C - unused */
2106
        1,                                                 /* 0x3D - unused */
2107
        1,                                                 /* 0x3E - unused */
2108
        1,                                                 /* 0x3F - unused */
2109
2110
        1,                                                 /* 0x40 - OPC_EQ */
2111
        1,                                                 /* 0x41 - OPC_NE */
2112
        1,                                                 /* 0x42 - OPC_LT */
2113
        1,                                                 /* 0x43 - OPC_LE */
2114
        1,                                                 /* 0x44 - OPC_GT */
2115
        1,                                                 /* 0x45 - OPC_GE */
2116
2117
        1,                                                 /* 0x46 - unused */
2118
        1,                                                 /* 0x47 - unused */
2119
        1,                                                 /* 0x48 - unused */
2120
        1,                                                 /* 0x49 - unused */
2121
        1,                                                 /* 0x4A - unused */
2122
        1,                                                 /* 0x4B - unused */
2123
        1,                                                 /* 0x4C - unused */
2124
        1,                                                 /* 0x4D - unused */
2125
        1,                                                 /* 0x4E - unused */
2126
        1,                                                 /* 0x4F - unused */
2127
2128
        1,                                             /* 0x50 - OPC_RETVAL */
2129
        1,                                             /* 0x51 - OPC_RETNIL */
2130
        1,                                            /* 0x52 - OPC_RETTRUE */
2131
        1,                                                 /* 0x53 - unused */
2132
        1,                                                /* 0x54 - OPC_RET */
2133
2134
        1,                                                 /* 0x55 - unused */
2135
        1,                                                 /* 0x56 - unused */
2136
        1,                                                 /* 0x57 - unused */
2137
2138
        6,                                               /* 0x58 - OPC_CALL */
2139
        2,                                            /* 0x59 - OPC_PTRCALL */
2140
2141
        1,                                                 /* 0x5A - unused */
2142
        1,                                                 /* 0x5B - unused */
2143
        1,                                                 /* 0x5C - unused */
2144
        1,                                                 /* 0x5D - unused */
2145
        1,                                                 /* 0x5E - unused */
2146
        1,                                                 /* 0x5F - unused */
2147
2148
        3,                                            /* 0x60 - OPC_GETPROP */
2149
        4,                                           /* 0x61 - OPC_CALLPROP */
2150
        2,                                        /* 0x62 - OPC_PTRCALLPROP */
2151
        3,                                        /* 0x63 - OPC_GETPROPSELF */
2152
        4,                                       /* 0x64 - OPC_CALLPROPSELF */
2153
        2,                                    /* 0x65 - OPC_PTRCALLPROPSELF */
2154
        7,                                         /* 0x66 - OPC_OBJGETPROP */
2155
        8,                                        /* 0x67 - OPC_OBJCALLPROP */
2156
        3,                                        /* 0x68 - OPC_GETPROPDATA */
2157
        1,                                     /* 0x69 - OPC_PTRGETPROPDATA */
2158
        4,                                        /* 0x6A - OPC_GETPROPLCL1 */
2159
        5,                                       /* 0x6B - OPC_CALLPROPLCL1 */
2160
        3,                                          /* 0x6C - OPC_GETPROPR0 */
2161
        4,                                         /* 0x6D - OPC_CALLPROPR0 */
2162
2163
        1,                                                 /* 0x6E - unused */
2164
        1,                                                 /* 0x6F - unused */
2165
        1,                                                 /* 0x70 - unused */
2166
        1,                                                 /* 0x71 - unused */
2167
2168
        4,                                            /* 0x72 - OPC_INHERIT */
2169
        2,                                         /* 0x73 - OPC_PTRINHERIT */
2170
        8,                                         /* 0x74 - OPC_EXPINHERIT */
2171
        6,                                      /* 0x75 - OPC_PTREXPINHERIT */
2172
        1,                                            /* 0x76 - OPC_VARARGC */
2173
        4,                                           /* 0x77 - OPC_DELEGATE */
2174
        2,                                        /* 0x78 - OPC_PTRDELEGATE */
2175
2176
        1,                                                 /* 0x79 - unused */
2177
        1,                                                 /* 0x7A - unused */
2178
        1,                                                 /* 0x7B - unused */
2179
        1,                                                 /* 0x7C - unused */
2180
        1,                                                 /* 0x7D - unused */
2181
        1,                                                 /* 0x7E - unused */
2182
        1,                                                 /* 0x7F - unused */
2183
2184
        2,                                            /* 0x80 - OPC_GETLCL1 */
2185
        3,                                            /* 0x81 - OPC_GETLCL2 */
2186
        2,                                            /* 0x82 - OPC_GETARG1 */
2187
        3,                                            /* 0x83 - OPC_GETARG2 */
2188
        1,                                           /* 0x84 - OPC_PUSHSELF */
2189
        5,                                           /* 0x85 - OPC_GETDBLCL */
2190
        5,                                           /* 0x86 - OPC_GETDBARG */
2191
        1,                                            /* 0x87 - OPC_GETARGC */
2192
        1,                                                /* 0x88 - OPC_DUP */
2193
        1,                                               /* 0x89 - OPC_DISC */
2194
        2,                                              /* 0x8A - OPC_DISC1 */
2195
        1,                                              /* 0x8B - OPC_GETR0 */
2196
        3,                                          /* 0x8C - OPC_GETDBARGC */
2197
        1,                                               /* 0x8D - OPC_SWAP */
2198
2199
        2,                                         /* 0x8E - OPC_PUSHCTXELE */
2200
2201
        1,                                                 /* 0x8F - unused */
2202
2203
        0,                 /* 0x90 - OPC_SWITCH - variable-size instruction */
2204
        3,                                                /* 0x91 - OPC_JMP */
2205
        3,                                                 /* 0x92 - OPC_JT */
2206
        3,                                                 /* 0x93 - OPC_JF */
2207
        3,                                                 /* 0x94 - OPC_JE */
2208
        3,                                                /* 0x95 - OPC_JNE */
2209
        3,                                                /* 0x96 - OPC_JGT */
2210
        3,                                                /* 0x97 - OPC_JGE */
2211
        3,                                                /* 0x98 - OPC_JLT */
2212
        3,                                                /* 0x99 - OPC_JLE */
2213
        3,                                                /* 0x9A - OPC_JST */
2214
        3,                                                /* 0x9B - OPC_JSF */
2215
        3,                                               /* 0x9C - OPC_LJSR */
2216
        3,                                               /* 0x9D - OPC_LRET */
2217
        3,                                               /* 0x9E - OPC_JNIL */
2218
        3,                                            /* 0x9F - OPC_JNOTNIL */
2219
        3,                                               /* 0xA0 - OPC_JR0T */
2220
        3,                                               /* 0xA1 - OPC_JR0F */
2221
2222
        1,                                                 /* 0xA2 - unused */
2223
        1,                                                 /* 0xA3 - unused */
2224
        1,                                                 /* 0xA4 - unused */
2225
        1,                                                 /* 0xA5 - unused */
2226
        1,                                                 /* 0xA6 - unused */
2227
        1,                                                 /* 0xA7 - unused */
2228
        1,                                                 /* 0xA8 - unused */
2229
        1,                                                 /* 0xA9 - unused */
2230
        1,                                                 /* 0xAA - unused */
2231
        1,                                                 /* 0xAB - unused */
2232
        1,                                                 /* 0xAC - unused */
2233
        1,                                                 /* 0xAD - unused */
2234
        1,                                                 /* 0xAE - unused */
2235
        1,                                                 /* 0xAF - unused */
2236
2237
        5,                                                /* 0xB0 - OPC_SAY */
2238
        3,                                          /* 0xB1 - OPC_BUILTIN_A */
2239
        3,                                          /* 0xB2 - OPC_BUILTIN_B */
2240
        3,                                          /* 0xB3 - OPC_BUILTIN_C */
2241
        3,                                          /* 0xB4 - OPC_BUILTIN_D */
2242
        3,                                           /* 0xB5 - OPC_BUILTIN1 */
2243
        4,                                           /* 0xB6 - OPC_BUILTIN2 */
2244
        0,      /* 0xB7 - OPC_CALLEXT (reserved; not currently implemented) */
2245
        1,                                              /* 0xB8 - OPC_THROW */
2246
        1,                                             /* 0xB9 - OPC_SAYVAL */
2247
2248
        1,                                              /* 0xBA - OPC_INDEX */
2249
        3,                                        /* 0xBB - OPC_IDXLCL1INT8 */
2250
        2,                                           /* 0xBC - OPC_IDXLINT8 */
2251
2252
        1,                                                 /* 0xBD - unused */
2253
        1,                                                 /* 0xBE - unused */
2254
        1,                                                 /* 0xBF - unused */
2255
2256
        3,                                               /* 0xC0 - OPC_NEW1 */
2257
        5,                                               /* 0xC1 - OPC_NEW2 */
2258
        3,                                             /* 0xC2 - OPC_TRNEW1 */
2259
        5,                                             /* 0xC3 - OPC_TRNEW2 */
2260
2261
        1,                                                 /* 0xC4 - unused */
2262
        1,                                                 /* 0xC5 - unused */
2263
        1,                                                 /* 0xC6 - unused */
2264
        1,                                                 /* 0xC7 - unused */
2265
        1,                                                 /* 0xC8 - unused */
2266
        1,                                                 /* 0xC9 - unused */
2267
        1,                                                 /* 0xCA - unused */
2268
        1,                                                 /* 0xCB - unused */
2269
        1,                                                 /* 0xCC - unused */
2270
        1,                                                 /* 0xCD - unused */
2271
        1,                                                 /* 0xCE - unused */
2272
        1,                                                 /* 0xCF - unused */
2273
2274
        3,                                             /* 0xD0 - OPC_INCLCL */
2275
        3,                                             /* 0xD1 - OPC_DECLCL */
2276
        3,                                           /* 0xD2 - OPC_ADDILCL1 */
2277
        7,                                           /* 0xD3 - OPC_ADDILCL4 */
2278
        3,                                           /* 0xD4 - OPC_ADDTOLCL */
2279
        3,                                         /* 0xD5 - OPC_SUBFROMLCL */
2280
        2,                                           /* 0xD6 - OPC_ZEROLCL1 */
2281
        3,                                           /* 0xD7 - OPC_ZEROLCL2 */
2282
        2,                                            /* 0xD8 - OPC_NILLCL1 */
2283
        3,                                            /* 0xD9 - OPC_NILLCL2 */
2284
        2,                                            /* 0xDA - OPC_ONELCL1 */
2285
        3,                                            /* 0xDB - OPC_ONELCL2 */
2286
2287
        1,                                                 /* 0xDC - unused */
2288
        1,                                                 /* 0xDD - unused */
2289
        1,                                                 /* 0xDE - unused */
2290
        1,                                                 /* 0xDF - unused */
2291
2292
        2,                                            /* 0xE0 - OPC_SETLCL1 */
2293
        3,                                            /* 0xE1 - OPC_SETLCL2 */
2294
        2,                                            /* 0xE2 - OPC_SETARG1 */
2295
        3,                                            /* 0xE3 - OPC_SETARG2 */
2296
        1,                                             /* 0xE4 - OPC_SETIND */
2297
        3,                                            /* 0xE5 - OPC_SETPROP */
2298
        1,                                         /* 0xE6 - OPC_PTRSETPROP */
2299
        3,                                        /* 0xE7 - OPC_SETPROPSELF */
2300
        7,                                         /* 0xE8 - OPC_OBJSETPROP */
2301
        5,                                           /* 0xE9 - OPC_SETDBLCL */
2302
        5,                                           /* 0xEA - OPC_SETDBARG */
2303
2304
        1,                                            /* 0xEB - OPC_SETSELF */
2305
        1,                                            /* 0xEC - OPC_LOADCTX */
2306
        1,                                           /* 0xED - OPC_STORECTX */
2307
        2,                                          /* 0xEE - OPC_SETLCL1R0 */
2308
        3,                                       /* 0xEF - OPC_SETINDLCL1I8 */
2309
2310
        1,                                                 /* 0xF0 - unused */
2311
2312
        1,                                                 /* 0xF1 - OPC_BP */
2313
        1,                                                /* 0xF2 - OPC_NOP */
2314
2315
        1,                                                 /* 0xF3 - unused */
2316
        1,                                                 /* 0xF4 - unused */
2317
        1,                                                 /* 0xF5 - unused */
2318
        1,                                                 /* 0xF6 - unused */
2319
        1,                                                 /* 0xF7 - unused */
2320
        1,                                                 /* 0xF8 - unused */
2321
        1,                                                 /* 0xF9 - unused */
2322
        1,                                                 /* 0xFA - unused */
2323
        1,                                                 /* 0xFB - unused */
2324
        1,                                                 /* 0xFC - unused */
2325
        1,                                                 /* 0xFD - unused */
2326
        1,                                                 /* 0xFE - unused */
2327
        255,                                               /* 0xFF - unused */
2328
    };
2329
    
2330
    /* 
2331
     *   scan the code stream starting at the given offset, continuing
2332
     *   through the current offset 
2333
     */
2334
    prv_op = OPC_NOP;
2335
    for (ofs = start_ofs, end_ofs = str->get_ofs() ; ofs < end_ofs ; )
2336
    {
2337
        uchar op;
2338
        ulong target_ofs;
2339
        ulong orig_target_ofs;
2340
        uchar target_op;
2341
        int done;
2342
        int chain_len;
2343
        
2344
        /* check the byte code instruction at the current location */
2345
        switch(op = str->get_byte_at(ofs))
2346
        {
2347
        case OPC_RETVAL:
2348
            /* 
2349
             *   If our previous opcode was PUSHTRUE or PUSHNIL, we can
2350
             *   replace the previous opcode with RETTRUE or RETNIL.  This
2351
             *   sequence can occur when we generate conditional code that
2352
             *   returns a value; in such cases, we sometimes can't elide
2353
             *   the PUSHx/RETVAL sequence during the original code
2354
             *   generation because the RETVAL itself is the target of a
2355
             *   label and thus must be retained as a separate instruction.
2356
             *   Converting the PUSHTRUE or PUSHNIL here won't do any harm,
2357
             *   as we'll still leave the RETVAL as a separate instruction.
2358
             *   Likewise, if the previous instruction was GET_R0, we can
2359
             *   change it to a simple RET.  
2360
             */
2361
            switch(prv_op)
2362
            {
2363
            case OPC_PUSHTRUE:
2364
                /* convert the PUSHTRUE to a RETTRUE */
2365
                str->write_at(ofs - 1, OPC_RETTRUE);
2366
                break;
2367
2368
            case OPC_PUSHNIL:
2369
                /* convert the PUSHNIL to a RETNIL */
2370
                str->write_at(ofs - 1, OPC_RETNIL);
2371
                break;
2372
2373
            case OPC_GETR0:
2374
                /* convert the GETR0 to a RET */
2375
                str->write_at(ofs - 1, OPC_RET);
2376
                break;
2377
            }
2378
2379
            /* skip the RETVAL */
2380
            ofs += 1;
2381
            break;
2382
            
2383
        case OPC_PUSHSTRI:
2384
            /* 
2385
             *   push in-line string: we have a UINT2 operand giving the
2386
             *   length in bytes of the string, followed by the bytes of the
2387
             *   string, so read the uint2 and then skip that amount plus
2388
             *   three additional bytes (one for the opcode, two for the
2389
             *   uint2 itself) 
2390
             */
2391
            ofs += 3 + str->readu2_at(ofs+1);
2392
            break;
2393
2394
        case OPC_SWITCH:
2395
            /* 
2396
             *   Switch: we have a UINT2 giving the number of cases,
2397
             *   followed by the cases, followed by an INT2; each case
2398
             *   consists of a DATAHOLDER plus a UINT2, for a total of 7
2399
             *   bytes.  The total is thus 5 bytes (the opcode, the case
2400
             *   count UINT2, the final INT2) plus 7 bytes times the number
2401
             *   of cases.  
2402
             */
2403
            ofs += 5 + 7*str->readu2_at(ofs+1);
2404
            break;
2405
2406
        case OPC_JMP:
2407
            /*
2408
             *   Unconditional jump: check for a jump to a RETURN of any
2409
             *   kind or a THROW.  If the destination consists of either of
2410
             *   those, replace the JMP with the target instruction.  If the
2411
             *   destination is an unconditional JMP, iteratively check its
2412
             *   destination.  
2413
             */
2414
            orig_target_ofs = target_ofs = ofs + 1 + str->read2_at(ofs + 1);
2415
2416
            /* 
2417
             *   Iterate through any chain of JMP's we find.  Abort if we
2418
             *   try following a chain longer than 20 jumps, in case we
2419
             *   should encounter any circular chains. 
2420
             */
2421
            for (done = FALSE, chain_len = 0 ; !done && chain_len < 20 ;
2422
                 ++chain_len)
2423
            {
2424
                switch(target_op = str->get_byte_at(target_ofs))
2425
                {
2426
                case OPC_RETVAL:
2427
                    /*
2428
                     *   Check for a special sequence that we can optimize
2429
                     *   even better than the usual.  If we have a GETR0
2430
                     *   followed by a JMP to a RETVAL, then we can
2431
                     *   eliminate the JMP *and* the GETR0, and just convert
2432
                     *   the GETR0 to a RET.
2433
                     */
2434
                    if (prv_op == OPC_GETR0)
2435
                    {
2436
                        /* 
2437
                         *   The GETR0 is the byte before the original JMP:
2438
                         *   simply replace it with a RET.  Note that we can
2439
                         *   leave the original jump intact, in case anyone
2440
                         *   else is pointing to it.  
2441
                         */
2442
                        str->write_at(ofs - 1, OPC_RET);
2443
2444
                        /* we're done iterating the chain of jumps-to-jumps */
2445
                        done = TRUE;
2446
                    }
2447
                    else
2448
                    {
2449
                        /* handle the same as any return instruction */
2450
                        goto any_RET;
2451
                    }
2452
                    break;
2453
2454
                case OPC_RETNIL:
2455
                case OPC_RETTRUE:
2456
                case OPC_RET:
2457
                case OPC_THROW:
2458
                any_RET:
2459
                    /* 
2460
                     *   it's a THROW or RETURN of some kind - simply copy
2461
                     *   it to the current slot; write NOP's over our jump
2462
                     *   offset operand, to make sure the code continues to
2463
                     *   be deterministically readable 
2464
                     */
2465
                    str->write_at(ofs, target_op);
2466
                    str->write_at(ofs + 1, (uchar)OPC_NOP);
2467
                    str->write_at(ofs + 2, (uchar)OPC_NOP);
2468
2469
                    /* we're done iterating the chain of jumps-to-jumps */
2470
                    done = TRUE;
2471
                    break;
2472
2473
                case OPC_JMP:
2474
                    /* 
2475
                     *   We're jumping to another jump - there's no reason
2476
                     *   to stop at the intermediate jump instruction, since
2477
                     *   we can simply jump directly to the destination
2478
                     *   address.  Calculate the new target address, and
2479
                     *   continue iterating, in case this jumps to something
2480
                     *   we can further optimize away.  
2481
                     */
2482
                    target_ofs = target_ofs + 1
2483
                                 + str->read2_at(target_ofs + 1);
2484
2485
                    /* 
2486
                     *   if it's a jump to the original location, it must be
2487
                     *   some unreachable code that generated a circular
2488
                     *   jump; ignore it in this case 
2489
                     */
2490
                    if (target_ofs == ofs)
2491
                        done = TRUE;
2492
2493
                    /* proceed */
2494
                    break;
2495
2496
                default:
2497
                    /* 
2498
                     *   For anything else, we're done with any chain of
2499
                     *   jumps to jumps.  If we indeed found a new target
2500
                     *   address, rewrite the original JMP instruction so
2501
                     *   that it jumps directly to the end of the chain
2502
                     *   rather than going through the intermediate jumps.  
2503
                     */
2504
                    if (target_ofs != orig_target_ofs)
2505
                        str->write2_at(ofs + 1, target_ofs - (ofs + 1));
2506
2507
                    /* we're done iterating */
2508
                    done = TRUE;
2509
                    break;
2510
                }
2511
            }
2512
2513
            /* whatever happened, skip past the jump */
2514
            ofs += 3;
2515
2516
            /* done */
2517
            break;
2518
2519
        case OPC_JT:
2520
        case OPC_JF:
2521
        case OPC_JE:
2522
        case OPC_JNE:
2523
        case OPC_JGT:
2524
        case OPC_JGE:
2525
        case OPC_JLT:
2526
        case OPC_JLE:
2527
        case OPC_JST:
2528
        case OPC_JSF:
2529
        case OPC_JNIL:
2530
        case OPC_JNOTNIL:
2531
        case OPC_JR0T:
2532
        case OPC_JR0F:
2533
            /*
2534
             *   We have a jump (conditional or otherwise).  Check the
2535
             *   target instruction to see if it's an unconditional jump; if
2536
             *   so, then we can jump straight to the target of the second
2537
             *   jump, since there's no reason to stop at the intermediate
2538
             *   jump instruction on our way to the final destination.  Make
2539
             *   this check iteratively, so that we eliminate any chain of
2540
             *   jumps to jumps and land at our final non-jump instruction
2541
             *   in one go. 
2542
             */
2543
            orig_target_ofs = target_ofs = ofs + 1 + str->read2_at(ofs + 1);
2544
            for (done = FALSE, chain_len = 0 ; !done && chain_len < 20 ;
2545
                 ++chain_len)
2546
            {
2547
                uchar target_op;
2548
2549
                /* get the target opcode */
2550
                target_op = str->get_byte_at(target_ofs);
2551
2552
                /* 
2553
                 *   if the target is an unconditional JMP, we can retarget
2554
                 *   the original instruction to jump directly to the target
2555
                 *   of the target JMP, bypassing the target JMP entirely and
2556
                 *   thus avoiding some unnecessary work at run-time 
2557
                 */
2558
                if (target_op == OPC_JMP)
2559
                {
2560
                    /* 
2561
                     *   retarget the original jump to go directly to the
2562
                     *   target of the target JMP 
2563
                     */
2564
                    target_ofs = target_ofs + 1
2565
                                 + str->read2_at(target_ofs + 1);
2566
                    
2567
                    /* 
2568
                     *   continue scanning for more opportunities, as the new
2569
                     *   target could also point to something we can bypass 
2570
                     */
2571
                    continue;
2572
                }
2573
                
2574
                /*
2575
                 *   Certain combinations are special.  If the original
2576
                 *   opcode was a JST or JSF, and the target is a JT or JF,
2577
                 *   we can recode the sequence so that the original opcode
2578
                 *   turns into a more efficient JT or JF and jumps directly
2579
                 *   past the JT or JF.  If we have a JST or JSF jumping to a
2580
                 *   JST or JSF, we can also recode that sequence to bypass
2581
                 *   the second jump.  In both cases, we can recode the
2582
                 *   sequence because the original jump will unequivocally
2583
                 *   determine the behavior at the target jump in such a way
2584
                 *   that we can compact the sequence into a single jump.  
2585
                 */
2586
                switch(op)
2587
                {
2588
                case OPC_JSF:
2589
                    /* 
2590
                     *   the original is a JSF: we can recode a jump to a
2591
                     *   JSF, JST, JF, or JT 
2592
                     */
2593
                    switch(target_op)
2594
                    {
2595
                    case OPC_JSF:
2596
                        /* 
2597
                         *   We're jumping to another JSF.  Since the
2598
                         *   original jump will only reach the target jump if
2599
                         *   the value on the top of the stack is false, and
2600
                         *   will then leave this same value on the stack to
2601
                         *   be tested again with the target JSF, we know the
2602
                         *   target JSF will perform its jump and leave the
2603
                         *   stack unchanged again.  So, we can simply
2604
                         *   retarget the original jump to the target of the
2605
                         *   target JSF.  
2606
                         */
2607
                        target_ofs = target_ofs + 1
2608
                                     + str->read2_at(target_ofs + 1);
2609
2610
                        /* keep scanning for additional opportunities */
2611
                        break;
2612
2613
                    case OPC_JST:
2614
                    case OPC_JT:
2615
                        /*
2616
                         *   We're jumping to a JST or a JT.  Since the JSF
2617
                         *   will only reach the JST/JT on a false value, we
2618
                         *   know the JST/JT will NOT jump - we know for a
2619
                         *   fact it will pop the non-true stack element and
2620
                         *   proceed without jumping.  Therefore, we can
2621
                         *   avoid saving the value from the original JSF,
2622
                         *   which means we can recode the original as the
2623
                         *   simpler JF (which doesn't bother saving the
2624
                         *   false value), and jump on false directly to the
2625
                         *   instruction after the target JST/JT.  
2626
                         */
2627
                        str->write_at(ofs, (uchar)OPC_JF);
2628
                        op = OPC_JF;
2629
2630
                        /* jump to the instruction after the target JST/JT */
2631
                        target_ofs += 3;
2632
2633
                        /* keep looking for more jumps */
2634
                        break;
2635
2636
                    case OPC_JF:
2637
                        /* 
2638
                         *   We're jumping to a JF: we know the JF will jump,
2639
                         *   because we had to have - and then save - a false
2640
                         *   value for the JSF to reach the JF in the first
2641
                         *   place.  Since we know for a fact the target JF
2642
                         *   will remove the false value and jump to its
2643
                         *   target, we can bypass the target JF by recoding
2644
                         *   the original instruction as a simpler JF and
2645
                         *   jumping directly to the target of the target JF.
2646
                         */
2647
                        str->write_at(ofs, (uchar)OPC_JF);
2648
                        op = OPC_JF;
2649
2650
                        /* jump to the tartet of the target JF */
2651
                        target_ofs = target_ofs + 1
2652
                                     + str->read2_at(target_ofs + 1);
2653
2654
                        /* keep scanning for more jumps */
2655
                        break;
2656
2657
                    default:
2658
                        /* can't make any assumptions about other targets */
2659
                        done = TRUE;
2660
                        break;
2661
                    }
2662
                    break;
2663
2664
                case OPC_JST:
2665
                    /* 
2666
                     *   the original is a JST: recode it if the target is a
2667
                     *   JSF, JST, JF, or JT 
2668
                     */
2669
                    switch(target_op)
2670
                    {
2671
                    case OPC_JST:
2672
                        /* JST jumping to JST: jump to the target's target */
2673
                        target_ofs = target_ofs + 1
2674
                                     + str->read2_at(target_ofs + 1);
2675
2676
                        /* keep looking */
2677
                        break;
2678
2679
                    case OPC_JSF:
2680
                    case OPC_JF:
2681
                        /* 
2682
                         *   JST jumping to JSF/JF: the JSF/JF will
2683
                         *   definitely pop the stack and not jump (since the
2684
                         *   original JST will have left a true value on the
2685
                         *   stack), so we can recode the JST as a more
2686
                         *   efficient JT and jump to the instruction after
2687
                         *   the JSF/JF target 
2688
                         */
2689
                        str->write_at(ofs, (uchar)OPC_JT);
2690
                        op = OPC_JT;
2691
2692
                        /* jump to the instruction after the target */
2693
                        target_ofs += 3;
2694
2695
                        /* keep looking */
2696
                        break;
2697
2698
                    case OPC_JT:
2699
                        /* 
2700
                         *   JST jumping to JT: the JT will definitely pop
2701
                         *   and jump, so we can recode the original as a
2702
                         *   simpler JT and jump to the target's target 
2703
                         */
2704
                        str->write_at(ofs, (uchar)OPC_JT);
2705
                        op = OPC_JT;
2706
2707
                        /* jump to the target of the target */
2708
                        target_ofs = target_ofs + 1
2709
                                     + str->read2_at(target_ofs + 1);
2710
2711
                        /* keep scanning */
2712
                        break;
2713
2714
                    default:
2715
                        /* can't make any assumptions about other targets */
2716
                        done = TRUE;
2717
                        break;
2718
                    }
2719
                    break;
2720
2721
                default:
2722
                    /* 
2723
                     *   we can't make assumptions about anything else, so
2724
                     *   we've come to the end of the road - stop scanning 
2725
                     */
2726
                    done = TRUE;
2727
                    break;
2728
                }
2729
            }
2730
2731
            /* 
2732
             *   if we found a chain of jumps, replace our original jump
2733
             *   target with the final jump target, bypassing the
2734
             *   intermediate jumps 
2735
             */
2736
            if (target_ofs != orig_target_ofs)
2737
                str->write2_at(ofs + 1, target_ofs - (ofs + 1));
2738
2739
            /* skip past the jump */
2740
            ofs += 3;
2741
2742
            /* done */
2743
            break;
2744
2745
        default:
2746
            /* 
2747
             *   everything else is a fixed-size instruction, so simply
2748
             *   consult our table of instruction lengths to determine the
2749
             *   offset of the next instruction 
2750
             */
2751
            ofs += op_siz[op];
2752
            break;
2753
        }
2754
2755
        /* remember the preceding opcode */
2756
        prv_op = op;
2757
    }
2758
}
2759
2760
/* ------------------------------------------------------------------------ */
2761
/*
2762
 *   Generic T3 node 
2763
 */
2764
2765
/* 
2766
 *   generate a jump-ahead instruction, returning a new label which serves
2767
 *   as the jump destination 
2768
 */
2769
CTcCodeLabel *CTcPrsNode::gen_jump_ahead(uchar opc)
2770
{
2771
    CTcCodeLabel *lbl;
2772
2773
    /* 
2774
     *   check to see if we should suppress the jump for peephole
2775
     *   optimization 
2776
     */
2777
    if (G_cg->can_skip_op())
2778
        return 0;
2779
2780
    /* emit the opcode */
2781
    G_cg->write_op(opc);
2782
2783
    /* allocate a new label */
2784
    lbl = G_cs->new_label_fwd();
2785
2786
    /* 
2787
     *   write the forward offset to the label (this will generate a fixup
2788
     *   record attached to the label, so that we'll come back and fix it
2789
     *   up when the real offset is known) 
2790
     */
2791
    G_cs->write_ofs2(lbl, 0);
2792
2793
    /* return the forward label */
2794
    return lbl;
2795
}
2796
2797
/*
2798
 *   Allocate a new label at the current write position 
2799
 */
2800
CTcCodeLabel *CTcPrsNode::new_label_here()
2801
{
2802
    /* 
2803
     *   suppress any peephole optimizations at this point -- someone
2804
     *   could jump directly to this instruction, so we can't combine an
2805
     *   instruction destined for this point with anything previous 
2806
     */
2807
    G_cg->clear_peephole();
2808
2809
    /* create and return a label at the current position */
2810
    return G_cs->new_label_here();
2811
}
2812
2813
/* 
2814
 *   define the position of a code label 
2815
 */
2816
void CTcPrsNode::def_label_pos(CTcCodeLabel *lbl)
2817
{
2818
    /* if the label is null, ignore it */
2819
    if (lbl == 0)
2820
        return;
2821
2822
    /* 
2823
     *   try eliminating a jump-to-next-instruction sequence: if the last
2824
     *   opcode was a JMP to this label, remove the last instruction
2825
     *   entirely 
2826
     */
2827
    if (G_cg->get_last_op() == OPC_JMP
2828
        && G_cs->has_fixup_at_ofs(lbl, G_cs->get_ofs() - 2))
2829
    {
2830
        /* remove the fixup pointing to the preceding JMP */
2831
        G_cs->remove_fixup_at_ofs(lbl, G_cs->get_ofs() - 2);
2832
        
2833
        /* the JMP is unnecessary - remove it */
2834
        G_cg->remove_last_jmp();
2835
    }
2836
    
2837
    /* define the label position and apply the fixup */
2838
    G_cs->def_label_pos(lbl);
2839
2840
    /* 
2841
     *   whenever we define a label, we must suppress any peephole
2842
     *   optimizations at this point - someone could jump directly to this
2843
     *   instruction, so we can't combine an instruction destined for this
2844
     *   point with anything previous 
2845
     */
2846
    G_cg->clear_peephole();
2847
}
2848
2849
/*
2850
 *   Generate code for an if-else conditional test.  The default
2851
 *   implementation is to evaluate the expression, and jump to the false
2852
 *   branch if the expression is false (or jump to the true part if the
2853
 *   expression is true and there's no false part).
2854
 */
2855
void CTcPrsNode::gen_code_cond(CTcCodeLabel *then_label,
2856
                               CTcCodeLabel *else_label)
2857
{
2858
    /* generate our expression code */
2859
    gen_code(FALSE, TRUE);
2860
2861
    /* 
2862
     *   if we have a 'then' part, jump to the 'then' part if the condition
2863
     *   is true; otherwise, jump to the 'else' part if the condition is
2864
     *   false 
2865
     */
2866
    if (then_label != 0)
2867
    {
2868
        /* we have a 'then' part, so jump if true to the 'then' part */
2869
        G_cg->write_op(OPC_JT);
2870
        G_cs->write_ofs2(then_label, 0);
2871
    }
2872
    else
2873
    {
2874
        /* we have an 'else' part, so jump if false to the 'else' */
2875
        G_cg->write_op(OPC_JF);
2876
        G_cs->write_ofs2(else_label, 0);
2877
    }
2878
2879
    /* the JF or JT pops an element off the stack */
2880
    G_cg->note_pop();
2881
}
2882
2883
/*
2884
 *   generate code for assignment to this node 
2885
 */
2886
int CTcPrsNode::gen_code_asi(int, tc_asitype_t, CTcPrsNode *,
2887
                             int ignore_error)
2888
{
2889
    /* 
2890
     *   if ignoring errors, the caller is trying to assign if possible
2891
     *   but doesn't require it to be possible; simply return false to
2892
     *   indicate that nothing happened if this is the case 
2893
     */
2894
    if (ignore_error)
2895
        return FALSE;
2896
    
2897
    /* we should never get here - throw an internal error */
2898
    G_tok->throw_internal_error(TCERR_GEN_BAD_LVALUE);
2899
    AFTER_ERR_THROW(return FALSE;)
2900
}
2901
2902
/*
2903
 *   generate code for taking the address of this node 
2904
 */
2905
void CTcPrsNode::gen_code_addr()
2906
{
2907
    /* we should never get here - throw an internal error */
2908
    G_tok->throw_internal_error(TCERR_GEN_BAD_ADDR);
2909
}
2910
2911
/*
2912
 *   Generate code to call the expression as a function or method.  
2913
 */
2914
void CTcPrsNode::gen_code_call(int discard, int argc, int varargs)
2915
{
2916
    /* function/method calls are never valid in speculative mode */
2917
    if (G_cg->is_speculative())
2918
        err_throw(VMERR_BAD_SPEC_EVAL);
2919
    
2920
    /*
2921
     *   For default nodes, assume that the result of evaluating the
2922
     *   expression contained in the node is a method or function pointer.
2923
     *   First, generate code to evaluate the expression, which should
2924
     *   yield an appropriate pointer value. 
2925
     */
2926
    gen_code(FALSE, FALSE);
2927
2928
    /* 
2929
     *   if we have a varargs list, modify the call instruction that
2930
     *   follows to make it a varargs call 
2931
     */
2932
    if (varargs)
2933
    {
2934
        /* swap the top of the stack to get the arg counter back on top */
2935
        G_cg->write_op(OPC_SWAP);
2936
2937
        /* write the varargs modifier */
2938
        G_cg->write_op(OPC_VARARGC);
2939
    }
2940
2941
    /* generate an indirect function call through the pointer */
2942
    G_cg->write_op(OPC_PTRCALL);
2943
    G_cs->write((char)argc);
2944
2945
    /* PTRCALL pops the arguments plus the function pointer */
2946
    G_cg->note_pop(argc + 1);
2947
2948
    /* 
2949
     *   if the caller isn't going to discard the return value, push the
2950
     *   result, which is sitting in R0 
2951
     */
2952
    if (!discard)
2953
    {
2954
        G_cg->write_op(OPC_GETR0);
2955
        G_cg->note_push();
2956
    }
2957
}
2958
2959
/*
2960
 *   Generate code for operator 'new' applied to this node
2961
 */
2962
void CTcPrsNode::gen_code_new(int, int, int, int, int)
2963
{
2964
    /* operator 'new' cannot be applied to a default node */
2965
    G_tok->log_error(TCERR_INVAL_NEW_EXPR);
2966
}
2967
2968
/*
2969
 *   Generate code for a member evaluation 
2970
 */
2971
void CTcPrsNode::gen_code_member(int discard,
2972
                                 CTcPrsNode *prop_expr, int prop_is_expr,
2973
                                 int argc, int varargs)
2974
{
2975
    /* evaluate my own expression to yield the object value */
2976
    gen_code(FALSE, FALSE);
2977
2978
    /* if we have an argument counter, put it back on top */
2979
    if (varargs)
2980
        G_cg->write_op(OPC_SWAP);
2981
2982
    /* use the generic code to generate the rest */
2983
    s_gen_member_rhs(discard, prop_expr, prop_is_expr, argc, varargs);
2984
}
2985
2986
/*
2987
 *   Generic code to generate the rest of a member expression after the
2988
 *   left side of the '.' has been generated.  This can be used for cases
2989
 *   where the left of the '.' is an arbitrary expression, and hence must
2990
 *   be evaluated at run-time.
2991
 */
2992
void CTcPrsNode::s_gen_member_rhs(int discard,
2993
                                  CTcPrsNode *prop_expr, int prop_is_expr,
2994
                                  int argc, int varargs)
2995
{
2996
    vm_prop_id_t prop;
2997
2998
    /* we can't call methods with argument in speculative mode */
2999
    if (argc != 0 && G_cg->is_speculative())
3000
        err_throw(VMERR_BAD_SPEC_EVAL);
3001
3002
    /* get or generate the property ID value */
3003
    prop = prop_expr->gen_code_propid(FALSE, prop_is_expr);
3004
3005
    /* 
3006
     *   if we got a property ID, generate a simple GETPROP or CALLPROP
3007
     *   instruction; otherwise, generate a PTRCALLPROP instruction
3008
     */
3009
    if (prop != VM_INVALID_PROP)
3010
    {
3011
        /* 
3012
         *   we have a constant property ID - generate a GETPROP or
3013
         *   CALLPROP with the property ID as constant data 
3014
         */
3015
        if (argc == 0)
3016
        {
3017
            /* no arguments - generate a GETPROP */
3018
            G_cg->write_op(G_cg->is_speculative()
3019
                           ? OPC_GETPROPDATA : OPC_GETPROP);
3020
            G_cs->write_prop_id(prop);
3021
3022
            /* this pops an object element */
3023
            G_cg->note_pop();
3024
        }
3025
        else
3026
        {
3027
            /* write the CALLPROP instruction */
3028
            G_cg->write_callprop(argc, varargs, prop);
3029
        }
3030
    }
3031
    else
3032
    {
3033
        if (G_cg->is_speculative())
3034
        {
3035
            /* 
3036
             *   speculative - use PTRGETPROPDATA to ensure we don't cause
3037
             *   any side effects 
3038
             */
3039
            G_cg->write_op(OPC_PTRGETPROPDATA);
3040
        }
3041
        else
3042
        {
3043
            /* 
3044
             *   if we have a varargs list, modify the call instruction
3045
             *   that follows to make it a varargs call 
3046
             */
3047
            if (varargs)
3048
            {
3049
                /* swap to get the arg counter back on top */
3050
                G_cg->write_op(OPC_SWAP);
3051
                
3052
                /* write the varargs modifier */
3053
                G_cg->write_op(OPC_VARARGC);
3054
            }
3055
3056
            /* a property pointer is on the stack - write a PTRCALLPROP */
3057
            G_cg->write_op(OPC_PTRCALLPROP);
3058
            G_cs->write((int)argc);
3059
        }
3060
3061
        /* 
3062
         *   ptrcallprop/ptrgetpropdata removes arguments, the object, and
3063
         *   the property 
3064
         */
3065
        G_cg->note_pop(argc + 2);
3066
    }
3067
3068
    /* if we're not discarding the result, push it from R0 */
3069
    if (!discard)
3070
    {
3071
        G_cg->write_op(OPC_GETR0);
3072
        G_cg->note_push();
3073
    }
3074
}
3075
3076
/*
3077
 *   Generate code to get the property ID of the expression.
3078
 */
3079
vm_prop_id_t CTcPrsNode::gen_code_propid(int check_only, int is_expr)
3080
{
3081
    /* 
3082
     *   simply evaluate the expression normally, anticipating that this
3083
     *   will yield a property ID value at run-time 
3084
     */
3085
    if (!check_only)
3086
        gen_code(FALSE, FALSE);
3087
3088
    /* tell the caller that there's no constant ID available */
3089
    return VM_INVALID_PROP;
3090
}
3091
3092
/* ------------------------------------------------------------------------ */
3093
/*
3094
 *   "self" 
3095
 */
3096
3097
/*
3098
 *   generate code 
3099
 */
3100
void CTPNSelf::gen_code(int discard, int)
3101
{
3102
    /* it's an error if we're not in a method context */
3103
    if (!G_cs->is_self_available())
3104
        G_tok->log_error(TCERR_SELF_NOT_AVAIL);
3105
    
3106
    /* if we're not discarding the result, push the "self" object */
3107
    if (!discard)
3108
    {
3109
        G_cg->write_op(OPC_PUSHSELF);
3110
        G_cg->note_push();
3111
    }
3112
}
3113
3114
/*
3115
 *   evaluate a property 
3116
 */
3117
void CTPNSelf::gen_code_member(int discard,
3118
                               CTcPrsNode *prop_expr, int prop_is_expr,
3119
                               int argc, int varargs)
3120
{
3121
    vm_prop_id_t prop;
3122
    
3123
    /* make sure "self" is available */
3124
    if (!G_cs->is_self_available())
3125
        G_tok->log_error(TCERR_SELF_NOT_AVAIL);
3126
3127
    /* don't allow arguments in speculative eval mode */
3128
    if (argc != 0 && G_cg->is_speculative())
3129
        err_throw(VMERR_BAD_SPEC_EVAL);
3130
3131
    /* generate the property value */
3132
    prop = prop_expr->gen_code_propid(FALSE, prop_is_expr);
3133
3134
    /* 
3135
     *   if we got a property ID, generate a simple GETPROPSELF or
3136
     *   CALLPROPSELF; otherwise, generate a PTRCALLPROPSELF 
3137
     */
3138
    if (prop != VM_INVALID_PROP)
3139
    {
3140
        /* 
3141
         *   we have a constant property ID - generate a GETPROPDATA,
3142
         *   GETPROPSELF, or CALLPROPSELF with the property ID as
3143
         *   immediate data 
3144
         */
3145
        if (G_cg->is_speculative())
3146
        {
3147
            /* speculative - use GETPROPDATA */
3148
            G_cg->write_op(OPC_PUSHSELF);
3149
            G_cg->write_op(OPC_GETPROPDATA);
3150
            G_cs->write_prop_id(prop);
3151
3152
            /* we pushed one element (self) and popped it back off */
3153
            G_cg->note_push();
3154
            G_cg->note_pop();
3155
        }
3156
        else if (argc == 0)
3157
        {
3158
            /* no arguments - generate a GETPROPSELF */
3159
            G_cg->write_op(OPC_GETPROPSELF);
3160
            G_cs->write_prop_id(prop);
3161
        }
3162
        else
3163
        {
3164
            /* add a varargs modifier if appropriate */
3165
            if (varargs)
3166
                G_cg->write_op(OPC_VARARGC);
3167
            
3168
            /* we have arguments - generate a CALLPROPSELF */
3169
            G_cg->write_op(OPC_CALLPROPSELF);
3170
            G_cs->write((char)argc);
3171
            G_cs->write_prop_id(prop);
3172
3173
            /* this removes arguments */
3174
            G_cg->note_pop(argc);
3175
        }
3176
    }
3177
    else
3178
    {
3179
        /* 
3180
         *   a property pointer is on the stack - use PTRGETPROPDATA or
3181
         *   PTRCALLPROPSELF, depending on the speculative mode 
3182
         */
3183
        if (G_cg->is_speculative())
3184
        {
3185
            /* speculative - use PTRGETPROPDATA after pushing self */
3186
            G_cg->write_op(OPC_PUSHSELF);
3187
            G_cg->write_op(OPC_PTRGETPROPDATA);
3188
3189
            /* we pushed self then removed self and the property ID */
3190
            G_cg->note_push();
3191
            G_cg->note_pop(2);
3192
        }
3193
        else
3194
        {
3195
            /* 
3196
             *   if we have a varargs list, modify the call instruction
3197
             *   that follows to make it a varargs call 
3198
             */
3199
            if (varargs)
3200
            {
3201
                /* swap to get the arg counter back on top */
3202
                G_cg->write_op(OPC_SWAP);
3203
                
3204
                /* write the varargs modifier */
3205
                G_cg->write_op(OPC_VARARGC);
3206
            }
3207
3208
            /* a prop pointer is on the stack - write a PTRCALLPROPSELF */
3209
            G_cg->write_op(OPC_PTRCALLPROPSELF);
3210
            G_cs->write((int)argc);
3211
            
3212
            /* this removes arguments and the property pointer */
3213
            G_cg->note_pop(argc + 1);
3214
        }
3215
    }
3216
3217
    /* if the result is needed, push it */
3218
    if (!discard)
3219
    {
3220
        G_cg->write_op(OPC_GETR0);
3221
        G_cg->note_push();
3222
    }
3223
}
3224
3225
3226
/*
3227
 *   generate code for an object before a '.' 
3228
 */
3229
vm_obj_id_t CTPNSelf::gen_code_obj_predot(int *is_self)
3230
{
3231
    /* make sure "self" is available */
3232
    if (!G_cs->is_self_available())
3233
        G_tok->log_error(TCERR_SELF_NOT_AVAIL);
3234
3235
    /* tell the caller that this is "self" */
3236
    *is_self = TRUE;
3237
    return VM_INVALID_OBJ;
3238
}
3239
3240
3241
/* ------------------------------------------------------------------------ */
3242
/*
3243
 *   "replaced" 
3244
 */
3245
3246
/*
3247
 *   evaluate 'replaced' on its own - this simply yields a function pointer
3248
 *   to the modified base code 
3249
 */
3250
void CTPNReplaced::gen_code(int discard, int for_condition)
3251
{
3252
    /* get the modified base function symbol */
3253
    CTcSymFunc *mod_base = G_cs->get_code_body()->get_replaced_func();
3254
3255
    /* make sure we're in a 'modify func()' context */
3256
    if (mod_base == 0)
3257
        G_tok->log_error(TCERR_REPLACED_NOT_AVAIL);
3258
3259
    /* this expression yields a pointer to the modified base function */
3260
    G_cg->write_op(OPC_PUSHFNPTR);
3261
3262
    /* add a fixup for the current code location */
3263
    if (mod_base != 0)
3264
        mod_base->add_abs_fixup(G_cs);
3265
3266
    /* write a placeholder offset - arbitrarily use zero */
3267
    G_cs->write4(0);
3268
3269
    /* note the push */
3270
    G_cg->note_push();
3271
}
3272
3273
/*
3274
 *   'replaced()' call - this invokes the modified base code 
3275
 */
3276
void CTPNReplaced::gen_code_call(int discard, int argc, int varargs)
3277
{
3278
    /* get the modified base function symbol */
3279
    CTcSymFunc *mod_base = G_cs->get_code_body()->get_replaced_func();
3280
3281
    /* make sure we're in a 'modify func()' context */
3282
    if (mod_base == 0)
3283
        G_tok->log_error(TCERR_REPLACED_NOT_AVAIL);
3284
3285
    /* write the varargs modifier if appropriate */
3286
    if (varargs)
3287
        G_cg->write_op(OPC_VARARGC);
3288
3289
    /* generate the call instruction and argument count */
3290
    G_cg->write_op(OPC_CALL);
3291
    G_cs->write((char)argc);
3292
3293
    /* generate a fixup for the call to the modified base code */
3294
    if (mod_base != 0)
3295
        mod_base->add_abs_fixup(G_cs);
3296
3297
    /* add a placeholder for the function address */
3298
    G_cs->write4(0);
3299
3300
    /* call removes arguments */
3301
    G_cg->note_pop(argc);
3302
3303
    /* make sure the argument count is correct */
3304
    if (mod_base != 0
3305
        && (mod_base->is_varargs() ? argc < mod_base->get_argc()
3306
                                   : argc != mod_base->get_argc()))
3307
        G_tok->log_error(TCERR_WRONG_ARGC_FOR_FUNC,
3308
                         (int)mod_base->get_sym_len(), mod_base->get_sym(),
3309
                         mod_base->get_argc(), argc);
3310
3311
    /* if we're not discarding, push the return value from R0 */
3312
    if (!discard)
3313
    {
3314
        G_cg->write_op(OPC_GETR0);
3315
        G_cg->note_push();
3316
    }
3317
}
3318
3319
/* ------------------------------------------------------------------------ */
3320
/*
3321
 *   "targetprop" 
3322
 */
3323
3324
/*
3325
 *   generate code 
3326
 */
3327
void CTPNTargetprop::gen_code(int discard, int)
3328
{
3329
    /* it's an error if we're not in a method context */
3330
    if (!G_cs->is_self_available())
3331
        G_tok->log_error(TCERR_TARGETPROP_NOT_AVAIL);
3332
3333
    /* if we're not discarding the result, push the target property ID */
3334
    if (!discard)
3335
    {
3336
        G_cg->write_op(OPC_PUSHCTXELE);
3337
        G_cs->write(PUSHCTXELE_TARGPROP);
3338
        G_cg->note_push();
3339
    }
3340
}
3341
3342
/* ------------------------------------------------------------------------ */
3343
/*
3344
 *   "targetobj" 
3345
 */
3346
3347
/*
3348
 *   generate code 
3349
 */
3350
void CTPNTargetobj::gen_code(int discard, int)
3351
{
3352
    /* it's an error if we're not in a method context */
3353
    if (!G_cs->is_self_available())
3354
        G_tok->log_error(TCERR_TARGETOBJ_NOT_AVAIL);
3355
3356
    /* if we're not discarding the result, push the target object ID */
3357
    if (!discard)
3358
    {
3359
        G_cg->write_op(OPC_PUSHCTXELE);
3360
        G_cs->write(PUSHCTXELE_TARGOBJ);
3361
        G_cg->note_push();
3362
    }
3363
}
3364
3365
/* ------------------------------------------------------------------------ */
3366
/*
3367
 *   "definingobj" 
3368
 */
3369
3370
/*
3371
 *   generate code 
3372
 */
3373
void CTPNDefiningobj::gen_code(int discard, int)
3374
{
3375
    /* it's an error if we're not in a method context */
3376
    if (!G_cs->is_self_available())
3377
        G_tok->log_error(TCERR_DEFININGOBJ_NOT_AVAIL);
3378
3379
    /* if we're not discarding the result, push the defining object ID */
3380
    if (!discard)
3381
    {
3382
        G_cg->write_op(OPC_PUSHCTXELE);
3383
        G_cs->write(PUSHCTXELE_DEFOBJ);
3384
        G_cg->note_push();
3385
    }
3386
}
3387
3388
3389
/* ------------------------------------------------------------------------ */
3390
/*
3391
 *   "inherited" 
3392
 */
3393
void CTPNInh::gen_code(int, int)
3394
{
3395
    /* 
3396
     *   we should never be asked to generate an "inherited" node
3397
     *   directly; these nodes should always be generated as part of
3398
     *   member evaluation 
3399
     */
3400
    G_tok->throw_internal_error(TCERR_GEN_CODE_INH);
3401
}
3402
3403
/*
3404
 *   evaluate a property 
3405
 */
3406
void CTPNInh::gen_code_member(int discard,
3407
                              CTcPrsNode *prop_expr, int prop_is_expr,
3408
                              int argc, int varargs)
3409
{
3410
    vm_prop_id_t prop;
3411
3412
    /* don't allow 'inherited' in speculative evaluation mode */
3413
    if (G_cg->is_speculative())
3414
        err_throw(VMERR_BAD_SPEC_EVAL);
3415
3416
    /*
3417
     *   If we're in a multi-method context, inherited() has a different
3418
     *   meaning from the ordinary method context meaning.
3419
     */
3420
    for (CTPNCodeBody *cb = G_cs->get_code_body() ; cb != 0 ;
3421
         cb = cb->get_enclosing())
3422
    {
3423
        /* check for a multi-method context */
3424
        CTcSymFunc *f = cb->get_func_sym();
3425
        if (f != 0 && f->is_multimethod())
3426
        {
3427
            /* generate the multi-method inherited() code */
3428
            gen_code_mminh(f, discard, prop_expr, prop_is_expr, argc, varargs);
3429
3430
            /* we're done */
3431
            return;
3432
        }
3433
    }
3434
3435
    /* 
3436
     *   make sure "self" is available - we obviously can't inherit
3437
     *   anything if we're not in an object's method 
3438
     */
3439
    if (!G_cs->is_self_available())
3440
        G_tok->log_error(TCERR_SELF_NOT_AVAIL);
3441
3442
    /* a type list ('inherited<>') is invalid for regular method inheritance */
3443
    if (typelist_ != 0)
3444
        G_tok->log_error(TCERR_MMINH_BAD_CONTEXT);
3445
3446
    /* generate the property value */
3447
    prop = prop_expr->gen_code_propid(FALSE, prop_is_expr);
3448
3449
    /* 
3450
     *   if we got a property ID, generate a simple INHERIT;
3451
     *   otherwise, generate a PTRINHERIT 
3452
     */
3453
    if (prop != VM_INVALID_PROP)
3454
    {
3455
        /* generate a varargs modifier if necessary */
3456
        if (varargs)
3457
            G_cg->write_op(OPC_VARARGC);
3458
        
3459
        /* we have a constant property ID - generate a regular INHERIT */
3460
        G_cg->write_op(OPC_INHERIT);
3461
        G_cs->write((char)argc);
3462
        G_cs->write_prop_id(prop);
3463
3464
        /* this removes arguments */
3465
        G_cg->note_pop(argc);
3466
    }
3467
    else
3468
    {
3469
        /* 
3470
         *   if we have a varargs list, modify the call instruction that
3471
         *   follows to make it a varargs call 
3472
         */
3473
        if (varargs)
3474
        {
3475
            /* swap the top of the stack to get the arg counter back on top */
3476
            G_cg->write_op(OPC_SWAP);
3477
            
3478
            /* write the varargs modifier */
3479
            G_cg->write_op(OPC_VARARGC);
3480
        }
3481
3482
        /* a property pointer is on the stack - write a PTRINHERIT */
3483
        G_cg->write_op(OPC_PTRINHERIT);
3484
        G_cs->write((int)argc);
3485
3486
        /* this removes arguments and the property pointer */
3487
        G_cg->note_pop(argc + 1);
3488
    }
3489
3490
    /* if the result is needed, push it */
3491
    if (!discard)
3492
    {
3493
        G_cg->write_op(OPC_GETR0);
3494
        G_cg->note_push();
3495
    }
3496
}
3497
3498
void CTPNInh::gen_code_mminh(CTcSymFunc *func, int discard,
3499
                             CTcPrsNode *prop_expr, int prop_is_expr,
3500
                             int argc, int varargs)
3501
{
3502
    /*
3503
     *   There are two forms of inherited() for multi-methods.
3504
     *   
3505
     *   inherited<types>(args) - this form takes an explicit type list, to
3506
     *   invoke a specific overridden version.
3507
     *   
3508
     *   inherited(args) - this form invokes the next inherited version,
3509
     *   determined dynamically at run-time.  
3510
     */
3511
    if (typelist_ == 0)
3512
    {
3513
        /*
3514
         *   It's the inherited(args) format.
3515
         *   
3516
         *   Call _multiMethodCallInherited(fromFunc, args).  We've already
3517
         *   pushed the args list, so just add the source function argument
3518
         *   (which is simply our defining function).  
3519
         */
3520
        func->gen_code(FALSE);
3521
3522
        /* look up _multiMethodCallInherited */
3523
        CTcSymFunc *mmci = (CTcSymFunc *)G_prs->get_global_symtab()->find(
3524
            "_multiMethodCallInherited", 25);
3525
3526
        if (mmci == 0 || mmci->get_type() != TC_SYM_FUNC)
3527
        {
3528
            /* undefined or incorrectly defined - log an error */
3529
            G_tok->log_error(TCERR_MMINH_MISSING_SUPPORT_FUNC,
3530
                             25, "_multiMethodCallInherited");
3531
        }
3532
        else
3533
        {
3534
            /* generate the call */
3535
            mmci->gen_code_call(discard, argc + 1, varargs);
3536
        }
3537
    }
3538
    else
3539
    {
3540
        /* 
3541
         *   It's the inherited<types>(args) format.
3542
         */
3543
3544
        /* 
3545
         *   Get the base name for the function.  'func' is the decorated
3546
         *   name for the containing function, which is of the form
3547
         *   'Base*type1;type2...'.  The base name is the part up to the
3548
         *   asterisk.  
3549
         */
3550
        const char *nm = func->getstr(), *p;
3551
        size_t rem = func->getlen();
3552
        for (p = nm ; rem != 0 && *p != '*' ; ++p, --rem) ;
3553
3554
        /* make a token for the base name */
3555
        CTcToken btok;
3556
        btok.set_text(nm, p - nm);
3557
        
3558
        /* build the decorated name for the target function */
3559
        CTcToken dtok;
3560
        typelist_->decorate_name(&dtok, &btok);
3561
3562
        /* look up the decorated name */
3563
        CTcSymFunc *ifunc = (CTcSymFunc *)G_prs->get_global_symtab()->find(
3564
            dtok.get_text(), dtok.get_text_len());
3565
3566
        /* if we found it, call it */
3567
        if (ifunc != 0 && ifunc->get_type() == TC_SYM_FUNC)
3568
        {
3569
            /* generate the call */
3570
            ifunc->gen_code_call(discard, argc, varargs);
3571
        }
3572
        else
3573
        {
3574
            /* function not found */
3575
            G_tok->log_error(TCERR_MMINH_UNDEF_FUNC, (int)(p - nm), nm);
3576
        }
3577
    }
3578
}
3579
3580
/* ------------------------------------------------------------------------ */
3581
/*
3582
 *   "inherited class"
3583
 */
3584
void CTPNInhClass::gen_code(int discard, int for_condition)
3585
{
3586
    /* 
3587
     *   we should never be asked to generate an "inherited" node
3588
     *   directly; these nodes should always be generated as part of
3589
     *   member evaluation 
3590
     */
3591
    G_tok->throw_internal_error(TCERR_GEN_CODE_INH);
3592
}
3593
3594
/*
3595
 *   evaluate a property 
3596
 */
3597
void CTPNInhClass::gen_code_member(int discard,
3598
                                   CTcPrsNode *prop_expr, int prop_is_expr,
3599
                                   int argc, int varargs)
3600
{
3601
    vm_prop_id_t prop;
3602
    CTcSymbol *objsym;
3603
    vm_obj_id_t obj;
3604
3605
    /* 
3606
     *   make sure "self" is available - we obviously can't inherit
3607
     *   anything if we're not in an object's method 
3608
     */
3609
    if (!G_cs->is_self_available())
3610
        G_tok->log_error(TCERR_SELF_NOT_AVAIL);
3611
3612
    /* don't allow 'inherited' in speculative evaluation mode */
3613
    if (G_cg->is_speculative())
3614
        err_throw(VMERR_BAD_SPEC_EVAL);
3615
3616
    /* get the superclass name symbol */
3617
    objsym = G_cs->get_symtab()->find_or_def_undef(sym_, len_, FALSE);
3618
    
3619
    /* if it's not an object, we can't inherit from it */
3620
    obj = objsym->get_val_obj();
3621
    if (obj == VM_INVALID_OBJ)
3622
    {
3623
        G_tok->log_error(TCERR_INH_NOT_OBJ, (int)len_, sym_);
3624
        return;
3625
    }
3626
3627
    /* generate the property value */
3628
    prop = prop_expr->gen_code_propid(FALSE, prop_is_expr);
3629
3630
    /* 
3631
     *   if we got a property ID, generate a simple EXPINHERIT; otherwise,
3632
     *   generate a PTREXPINHERIT 
3633
     */
3634
    if (prop != VM_INVALID_PROP)
3635
    {
3636
        /* add a varargs modifier if needed */
3637
        if (varargs)
3638
            G_cg->write_op(OPC_VARARGC);
3639
        
3640
        /* we have a constant property ID - generate a regular EXPINHERIT */
3641
        G_cg->write_op(OPC_EXPINHERIT);
3642
        G_cs->write((char)argc);
3643
        G_cs->write_prop_id(prop);
3644
        G_cs->write_obj_id(obj);
3645
3646
        /* this removes argumnts */
3647
        G_cg->note_pop(argc);
3648
    }
3649
    else
3650
    {
3651
        /* 
3652
         *   if we have a varargs list, modify the call instruction that
3653
         *   follows to make it a varargs call 
3654
         */
3655
        if (varargs)
3656
        {
3657
            /* swap the top of the stack to get the arg counter back on top */
3658
            G_cg->write_op(OPC_SWAP);
3659
            
3660
            /* write the varargs modifier */
3661
            G_cg->write_op(OPC_VARARGC);
3662
        }
3663
        
3664
        /* a property pointer is on the stack - write a PTREXPINHERIT */
3665
        G_cg->write_op(OPC_PTREXPINHERIT);
3666
        G_cs->write((int)argc);
3667
        G_cs->write_obj_id(obj);
3668
3669
        /* this removes arguments and the property pointer */
3670
        G_cg->note_pop(argc + 1);
3671
    }
3672
3673
    /* if the result is needed, push it */
3674
    if (!discard)
3675
    {
3676
        G_cg->write_op(OPC_GETR0);
3677
        G_cg->note_push();
3678
    }
3679
}
3680
3681
/* ------------------------------------------------------------------------ */
3682
/*
3683
 *   "delegated" 
3684
 */
3685
void CTPNDelegated::gen_code(int discard, int for_condition)
3686
{
3687
    /* 
3688
     *   we should never be asked to generate a "delegated" node directly;
3689
     *   these nodes should always be generated as part of member evaluation 
3690
     */
3691
    G_tok->throw_internal_error(TCERR_GEN_CODE_DELEGATED);
3692
}
3693
3694
/*
3695
 *   evaluate a property 
3696
 */
3697
void CTPNDelegated::gen_code_member(int discard,
3698
                                    CTcPrsNode *prop_expr, int prop_is_expr,
3699
                                    int argc, int varargs)
3700
{
3701
    vm_prop_id_t prop;
3702
3703
    /* 
3704
     *   make sure "self" is available - we obviously can't delegate
3705
     *   anything if we're not in an object's method 
3706
     */
3707
    if (!G_cs->is_self_available())
3708
        G_tok->log_error(TCERR_SELF_NOT_AVAIL);
3709
3710
    /* don't allow 'delegated' in speculative evaluation mode */
3711
    if (G_cg->is_speculative())
3712
        err_throw(VMERR_BAD_SPEC_EVAL);
3713
3714
    /* generate the delegatee expression */
3715
    delegatee_->gen_code(FALSE, FALSE);
3716
3717
    /* if we have an argument counter, put it back on top */
3718
    if (varargs)
3719
        G_cg->write_op(OPC_SWAP);
3720
3721
    /* generate the property value */
3722
    prop = prop_expr->gen_code_propid(FALSE, prop_is_expr);
3723
3724
    /* 
3725
     *   if we got a property ID, generate a simple DELEGATE; otherwise,
3726
     *   generate a PTRDELEGATE 
3727
     */
3728
    if (prop != VM_INVALID_PROP)
3729
    {
3730
        /* add a varargs modifier if needed */
3731
        if (varargs)
3732
            G_cg->write_op(OPC_VARARGC);
3733
3734
        /* we have a constant property ID - generate a regular DELEGATE */
3735
        G_cg->write_op(OPC_DELEGATE);
3736
        G_cs->write((char)argc);
3737
        G_cs->write_prop_id(prop);
3738
3739
        /* this removes arguments and the object value */
3740
        G_cg->note_pop(argc + 1);
3741
    }
3742
    else
3743
    {
3744
        /* 
3745
         *   if we have a varargs list, modify the call instruction that
3746
         *   follows to make it a varargs call 
3747
         */
3748
        if (varargs)
3749
        {
3750
            /* swap the top of the stack to get the arg counter back on top */
3751
            G_cg->write_op(OPC_SWAP);
3752
3753
            /* write the varargs modifier */
3754
            G_cg->write_op(OPC_VARARGC);
3755
        }
3756
3757
        /* a property pointer is on the stack - write a PTRDELEGATE */
3758
        G_cg->write_op(OPC_PTRDELEGATE);
3759
        G_cs->write((int)argc);
3760
3761
        /* this removes arguments, the object, and the property pointer */
3762
        G_cg->note_pop(argc + 2);
3763
    }
3764
3765
    /* if the result is needed, push it */
3766
    if (!discard)
3767
    {
3768
        G_cg->write_op(OPC_GETR0);
3769
        G_cg->note_push();
3770
    }
3771
}
3772
3773
3774
3775
/* ------------------------------------------------------------------------ */
3776
/*
3777
 *   "argcount" 
3778
 */
3779
void CTPNArgc::gen_code(int discard, int)
3780
{
3781
    /* generate the argument count, if we're not discarding */
3782
    if (!discard)
3783
    {
3784
        if (G_cg->is_eval_for_debug())
3785
        {
3786
            /* generate a debug argument count evaluation */
3787
            G_cg->write_op(OPC_GETDBARGC);
3788
            G_cs->write2(G_cg->get_debug_stack_level());
3789
        }
3790
        else
3791
        {
3792
            /* generate the normal argument count evaluation */
3793
            G_cg->write_op(OPC_GETARGC);
3794
        }
3795
3796
        /* we push one element */
3797
        G_cg->note_push();
3798
    }
3799
}
3800
3801
3802
/* ------------------------------------------------------------------------ */
3803
/*
3804
 *   constant
3805
 */
3806
void CTPNConst::gen_code(int discard, int)
3807
{
3808
    /* if we're discarding the value, do nothing */
3809
    if (discard)
3810
        return;
3811
    
3812
    /* generate the appropriate type of push for the value */
3813
    switch(val_.get_type())
3814
    {
3815
    case TC_CVT_NIL:
3816
        G_cg->write_op(OPC_PUSHNIL);
3817
        break;
3818
3819
    case TC_CVT_TRUE:
3820
        G_cg->write_op(OPC_PUSHTRUE);
3821
        break;
3822
3823
    case TC_CVT_INT:
3824
        /* write the push-integer instruction */
3825
        s_gen_code_int(val_.get_val_int());
3826
3827
        /* s_gen_code_int notes a push, which we'll do also, so cancel it */
3828
        G_cg->note_pop();
3829
        break;
3830
3831
    case TC_CVT_FLOAT:
3832
        /* we'll represent it as a BigNumber object */
3833
        G_cg->write_op(OPC_PUSHOBJ);
3834
3835
        /* generate the BigNumber object and write its ID */
3836
        G_cs->write_obj_id(G_cg->gen_bignum_obj(val_.get_val_float(),
3837
                                                val_.get_val_float_len()));
3838
        break;
3839
3840
    case TC_CVT_SSTR:
3841
        /* write the instruction to push a constant pool string */
3842
        G_cg->write_op(OPC_PUSHSTR);
3843
        
3844
        /* 
3845
         *   add the string to the constant pool, creating a fixup at the
3846
         *   current code stream location 
3847
         */
3848
        G_cg->add_const_str(val_.get_val_str(), val_.get_val_str_len(),
3849
                            G_cs, G_cs->get_ofs());
3850
3851
        /* 
3852
         *   write a placeholder address - this will be corrected by the
3853
         *   fixup that add_const_str() created for us 
3854
         */
3855
        G_cs->write4(0);
3856
        break;
3857
3858
    case TC_CVT_LIST:
3859
        /* write the instruction */
3860
        G_cg->write_op(OPC_PUSHLST);
3861
3862
        /* 
3863
         *   add the list to the constant pool, creating a fixup at the
3864
         *   current code stream location 
3865
         */
3866
        G_cg->add_const_list(val_.get_val_list(), G_cs, G_cs->get_ofs());
3867
3868
        /* 
3869
         *   write a placeholder address - this will be corrected by the
3870
         *   fixup that add_const_list() created for us
3871
         */
3872
        G_cs->write4(0);
3873
        break;
3874
3875
    case TC_CVT_OBJ:
3876
        /* generate the object ID */
3877
        G_cg->write_op(OPC_PUSHOBJ);
3878
        G_cs->write_obj_id(val_.get_val_obj());
3879
        break;
3880
3881
    case TC_CVT_PROP:
3882
        /* generate the property address */
3883
        G_cg->write_op(OPC_PUSHPROPID);
3884
        G_cs->write_prop_id(val_.get_val_prop());
3885
        break;
3886
3887
    case TC_CVT_ENUM:
3888
        /* generate the enum value */
3889
        G_cg->write_op(OPC_PUSHENUM);
3890
        G_cs->write_enum_id(val_.get_val_enum());
3891
        break;
3892
3893
    case TC_CVT_FUNCPTR:
3894
        /* generate the function pointer instruction */
3895
        G_cg->write_op(OPC_PUSHFNPTR);
3896
3897
        /* add a fixup for the function address */
3898
        val_.get_val_funcptr_sym()->add_abs_fixup(G_cs);
3899
3900
        /* write out a placeholder - arbitrarily use zero */
3901
        G_cs->write4(0);
3902
        break;
3903
3904
    case TC_CVT_ANONFUNCPTR:
3905
        /* generate the function pointer instruction */
3906
        G_cg->write_op(OPC_PUSHFNPTR);
3907
3908
        /* add a fixup for the code body address */
3909
        val_.get_val_anon_func_ptr()->add_abs_fixup(G_cs);
3910
3911
        /* write our a placeholder */
3912
        G_cs->write4(0);
3913
        break;
3914
3915
    default:
3916
        /* anything else is an internal error */
3917
        G_tok->throw_internal_error(TCERR_GEN_UNK_CONST_TYPE);
3918
    }
3919
3920
    /* all of these push a value */
3921
    G_cg->note_push();
3922
}
3923
3924
/*
3925
 *   generate code to push an integer value 
3926
 */
3927
void CTPNConst::s_gen_code_int(long intval)
3928
{
3929
    /* push the smallest format that will fit the value */
3930
    if (intval == 0)
3931
    {
3932
        /* write the special PUSH_0 instruction */
3933
        G_cg->write_op(OPC_PUSH_0);
3934
    }
3935
    else if (intval == 1)
3936
    {
3937
        /* write the special PUSH_1 instruction */
3938
        G_cg->write_op(OPC_PUSH_1);
3939
    }
3940
    else if (intval < 127 && intval >= -128)
3941
    {
3942
        /* it fits in eight bits */
3943
        G_cg->write_op(OPC_PUSHINT8);
3944
        G_cs->write((char)intval);
3945
    }
3946
    else
3947
    {
3948
        /* it doesn't fit in 8 bits - use a full 32 bits */
3949
        G_cg->write_op(OPC_PUSHINT);
3950
        G_cs->write4(intval);
3951
    }
3952
3953
    /* however we did it, we left one value on the stack */
3954
    G_cg->note_push();
3955
}
3956
3957
/*
3958
 *   Generate code to apply operator 'new' to the constant.  We can apply
3959
 *   'new' only to constant object values. 
3960
 */
3961
void CTPNConst::gen_code_new(int discard, int argc, int varargs,
3962
                             int /*from_call*/, int is_transient)
3963
{
3964
    /* check the type */
3965
    switch(val_.get_type())
3966
    {
3967
    case TC_CVT_OBJ:
3968
        /* 
3969
         *   Treat this the same as any other 'new' call.  An object symbol
3970
         *   folded into a constant is guaranteed to be of metaclass
3971
         *   TadsObject - that's the only kind of symbol we'll ever fold this
3972
         *   way. 
3973
         */
3974
        CTcSymObj::s_gen_code_new(discard,
3975
                                  val_.get_val_obj(), val_.get_val_obj_meta(),
3976
                                  argc, varargs, is_transient);
3977
        break;
3978
3979
    default:
3980
        /* can't apply 'new' to other constant values */
3981
        G_tok->log_error(TCERR_INVAL_NEW_EXPR);
3982
        break;
3983
    }
3984
}
3985
3986
/*
3987
 *   Generate code to make a function call to this expression.  If we're
3988
 *   calling a function, we can generate this directly.  
3989
 */
3990
void CTPNConst::gen_code_call(int discard, int argc, int varargs)
3991
{
3992
    /* check our type */
3993
    switch(val_.get_type())
3994
    {
3995
    case TC_CVT_FUNCPTR:
3996
        /* generate a call to our function symbol */
3997
        val_.get_val_funcptr_sym()->gen_code_call(discard, argc, varargs);
3998
        break;
3999
4000
    default:
4001
        /* other types cannot be called */
4002
        G_tok->log_error(TCERR_CANNOT_CALL_CONST);
4003
        break;
4004
    }
4005
}
4006
4007
/*
4008
 *   generate a property ID expression 
4009
 */
4010
vm_prop_id_t CTPNConst::gen_code_propid(int check_only, int is_expr)
4011
{
4012
    /* check the type */
4013
    switch(val_.get_type())
4014
    {
4015
    case TC_CVT_PROP:
4016
        /* return the constant property ID */
4017
        return (vm_prop_id_t)val_.get_val_prop();
4018
4019
    default:
4020
        /* other values cannot be used as properties */
4021
        if (!check_only)
4022
            G_tok->log_error(TCERR_INVAL_PROP_EXPR);
4023
        return VM_INVALID_PROP;
4024
    }
4025
}
4026
4027
4028
/*
4029
 *   Generate code for a member evaluation 
4030
 */
4031
void CTPNConst::gen_code_member(int discard,
4032
                                CTcPrsNode *prop_expr, int prop_is_expr,
4033
                                int argc, int varargs)
4034
{
4035
    /* check our constant type */
4036
    switch(val_.get_type())
4037
    {
4038
    case TC_CVT_OBJ:
4039
        /* call the object symbol code to do the work */
4040
        CTcSymObj::s_gen_code_member(discard, prop_expr, prop_is_expr,
4041
                                     argc, val_.get_val_obj(), varargs);
4042
        break;
4043
4044
    case TC_CVT_LIST:
4045
    case TC_CVT_SSTR:
4046
    case TC_CVT_FLOAT:
4047
        /* 
4048
         *   list/string/BigNumber constant - generate our value as
4049
         *   normal, then use the standard member generation 
4050
         */
4051
        gen_code(FALSE, FALSE);
4052
4053
        /* if we have an argument counter, put it back on top */
4054
        if (varargs)
4055
            G_cg->write_op(OPC_SWAP);
4056
4057
        /* use standard member generation */
4058
        CTcPrsNode::s_gen_member_rhs(discard, prop_expr, prop_is_expr,
4059
                                     argc, varargs);
4060
        break;
4061
4062
    default:
4063
        G_tok->log_error(TCERR_INVAL_OBJ_EXPR);
4064
        break;
4065
    }
4066
}
4067
4068
4069
/*
4070
 *   generate code for an object before a '.'  
4071
 */
4072
vm_obj_id_t CTPNConst::gen_code_obj_predot(int *is_self)
4073
{
4074
    /* we're certainly not "self" */
4075
    *is_self = FALSE;
4076
4077
    /* if I don't have an object value, this is illegal */
4078
    if (val_.get_type() != TC_CVT_OBJ)
4079
    {
4080
        G_tok->log_error(TCERR_INVAL_OBJ_EXPR);
4081
        return VM_INVALID_OBJ;
4082
    }
4083
4084
    /* report our constant object value */
4085
    return val_.get_val_obj();
4086
}
4087
4088
/* ------------------------------------------------------------------------ */
4089
/*
4090
 *   debugger constant 
4091
 */
4092
void CTPNDebugConst::gen_code(int discard, int for_condition)
4093
{
4094
    /* if we're discarding the value, do nothing */
4095
    if (discard)
4096
        return;
4097
4098
    /* generate the appropriate type of push for the value */
4099
    switch(val_.get_type())
4100
    {
4101
    case TC_CVT_SSTR:
4102
        /* write the in-line string instruction */
4103
        G_cg->write_op(OPC_PUSHSTRI);
4104
        G_cs->write2(val_.get_val_str_len());
4105
        G_cs->write(val_.get_val_str(), val_.get_val_str_len());
4106
4107
        /* note the value push */
4108
        G_cg->note_push();
4109
        break;
4110
4111
    case TC_CVT_LIST:
4112
        /* we should never have a constant list when debugging */
4113
        assert(FALSE);
4114
        break;
4115
4116
    case TC_CVT_FUNCPTR:
4117
        /* generate the function pointer instruction */
4118
        G_cg->write_op(OPC_PUSHFNPTR);
4119
4120
        /* 
4121
         *   write the actual function address - no need for fixups in the
4122
         *   debugger, since everything's fully resolved 
4123
         */
4124
        G_cs->write4(val_.get_val_funcptr_sym()->get_code_pool_addr());
4125
4126
        /* note the value push */
4127
        G_cg->note_push();
4128
        break;
4129
4130
    case TC_CVT_ANONFUNCPTR:
4131
        /* 
4132
         *   we should never see an anonymous function pointer in the
4133
         *   debugger 
4134
         */
4135
        assert(FALSE);
4136
        break;
4137
4138
    case TC_CVT_FLOAT:
4139
        {
4140
            CTcSymMetaclass *sym;
4141
4142
            /* 
4143
             *   find the 'BigNumber' metaclass - if it's not defined, we
4144
             *   can't create BigNumber values 
4145
             */
4146
            sym = (CTcSymMetaclass *)G_prs->get_global_symtab()
4147
                  ->find("BigNumber", 9);
4148
            if (sym == 0 || sym->get_type() != TC_SYM_METACLASS)
4149
                err_throw(VMERR_INVAL_DBG_EXPR);
4150
4151
            /* push the floating value as an immediate string */
4152
            G_cg->write_op(OPC_PUSHSTRI);
4153
            G_cs->write2(val_.get_val_str_len());
4154
            G_cs->write(val_.get_val_str(), val_.get_val_str_len());
4155
4156
            /* create the new BigNumber object from the string */
4157
            G_cg->write_op(OPC_NEW2);
4158
            G_cs->write2(1);
4159
            G_cs->write2(sym->get_meta_idx());
4160
4161
            /* retrieve the value */
4162
            G_cg->write_op(OPC_GETR0);
4163
4164
            /* 
4165
             *   note the net push of one value (we pushed the argument,
4166
             *   popped the argument, and pushed the new object) 
4167
             */
4168
            G_cg->note_push();
4169
        }
4170
        break;
4171
4172
    default:
4173
        /* handle normally for anything else */
4174
        CTPNConst::gen_code(discard, for_condition);
4175
        break;
4176
    }
4177
}
4178
4179
4180
/* ------------------------------------------------------------------------ */
4181
/*
4182
 *   Generic Unary Operator 
4183
 */
4184
4185
/* 
4186
 *   Generate a unary-operator opcode.  We assume that the opcode has no
4187
 *   side effects other than to compute the result, so we do not generate
4188
 *   the opcode at all if 'discard' is true; we do, however, always
4189
 *   generate code for the subexpression to ensure that its side effects
4190
 *   are performed.
4191
 *   
4192
 *   In most cases, the caller simply should pass through its 'discard'
4193
 *   status, since the result of the subexpression is generally needed
4194
 *   only when the result of the enclosing expression is needed.
4195
 *   
4196
 *   In most cases, the caller should pass FALSE for 'for_condition',
4197
 *   because applying an operator to the result generally requires that
4198
 *   the result be properly converted for use as a temporary value.
4199
 *   However, when the caller knows that its own opcode will perform the
4200
 *   same conversions that a conditional opcode would, 'for_condition'
4201
 *   should be TRUE.  In most cases, the caller's own 'for_condition'
4202
 *   status is not relevant and should thus not be passed through.  
4203
 */
4204
void CTPNUnary::gen_unary(uchar opc, int discard, int for_condition)
4205
{
4206
    /* 
4207
     *   Generate the operand.  Pass through the 'discard' status to the
4208
     *   operand - if the result of the parent operator is being
4209
     *   discarded, then so is the result of this subexpression.  In
4210
     *   addition, pass through the caller's 'for_condition' disposition.  
4211
     */
4212
    sub_->gen_code(discard, for_condition);
4213
4214
    /* apply the operator if we're not discarding the result */
4215
    if (!discard)
4216
        G_cg->write_op(opc);
4217
}
4218
4219
/* ------------------------------------------------------------------------ */
4220
/*
4221
 *   Generic Binary Operator
4222
 */
4223
4224
/*
4225
 *   Generate a binary-operator opcode.
4226
 *   
4227
 *   In most cases, the caller's 'discard' status should be passed
4228
 *   through, since the results of the operands are usually needed if and
4229
 *   only if the results of the enclosing expression are needed.
4230
 *   
4231
 *   In most cases, the caller should pass FALSE for 'for_condition'.
4232
 *   Only when the caller knows that the opcode will perform the same
4233
 *   conversions as a BOOLIZE instruction should it pass TRUE for
4234
 *   'for_condition'.  
4235
 */
4236
void CTPNBin::gen_binary(uchar opc, int discard, int for_condition)
4237
{
4238
    /* 
4239
     *   generate the operands, passing through the discard and
4240
     *   conditional status 
4241
     */
4242
    left_->gen_code(discard, for_condition);
4243
    right_->gen_code(discard, for_condition);
4244
4245
    /* generate our operand if we're not discarding the result */
4246
    if (!discard)
4247
    {
4248
        /* apply the operator */
4249
        G_cg->write_op(opc);
4250
4251
        /* 
4252
         *   boolean operators all remove two values and push one, so
4253
         *   there's a net pop 
4254
         */
4255
        G_cg->note_pop();
4256
    }
4257
}
4258
4259
4260
4261
/* ------------------------------------------------------------------------ */
4262
/*
4263
 *   logical NOT
4264
 */
4265
void CTPNNot::gen_code(int discard, int)
4266
{
4267
    /*
4268
     *   Generate the subexpression and apply the NOT opcode.  Note that
4269
     *   we can compute the subexpression as though we were applying a
4270
     *   condition, because the NOT opcode takes exactly the same kind of
4271
     *   input as any condition opcode; we can thus avoid an extra
4272
     *   conversion in some cases.  
4273
     */
4274
    gen_unary(OPC_NOT, discard, TRUE);
4275
}
4276
4277
/* ------------------------------------------------------------------------ */
4278
/*
4279
 *   Boolean-ize operator
4280
 */
4281
void CTPNBoolize::gen_code(int discard, int for_condition)
4282
{
4283
    /*
4284
     *   If the result will be used for a conditional, there's no need to
4285
     *   generate an instruction to convert the value to boolean.  The opcode
4286
     *   that will be used for the condition will perform exactly the same
4287
     *   conversions that this opcode would apply; avoid the redundant work
4288
     *   in this case, and simply generate the underlying expression
4289
     *   directly.  
4290
     */
4291
    if (for_condition)
4292
    {
4293
        /* generate the underlying expression without modification */
4294
        sub_->gen_code(discard, for_condition);
4295
4296
        /* done */
4297
        return;
4298
    }
4299
    
4300
    /*
4301
     *   Generate the subexpression and apply the BOOLIZE operator.  Since
4302
     *   we're explicitly boolean-izing the value, there's no need for the
4303
     *   subexpression to do the same thing, so the subexpression can
4304
     *   pretend it's generating for a conditional.  
4305
     */
4306
    gen_unary(OPC_BOOLIZE, discard, TRUE);
4307
}
4308
4309
4310
/* ------------------------------------------------------------------------ */
4311
/*
4312
 *   bitwise NOT 
4313
 */
4314
void CTPNBNot::gen_code(int discard, int)
4315
{
4316
    gen_unary(OPC_BNOT, discard, FALSE);
4317
}
4318
4319
4320
/* ------------------------------------------------------------------------ */
4321
/*
4322
 *   arithmetic positive
4323
 */
4324
void CTPNPos::gen_code(int discard, int)
4325
{
4326
    /* 
4327
     *   simply generate our operand, since the operator itself has no
4328
     *   effect 
4329
     */
4330
    sub_->gen_code(discard, FALSE); 
4331
}
4332
4333
/* ------------------------------------------------------------------------ */
4334
/*
4335
 *   unary arithmetic negative
4336
 */
4337
void CTPNNeg::gen_code(int discard, int)
4338
{
4339
    gen_unary(OPC_NEG, discard, FALSE);
4340
}
4341
4342
/* ------------------------------------------------------------------------ */
4343
/*
4344
 *   pre-increment
4345
 */
4346
void CTPNPreInc::gen_code(int discard, int)
4347
{
4348
    /* ask the subnode to generate it */
4349
    if (!sub_->gen_code_asi(discard, TC_ASI_PREINC, 0, FALSE))
4350
    {
4351
        /* 
4352
         *   the subnode didn't handle it - generate code to evaluate the
4353
         *   subnode, increment that value, then assign the result back to
4354
         *   the subnode with a simple assignment 
4355
         */
4356
        sub_->gen_code(FALSE, FALSE);
4357
4358
        /* increment the value at top of stack */
4359
        G_cg->write_op(OPC_INC);
4360
4361
        /* 
4362
         *   generate a simple assignment back to the subexpression; if
4363
         *   we're using the value, let the simple assignment leave its
4364
         *   value on the stack, since the result is the value *after* the
4365
         *   increment 
4366
         */
4367
        sub_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE);
4368
    }
4369
}
4370
4371
/* ------------------------------------------------------------------------ */
4372
/*
4373
 *   pre-decrement
4374
 */
4375
void CTPNPreDec::gen_code(int discard, int)
4376
{
4377
    /* ask the subnode to generate it */
4378
    if (!sub_->gen_code_asi(discard, TC_ASI_PREDEC, 0, FALSE))
4379
    {
4380
        /* 
4381
         *   the subnode didn't handle it - generate code to evaluate the
4382
         *   subnode, decrement that value, then assign the result back to
4383
         *   the subnode with a simple assignment 
4384
         */
4385
        sub_->gen_code(FALSE, FALSE);
4386
4387
        /* decrement the value at top of stack */
4388
        G_cg->write_op(OPC_DEC);
4389
4390
        /* 
4391
         *   generate a simple assignment back to the subexpression; if
4392
         *   we're using the value, let the simple assignment leave its
4393
         *   value on the stack, since the result is the value *after* the
4394
         *   decrement 
4395
         */
4396
        sub_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE);
4397
    }
4398
}
4399
4400
/* ------------------------------------------------------------------------ */
4401
/*
4402
 *   post-increment
4403
 */
4404
void CTPNPostInc::gen_code(int discard, int)
4405
{
4406
    /* ask the subnode to generate it */
4407
    if (!sub_->gen_code_asi(discard, TC_ASI_POSTINC, 0, FALSE))
4408
    {
4409
        /* 
4410
         *   the subnode didn't handle it - generate code to evaluate the
4411
         *   subnode, increment that value, then assign the result back to
4412
         *   the subnode with a simple assignment 
4413
         */
4414
        sub_->gen_code(FALSE, FALSE);
4415
4416
        /* 
4417
         *   if we're keeping the result, duplicate the value at top of
4418
         *   stack prior to the increment - since this is a
4419
         *   post-increment, the result is the value *before* the
4420
         *   increment 
4421
         */
4422
        if (!discard)
4423
        {
4424
            G_cg->write_op(OPC_DUP);
4425
            G_cg->note_push();
4426
        }
4427
4428
        /* increment the value at top of stack */
4429
        G_cg->write_op(OPC_INC);
4430
4431
        /* 
4432
         *   Generate a simple assignment back to the subexpression.
4433
         *   Discard the result of this assignment, regardless of whether
4434
         *   the caller wants the result of the overall expression,
4435
         *   because we've already pushed the actual result, which is the
4436
         *   original value before the increment operation.
4437
         */
4438
        sub_->gen_code_asi(TRUE, TC_ASI_SIMPLE, 0, FALSE);
4439
    }
4440
}
4441
4442
/* ------------------------------------------------------------------------ */
4443
/*
4444
 *   post-decrement
4445
 */
4446
void CTPNPostDec::gen_code(int discard, int)
4447
{
4448
    /* ask the subnode to generate it */
4449
    if (!sub_->gen_code_asi(discard, TC_ASI_POSTDEC, 0, FALSE))
4450
    {
4451
        /* 
4452
         *   the subnode didn't handle it - generate code to evaluate the
4453
         *   subnode, decrement that value, then assign the result back to
4454
         *   the subnode with a simple assignment 
4455
         */
4456
        sub_->gen_code(FALSE, FALSE);
4457
4458
        /* 
4459
         *   if we're keeping the result, duplicate the value at top of
4460
         *   stack prior to the decrement - since this is a
4461
         *   post-decrement, the result is the value *before* the
4462
         *   decrement 
4463
         */
4464
        if (!discard)
4465
        {
4466
            G_cg->write_op(OPC_DUP);
4467
            G_cg->note_push();
4468
        }
4469
4470
        /* decrement the value at top of stack */
4471
        G_cg->write_op(OPC_DEC);
4472
4473
        /* 
4474
         *   Generate a simple assignment back to the subexpression.
4475
         *   Discard the result of this assignment, regardless of whether
4476
         *   the caller wants the result of the overall expression,
4477
         *   because we've already pushed the actual result, which is the
4478
         *   original value before the decrement operation.
4479
         */
4480
        sub_->gen_code_asi(TRUE, TC_ASI_SIMPLE, 0, FALSE);
4481
    }
4482
}
4483
4484
/* ------------------------------------------------------------------------ */
4485
/*
4486
 *   operator 'new'
4487
 */
4488
void CTPNNew::gen_code(int discard, int /*for condition*/)
4489
{
4490
    /* 
4491
     *   ask my subexpression to generate the code - at this point we
4492
     *   don't know the number of arguments, so pass in zero for now 
4493
     */
4494
    sub_->gen_code_new(discard, 0, FALSE, FALSE, transient_);
4495
}
4496
4497
/* ------------------------------------------------------------------------ */
4498
/*
4499
 *   operator 'delete'
4500
 */
4501
void CTPNDelete::gen_code(int, int)
4502
{
4503
    /* 'delete' generates no code for T3 VM */
4504
}
4505
4506
/* ------------------------------------------------------------------------ */
4507
/*
4508
 *   comma operator
4509
 */
4510
void CTPNComma::gen_code(int discard, int for_condition)
4511
{
4512
    /* 
4513
     *   Generate each side's code.  Note that the left side is *always*
4514
     *   discarded, regardless of whether the result of the comma operator
4515
     *   will be discarded.  After we generate our subexpressions, there's
4516
     *   nothing left to do, since the comma operator itself doesn't
4517
     *   change anything - we simply use the right operand result as our
4518
     *   result.
4519
     *   
4520
     *   Pass through the 'for_condition' status to the right operand,
4521
     *   since we pass through its result to the caller.  For the left
4522
     *   operand, treat it as a condition - we don't care about the result
4523
     *   value, so don't bother performing any extra conversions on it.  
4524
     */
4525
    left_->gen_code(TRUE, TRUE);
4526
    right_->gen_code(discard, for_condition);
4527
}
4528
4529
/* ------------------------------------------------------------------------ */
4530
/*
4531
 *   logical OR (short-circuit logic)
4532
 */
4533
void CTPNOr::gen_code(int discard, int for_condition)
4534
{
4535
    CTcCodeLabel *lbl;
4536
    
4537
    /* 
4538
     *   First, evaluate the left-hand side; we need the result even if
4539
     *   we're discarding the overall expression, since we will check the
4540
     *   result to see if we should even evaluate the right-hand side.
4541
     *   We're using the value for a condition, so don't bother
4542
     *   boolean-izing it.  
4543
     */
4544
    left_->gen_code(FALSE, TRUE);
4545
4546
    /* 
4547
     *   If the left-hand side is true, there's no need to evaluate the
4548
     *   right-hand side (and, in fact, we're not even allowed to evaluate
4549
     *   the right-hand side because of the short-circuit logic rule).
4550
     *   So, if the lhs is true, we want to jump around the code to
4551
     *   evaluate the rhs, saving the 'true' result if we're not
4552
     *   discarding the overall result. 
4553
     */
4554
    lbl = gen_jump_ahead(discard ? OPC_JT : OPC_JST);
4555
4556
    /* 
4557
     *   Evaluate the right-hand side.  We don't need to save the result
4558
     *   unless we need the result of the overall expression.  Generate
4559
     *   the value as though we were going to booleanize it ourselves,
4560
     *   since we'll do just that (hence pass for_condition = TRUE).  
4561
     */
4562
    right_->gen_code(discard, TRUE); 
4563
4564
    /*
4565
     *   If we discarded the result, we generated a JT which explicitly
4566
     *   popped a value.  If we didn't discard the result, we generated a
4567
     *   JST; this may or may not pop the value.  However, if it doesn't
4568
     *   pop the value (save on true), it will bypass the right side
4569
     *   evaluation, and will thus "pop" that value in the sense that it
4570
     *   will never be pushed.  So, note a pop either way.  
4571
     */
4572
    G_cg->note_pop();
4573
4574
    /* define the label for the jump over the rhs */
4575
    def_label_pos(lbl);
4576
4577
    /* 
4578
     *   if the result is not going to be used directly for a condition,
4579
     *   we must boolean-ize the value 
4580
     */
4581
    if (!for_condition)
4582
        G_cg->write_op(OPC_BOOLIZE);
4583
}
4584
4585
/*
4586
 *   Generate code for the short-circuit OR when used in a condition.  We can
4587
 *   use the fact that we're being used conditionally to avoid actually
4588
 *   pushing the result value onto the stack, instead simply branching to the
4589
 *   appropriate point in the enclosing control structure instead.  
4590
 */
4591
void CTPNOr::gen_code_cond(CTcCodeLabel *then_label,
4592
                           CTcCodeLabel *else_label)
4593
{
4594
    CTcCodeLabel *internal_then;
4595
4596
    /*
4597
     *   First, generate the conditional code for our left operand.  If the
4598
     *   condition is true, we can short-circuit the rest of the expression
4599
     *   by jumping directly to the 'then' label.  If the caller provided a
4600
     *   'then' label, we can jump directly to the caller's 'then' label;
4601
     *   otherwise, we must synthesize our own internal label, which we'll
4602
     *   define at the end of our generated code so that we'll fall through
4603
     *   on true to the enclosing code.  In any case, we want to fall through
4604
     *   if the condition is false, so that control will flow to the code for
4605
     *   our right operand if the left operand is false.  
4606
     */
4607
    internal_then = (then_label == 0 ? G_cs->new_label_fwd() : then_label);
4608
    left_->gen_code_cond(internal_then, 0);
4609
4610
    /* 
4611
     *   Now, generate code for our right operand.  We can generate this code
4612
     *   using the caller's destination labels directly: if we reach this
4613
     *   code at all, it's because the left operand was false, in which case
4614
     *   the result is simply the value of the right operand.  
4615
     */
4616
    right_->gen_code_cond(then_label, else_label);
4617
4618
    /* 
4619
     *   If we created an internal 'then' label, it goes at the end of our
4620
     *   generated code: this ensures that we fall off the end of our code
4621
     *   if the left subexpression is true, which is what the caller told us
4622
     *   they wanted when they gave us a null 'then' label.  If the caller
4623
     *   gave us an explicit 'then' label, we'll have jumped there directly
4624
     *   if the first subexpression was true.  
4625
     */
4626
    if (then_label == 0)
4627
        def_label_pos(internal_then);
4628
}
4629
4630
/* ------------------------------------------------------------------------ */
4631
/*
4632
 *   logical AND (short-circuit logic)
4633
 */
4634
void CTPNAnd::gen_code(int discard, int for_condition)
4635
{
4636
    CTcCodeLabel *lbl;
4637
4638
    /* 
4639
     *   first, evaluate the left-hand side; we need the result even if
4640
     *   we're discarding the overall expression, since we will check the
4641
     *   result to see if we should even evaluate the right-hand side 
4642
     */
4643
    left_->gen_code(FALSE, TRUE);
4644
 
4645
    /* 
4646
     *   If the left-hand side is false, there's no need to evaluate the
4647
     *   right-hand side (and, in fact, we're not even allowed to evaluate
4648
     *   the right-hand side because of the short-circuit logic rule).
4649
     *   So, if the lhs is false, we want to jump around the code to
4650
     *   evaluate the rhs, saving the false result if we're not discarding
4651
     *   the overall result.  
4652
     */
4653
    lbl = gen_jump_ahead(discard ? OPC_JF : OPC_JSF);
4654
4655
    /* 
4656
     *   Evaluate the right-hand side.  We don't need to save the result
4657
     *   unless we need the result of the overall expression.  
4658
     */
4659
    right_->gen_code(discard, TRUE);
4660
 
4661
    /* define the label for the jump over the rhs */
4662
    def_label_pos(lbl);
4663
4664
    /*
4665
     *   If we discarded the result, we generated a JF which explicitly
4666
     *   popped a value.  If we didn't discard the result, we generated a
4667
     *   JSF; this may or may not pop the value.  However, if it doesn't
4668
     *   pop the value (save on false), it will bypass the right side
4669
     *   evaluation, and will thus "pop" that value in the sense that it
4670
     *   will never be pushed.  So, note a pop either way.  
4671
     */
4672
    G_cg->note_pop();
4673
4674
    /* 
4675
     *   if the result is not going to be used directly for a condition,
4676
     *   we must boolean-ize the value 
4677
     */
4678
    if (!for_condition)
4679
        G_cg->write_op(OPC_BOOLIZE);
4680
}
4681
4682
/*
4683
 *   Generate code for the short-circuit AND when used in a condition.  We
4684
 *   can use the fact that we're being used conditionally to avoid actually
4685
 *   pushing the result value onto the stack, instead simply branching to the
4686
 *   appropriate point in the enclosing control structure instead.  
4687
 */
4688
void CTPNAnd::gen_code_cond(CTcCodeLabel *then_label,
4689
                            CTcCodeLabel *else_label)
4690
{
4691
    CTcCodeLabel *internal_else;
4692
4693
    /*
4694
     *   First, generate the conditional code for our left operand.  If the
4695
     *   condition is false, we can short-circuit the rest of the expression
4696
     *   by jumping directly to the 'else' label.  If the caller provided an
4697
     *   'else' label, we can jump directly to the caller's 'else' label;
4698
     *   otherwise, we must synthesize our own internal label, which we'll
4699
     *   define at the end of our generated code so that we'll fall through
4700
     *   on false to the enclosing code.  In any case, we want to fall
4701
     *   through if the condition is true, so that control will flow to the
4702
     *   code for our right operand if the left operand is true.  
4703
     */
4704
    internal_else = (else_label == 0 ? G_cs->new_label_fwd() : else_label);
4705
    left_->gen_code_cond(0, internal_else);
4706
4707
    /* 
4708
     *   Now, generate code for our right operand.  We can generate this code
4709
     *   using the caller's destination labels directly: if we reach this
4710
     *   code at all, it's because the left operand was true, in which case
4711
     *   the result is simply the value of the right operand.  
4712
     */
4713
    right_->gen_code_cond(then_label, else_label);
4714
4715
    /* 
4716
     *   If we created an internal 'else' label, it goes at the end of our
4717
     *   generated code: this ensures that we fall off the end of our code
4718
     *   if the left subexpression is false, which is what the caller told
4719
     *   us they wanted when they gave us a null 'else' label.  If the
4720
     *   caller gave us an explicit 'else' label, we'll have jumped there
4721
     *   directly if the first subexpression was false.  
4722
     */
4723
    if (else_label == 0)
4724
        def_label_pos(internal_else);
4725
}
4726
4727
4728
/* ------------------------------------------------------------------------ */
4729
/*
4730
 *   bitwise OR
4731
 */
4732
void CTPNBOr::gen_code(int discard, int)
4733
{
4734
    gen_binary(OPC_BOR, discard, FALSE);
4735
}
4736
4737
/* ------------------------------------------------------------------------ */
4738
/*
4739
 *   bitwise AND
4740
 */
4741
void CTPNBAnd::gen_code(int discard, int)
4742
{
4743
    gen_binary(OPC_BAND, discard, FALSE);
4744
}
4745
4746
/* ------------------------------------------------------------------------ */
4747
/*
4748
 *   bitwise XOR
4749
 */
4750
void CTPNBXor::gen_code(int discard, int)
4751
{
4752
    gen_binary(OPC_XOR, discard, FALSE);
4753
}
4754
4755
/* ------------------------------------------------------------------------ */
4756
/*
4757
 *   greater-than
4758
 */
4759
void CTPNGt::gen_code(int discard, int)
4760
{
4761
    gen_binary(OPC_GT, discard, FALSE);
4762
}
4763
4764
/* ------------------------------------------------------------------------ */
4765
/*
4766
 *   greater-or-equal
4767
 */
4768
void CTPNGe::gen_code(int discard, int)
4769
{
4770
    gen_binary(OPC_GE, discard, FALSE);
4771
}
4772
4773
/* ------------------------------------------------------------------------ */
4774
/*
4775
 *   less-than
4776
 */
4777
void CTPNLt::gen_code(int discard, int)
4778
{
4779
    gen_binary(OPC_LT, discard, FALSE);
4780
}
4781
4782
/* ------------------------------------------------------------------------ */
4783
/*
4784
 *   less-or-equal
4785
 */
4786
void CTPNLe::gen_code(int discard, int)
4787
{
4788
    gen_binary(OPC_LE, discard, FALSE);
4789
}
4790
4791
/* ------------------------------------------------------------------------ */
4792
/*
4793
 *   compare for equality
4794
 */
4795
void CTPNEq::gen_code(int discard, int)
4796
{
4797
    gen_binary(OPC_EQ, discard, FALSE);
4798
}
4799
4800
/* ------------------------------------------------------------------------ */
4801
/*
4802
 *   compare for inequality
4803
 */
4804
void CTPNNe::gen_code(int discard, int)
4805
{
4806
    gen_binary(OPC_NE, discard, FALSE);
4807
}
4808
4809
/* ------------------------------------------------------------------------ */
4810
/*
4811
 *   'is in' 
4812
 */
4813
void CTPNIsIn::gen_code(int discard, int)
4814
{
4815
    CTPNArglist *lst;
4816
    CTPNArg *arg;
4817
    CTcCodeLabel *lbl_found;
4818
    CTcCodeLabel *lbl_done;
4819
4820
    /* allocate our 'found' label */
4821
    lbl_found = G_cs->new_label_fwd();
4822
4823
    /* 
4824
     *   allocate our 'done' label - we only need to do this if we don't
4825
     *   have a constant true value and we're not discarding the result
4826
     */
4827
    if (!const_true_ && !discard)
4828
        lbl_done = G_cs->new_label_fwd();
4829
4830
    /* generate my left-side expression */
4831
    left_->gen_code(FALSE, FALSE);
4832
4833
    /* the right side is always an argument list */
4834
    lst = (CTPNArglist *)right_;
4835
4836
    /* compare to each element in the list on the right */
4837
    for (arg = lst->get_arg_list_head() ; arg != 0 ;
4838
         arg = arg->get_next_arg())
4839
    {
4840
        /* 
4841
         *   duplicate the left-side value, so we don't have to generate
4842
         *   it again for this comparison 
4843
         */
4844
        G_cg->write_op(OPC_DUP);
4845
4846
        /* generate this list element */
4847
        arg->gen_code(FALSE, FALSE);
4848
4849
        /* if they're equal, jump to the 'found' label */
4850
        G_cg->write_op(OPC_JE);
4851
        G_cs->write_ofs2(lbl_found, 0);
4852
4853
        /* we pushed one more (DUP) and popped two (JE) */
4854
        G_cg->note_push(1);
4855
        G_cg->note_pop(2);
4856
    }
4857
4858
    /* 
4859
     *   Generate the code that comes at the end of all of tests when we
4860
     *   fail to find any matches - we simply discard the left-side value
4861
     *   from the stack, push our 'nil' value, and jump to the end label.
4862
     *   
4863
     *   If we have a constant 'true' value, there's no need to do any of
4864
     *   this, because we know that, even after testing all of our
4865
     *   non-constant values, there's a constant value that makes the
4866
     *   entire expression true, and we can thus just fall through to the
4867
     *   'found' code.
4868
     *   
4869
     *   If we're discarding the result, there's no need to push a
4870
     *   separate value for the result, so we can just fall through to the
4871
     *   common ending code in this case.  
4872
     */
4873
    if (!const_true_ && !discard)
4874
    {
4875
        G_cg->write_op(OPC_DISC);
4876
        G_cg->write_op(OPC_PUSHNIL);
4877
        G_cg->write_op(OPC_JMP);
4878
        G_cs->write_ofs2(lbl_done, 0);
4879
    }
4880
4881
    /* 
4882
     *   Generate the 'found' code - this discards the left-side value and
4883
     *   pushes our 'true' result.  Note that there's no reason to push
4884
     *   our result if we're discarding it.  
4885
     */
4886
    def_label_pos(lbl_found);
4887
    G_cg->write_op(OPC_DISC);
4888
4889
    /* 
4890
     *   if we're discarding the result, just note the pop of the left
4891
     *   value; otherwise, push our result 
4892
     */
4893
    if (discard)
4894
        G_cg->note_pop();
4895
    else
4896
        G_cg->write_op(OPC_PUSHTRUE);
4897
4898
    /* our 'done' label is here, if we needed one */
4899
    if (!const_true_ && !discard)
4900
        def_label_pos(lbl_done);
4901
}
4902
4903
/* ------------------------------------------------------------------------ */
4904
/*
4905
 *   'not in' 
4906
 */
4907
void CTPNNotIn::gen_code(int discard, int)
4908
{
4909
    CTPNArglist *lst;
4910
    CTPNArg *arg;
4911
    CTcCodeLabel *lbl_found;
4912
    CTcCodeLabel *lbl_done;
4913
4914
    /* allocate our 'found' label */
4915
    lbl_found = G_cs->new_label_fwd();
4916
4917
    /* 
4918
     *   allocate our 'done' label - we only need to do this if we don't
4919
     *   have a constant false value 
4920
     */
4921
    if (!const_false_ && !discard)
4922
        lbl_done = G_cs->new_label_fwd();
4923
4924
    /* generate my left-side expression */
4925
    left_->gen_code(FALSE, FALSE);
4926
4927
    /* the right side is always an argument list */
4928
    lst = (CTPNArglist *)right_;
4929
4930
    /* compare to each element in the list on the right */
4931
    for (arg = lst->get_arg_list_head() ; arg != 0 ;
4932
         arg = arg->get_next_arg())
4933
    {
4934
        /* 
4935
         *   duplicate the left-side value, so we don't have to generate
4936
         *   it again for this comparison 
4937
         */
4938
        G_cg->write_op(OPC_DUP);
4939
4940
        /* generate this list element */
4941
        arg->gen_code(FALSE, FALSE);
4942
4943
        /* if they're equal, jump to the 'found' label */
4944
        G_cg->write_op(OPC_JE);
4945
        G_cs->write_ofs2(lbl_found, 0);
4946
4947
        /* we pushed one more (DUP) and popped two (JE) */
4948
        G_cg->note_push(1);
4949
        G_cg->note_pop(2);
4950
    }
4951
4952
    /* 
4953
     *   Generate the code that comes at the end of all of tests when we
4954
     *   fail to find any matches - we simply discard the left-side value
4955
     *   from the stack, push our 'true' value, and jump to the end label.
4956
     *   
4957
     *   If we have a constant 'nil' value, however, there's no need to do
4958
     *   any of this, because we know that, even after testing all of our
4959
     *   non-constant values, there's a matching constant value that makes
4960
     *   the entire expression false (because 'not in' is false if we find
4961
     *   a match), and we can thus just fall through to the 'found' code.  
4962
     */
4963
    if (!const_false_ && !discard)
4964
    {
4965
        G_cg->write_op(OPC_DISC);
4966
        G_cg->write_op(OPC_PUSHTRUE);
4967
        G_cg->write_op(OPC_JMP);
4968
        G_cs->write_ofs2(lbl_done, 0);
4969
    }
4970
4971
    /* 
4972
     *   generate the 'found' code - this discards the left-side value and
4973
     *   pushes our 'nil' result (because the result of 'not in' is false
4974
     *   if we found the value) 
4975
     */
4976
    def_label_pos(lbl_found);
4977
    G_cg->write_op(OPC_DISC);
4978
4979
    /* push the result, or note the pop if we're just discarding it */
4980
    if (discard)
4981
        G_cg->note_pop();
4982
    else
4983
        G_cg->write_op(OPC_PUSHNIL);
4984
4985
    /* our 'done' label is here, if we needed one */
4986
    if (!const_false_ && !discard)
4987
        def_label_pos(lbl_done);
4988
}
4989
4990
/* ------------------------------------------------------------------------ */
4991
/*
4992
 *   bit-shift left
4993
 */
4994
void CTPNShl::gen_code(int discard, int)
4995
{
4996
    gen_binary(OPC_SHL, discard, FALSE);
4997
}
4998
4999
/* ------------------------------------------------------------------------ */
5000
/*
5001
 *   bit-shift right
5002
 */
5003
void CTPNShr::gen_code(int discard, int)
5004
{
5005
    gen_binary(OPC_SHR, discard, FALSE);
5006
}
5007
5008
/* ------------------------------------------------------------------------ */
5009
/*
5010
 *   multiply
5011
 */
5012
void CTPNMul::gen_code(int discard, int)
5013
{
5014
    /* if either side is zero or one, we can apply special handling */
5015
    if (left_->is_const_int(0))
5016
    {
5017
        /* evaluate the right for side effects and discard the result */
5018
        right_->gen_code(TRUE, TRUE);
5019
5020
        /* the result is zero */
5021
        G_cg->write_op(OPC_PUSH_0);
5022
        G_cg->note_push();
5023
5024
        /* done */
5025
        return;
5026
    }
5027
    else if (right_->is_const_int(0))
5028
    {
5029
        /* evaluate the left for side effects and discard the result */
5030
        left_->gen_code(TRUE, TRUE);
5031
5032
        /* the result is zero */
5033
        G_cg->write_op(OPC_PUSH_0);
5034
        G_cg->note_push();
5035
5036
        /* done */
5037
        return;
5038
    }
5039
    else if (left_->is_const_int(1))
5040
    {
5041
        /* 
5042
         *   evaluate the right side - it's the result; note that, because
5043
         *   of the explicit multiplication, we must compute logical
5044
         *   results using assignment (not 'for condition') rules 
5045
         */
5046
        right_->gen_code(discard, FALSE);
5047
5048
        /* done */
5049
        return;
5050
    }
5051
    else if (right_->is_const_int(1))
5052
    {
5053
        /* evaluate the right side - it's the result */
5054
        left_->gen_code(discard, FALSE);
5055
5056
        /* done */
5057
        return;
5058
    }
5059
5060
    /* apply generic handling */
5061
    gen_binary(OPC_MUL, discard, FALSE);
5062
}
5063
5064
/* ------------------------------------------------------------------------ */
5065
/*
5066
 *   divide
5067
 */
5068
void CTPNDiv::gen_code(int discard, int for_cond)
5069
{
5070
    /* if dividing by 1, we can skip the whole thing (except side effects) */
5071
    if (right_->is_const_int(1))
5072
    {
5073
        /* 
5074
         *   simply generate the left side for side effects; actually
5075
         *   doing the arithmetic has no effect 
5076
         */
5077
        left_->gen_code(discard, for_cond);
5078
        return;
5079
    }
5080
5081
    /* if the left side is zero, the result is always zero */
5082
    if (left_->is_const_int(0))
5083
    {
5084
        /* evaluate the right for side effects, but discard the result */
5085
        right_->gen_code(TRUE, TRUE);
5086
5087
        /* the result is zero */
5088
        G_cg->write_op(OPC_PUSH_0);
5089
        G_cg->note_push();
5090
        return;
5091
    }
5092
5093
    /* use generic code generation */
5094
    gen_binary(OPC_DIV, discard, FALSE);
5095
}
5096
5097
/* ------------------------------------------------------------------------ */
5098
/*
5099
 *   modulo
5100
 */
5101
void CTPNMod::gen_code(int discard, int for_condition)
5102
{
5103
    /* if dividing by 1, we can skip the whole thing (except side effects) */
5104
    if (right_->is_const_int(1))
5105
    {
5106
        /* 
5107
         *   simply generate the left side for side effects; actually
5108
         *   doing the arithmetic has no effect 
5109
         */
5110
        left_->gen_code(discard, for_condition);
5111
5112
        /* the result is zero */
5113
        G_cg->write_op(OPC_PUSH_0);
5114
        G_cg->note_push();
5115
        return;
5116
    }
5117
5118
    /* if the left side is zero, the result is always zero */
5119
    if (left_->is_const_int(0))
5120
    {
5121
        /* evaluate the right for side effects, but discard the result */
5122
        right_->gen_code(TRUE, TRUE);
5123
5124
        /* the result is zero */
5125
        G_cg->write_op(OPC_PUSH_0);
5126
        G_cg->note_push();
5127
        return;
5128
    }
5129
5130
    /* use generic processing */
5131
    gen_binary(OPC_MOD, discard, FALSE);
5132
}
5133
5134
/* ------------------------------------------------------------------------ */
5135
/*
5136
 *   subtract
5137
 */
5138
void CTPNSub::gen_code(int discard, int for_cond)
5139
{
5140
    /* check for subtracting 1, which we can accomplish more efficiently */
5141
    if (right_->is_const_int(1))
5142
    {
5143
        /* 
5144
         *   We're subtracting one - use decrement.  The decrement
5145
         *   operator itself has no side effects, so we can pass through
5146
         *   the 'discard' status to the subnode.  
5147
         */
5148
        left_->gen_code(discard, FALSE);
5149
5150
        /* apply decrement if we're not discarding the result */
5151
        if (!discard)
5152
            G_cg->write_op(OPC_DEC);
5153
    }
5154
    else
5155
    {
5156
        /* we can't do anything special - use the general-purpose code */
5157
        gen_binary(OPC_SUB, discard, FALSE);
5158
    }
5159
}
5160
5161
/* ------------------------------------------------------------------------ */
5162
/*
5163
 *   add
5164
 */
5165
void CTPNAdd::gen_code(int discard, int)
5166
{
5167
    /* check for adding 1, which we can accomplish more efficiently */
5168
    if (right_->is_const_int(1))
5169
    {
5170
        /* 
5171
         *   We're adding one - use increment.  The increment operator
5172
         *   itself has no side effects, so we can pass through the
5173
         *   'discard' status to the subnode.  
5174
         */
5175
        left_->gen_code(discard, FALSE);
5176
        
5177
        /* apply increment if we're not discarding the result */
5178
        if (!discard)
5179
            G_cg->write_op(OPC_INC);
5180
    }
5181
    else
5182
    {
5183
        /* we can't do anything special - use the general-purpose code */
5184
        gen_binary(OPC_ADD, discard, FALSE);
5185
    }
5186
}
5187
5188
/* ------------------------------------------------------------------------ */
5189
/*
5190
 *   simple assignment
5191
 */
5192
void CTPNAsi::gen_code(int discard, int)
5193
{
5194
    /* 
5195
     *   Ask the left subnode to generate a simple assignment to the value
5196
     *   on the right.  Simple assignments cannot be refused, so we don't
5197
     *   need to try to do any assignment work ourselves.  
5198
     */
5199
    left_->gen_code_asi(discard, TC_ASI_SIMPLE, right_, FALSE);
5200
}
5201
5202
/* ------------------------------------------------------------------------ */
5203
/*
5204
 *   add and assign
5205
 */
5206
void CTPNAddAsi::gen_code(int discard, int)
5207
{
5208
    /* 
5209
     *   ask the left subnode to generate an add-and-assign; if it can't,
5210
     *   handle it generically 
5211
     */
5212
    if (!left_->gen_code_asi(discard, TC_ASI_ADD, right_, FALSE))
5213
    {
5214
        /* 
5215
         *   there's no special coding for this assignment type -- compute
5216
         *   the result generically, then assign the result as a simple
5217
         *   assignment, which cannot be refused
5218
         */
5219
        gen_binary(OPC_ADD, FALSE, FALSE);
5220
        left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE);
5221
    }
5222
}
5223
5224
/* ------------------------------------------------------------------------ */
5225
/*
5226
 *   subtract and assign
5227
 */
5228
void CTPNSubAsi::gen_code(int discard, int)
5229
{
5230
    /* 
5231
     *   ask the left subnode to generate a subtract-and-assign; if it
5232
     *   can't, handle it generically 
5233
     */
5234
    if (!left_->gen_code_asi(discard, TC_ASI_SUB, right_, FALSE))
5235
    {
5236
        /* 
5237
         *   there's no special coding for this assignment type -- compute
5238
         *   the result generically, then assign the result as a simple
5239
         *   assignment, which cannot be refused 
5240
         */
5241
        gen_binary(OPC_SUB, FALSE, FALSE);
5242
        left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE);
5243
    }
5244
}
5245
5246
/* ------------------------------------------------------------------------ */
5247
/*
5248
 *   multiply and assign
5249
 */
5250
void CTPNMulAsi::gen_code(int discard, int)
5251
{
5252
    /* 
5253
     *   ask the left subnode to generate a multiply-and-assign; if it
5254
     *   can't, handle it generically 
5255
     */
5256
    if (!left_->gen_code_asi(discard, TC_ASI_MUL, right_, FALSE))
5257
    {
5258
        /* 
5259
         *   there's no special coding for this assignment type -- compute
5260
         *   the result generically, then assign the result as a simple
5261
         *   assignment, which cannot be refused 
5262
         */
5263
        gen_binary(OPC_MUL, FALSE, FALSE);
5264
        left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE);
5265
    }
5266
}
5267
5268
/* ------------------------------------------------------------------------ */
5269
/*
5270
 *   divide and assign
5271
 */
5272
void CTPNDivAsi::gen_code(int discard, int)
5273
{
5274
    /* 
5275
     *   ask the left subnode to generate a divide-and-assign; if it
5276
     *   can't, handle it generically 
5277
     */
5278
    if (!left_->gen_code_asi(discard, TC_ASI_DIV, right_, FALSE))
5279
    {
5280
        /* 
5281
         *   there's no special coding for this assignment type -- compute
5282
         *   the result generically, then assign the result as a simple
5283
         *   assignment, which cannot be refused 
5284
         */
5285
        gen_binary(OPC_DIV, FALSE, FALSE);
5286
        left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE);
5287
    }
5288
}
5289
5290
/* ------------------------------------------------------------------------ */
5291
/*
5292
 *   modulo and assign
5293
 */
5294
void CTPNModAsi::gen_code(int discard, int)
5295
{
5296
    /* 
5297
     *   ask the left subnode to generate a mod-and-assign; if it can't,
5298
     *   handle it generically 
5299
     */
5300
    if (!left_->gen_code_asi(discard, TC_ASI_MOD, right_, FALSE))
5301
    {
5302
        /* 
5303
         *   there's no special coding for this assignment type -- compute
5304
         *   the result generically, then assign the result as a simple
5305
         *   assignment, which cannot be refused 
5306
         */
5307
        gen_binary(OPC_MOD, FALSE, FALSE);
5308
        left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE);
5309
    }
5310
}
5311
5312
/* ------------------------------------------------------------------------ */
5313
/*
5314
 *   bitwise-AND and assign
5315
 */
5316
void CTPNBAndAsi::gen_code(int discard, int)
5317
{
5318
    /* 
5319
     *   ask the left subnode to generate an AND-and-assign; if it can't,
5320
     *   handle it generically 
5321
     */
5322
    if (!left_->gen_code_asi(discard, TC_ASI_BAND, right_, FALSE))
5323
    {
5324
        /* 
5325
         *   there's no special coding for this assignment type -- compute
5326
         *   the result generically, then assign the result as a simple
5327
         *   assignment, which cannot be refused 
5328
         */
5329
        gen_binary(OPC_BAND, FALSE, FALSE);
5330
        left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE);
5331
    }
5332
}
5333
5334
/* ------------------------------------------------------------------------ */
5335
/*
5336
 *   bitwise-OR and assign
5337
 */
5338
void CTPNBOrAsi::gen_code(int discard, int)
5339
{
5340
    /* 
5341
     *   ask the left subnode to generate an OR-and-assign; if it can't,
5342
     *   handle it generically 
5343
     */
5344
    if (!left_->gen_code_asi(discard, TC_ASI_BOR, right_, FALSE))
5345
    {
5346
        /* 
5347
         *   there's no special coding for this assignment type -- compute
5348
         *   the result generically, then assign the result as a simple
5349
         *   assignment, which cannot be refused 
5350
         */
5351
        gen_binary(OPC_BOR, FALSE, FALSE);
5352
        left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE);
5353
    }
5354
}
5355
5356
/* ------------------------------------------------------------------------ */
5357
/*
5358
 *   bitwise-XOR and assign
5359
 */
5360
void CTPNBXorAsi::gen_code(int discard, int)
5361
{
5362
    /* 
5363
     *   ask the left subnode to generate an XOR-and-assign; if it can't,
5364
     *   handle it generically 
5365
     */
5366
    if (!left_->gen_code_asi(discard, TC_ASI_BXOR, right_, FALSE))
5367
    {
5368
        /* 
5369
         *   there's no special coding for this assignment type -- compute
5370
         *   the result generically, then assign the result as a simple
5371
         *   assignment, which cannot be refused 
5372
         */
5373
        gen_binary(OPC_XOR, FALSE, FALSE);
5374
        left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE);
5375
    }
5376
}
5377
5378
/* ------------------------------------------------------------------------ */
5379
/*
5380
 *   bit-shift left and assign
5381
 */
5382
void CTPNShlAsi::gen_code(int discard, int)
5383
{
5384
    /* 
5385
     *   ask the left subnode to generate an shift-left-and-assign; if it
5386
     *   can't, handle it generically 
5387
     */
5388
    if (!left_->gen_code_asi(discard, TC_ASI_SHL, right_, FALSE))
5389
    {
5390
        /* 
5391
         *   there's no special coding for this assignment type -- compute
5392
         *   the result generically, then assign the result as a simple
5393
         *   assignment, which cannot be refused 
5394
         */
5395
        gen_binary(OPC_SHL, FALSE, FALSE);
5396
        left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE);
5397
    }
5398
}
5399
5400
/* ------------------------------------------------------------------------ */
5401
/*
5402
 *   bit-shift right and assign
5403
 */
5404
void CTPNShrAsi::gen_code(int discard, int)
5405
{
5406
    /* 
5407
     *   ask the left subnode to generate a shift-right-and-assign; if it
5408
     *   can't, handle it generically 
5409
     */
5410
    if (!left_->gen_code_asi(discard, TC_ASI_SHR, right_, FALSE))
5411
    {
5412
        /* 
5413
         *   there's no special coding for this assignment type -- compute
5414
         *   the result generically, then assign the result as a simple
5415
         *   assignment, which cannot be refused 
5416
         */
5417
        gen_binary(OPC_SHR, FALSE, FALSE);
5418
        left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE);
5419
    }
5420
}
5421
5422
/* ------------------------------------------------------------------------ */
5423
/*
5424
 *   subscript a list/array value
5425
 */
5426
void CTPNSubscript::gen_code(int discard, int)
5427
{
5428
    gen_binary(OPC_INDEX, discard, FALSE);
5429
}
5430
5431
/*
5432
 *   assign to a subscripted value
5433
 */
5434
int CTPNSubscript::gen_code_asi(int discard, tc_asitype_t typ,
5435
                                CTcPrsNode *rhs, int)
5436
{
5437
    /* 
5438
     *   If this isn't a simple assignment, tell the caller to emit the
5439
     *   generic code to compute the composite result, then call us again
5440
     *   for a simple assignment.  We can't add any value with specialized
5441
     *   instructions for composite assignments, so there's no point in
5442
     *   dealing with those here. 
5443
     */
5444
    if (typ != TC_ASI_SIMPLE)
5445
        return FALSE;
5446
    
5447
    /* 
5448
     *   Generate the value to assign to the element - that's the right
5449
     *   side of the assignment operator.  If rhs is null, it means the
5450
     *   caller has already done this.  
5451
     */
5452
    if (rhs != 0)
5453
        rhs->gen_code(FALSE, FALSE);
5454
5455
    /* 
5456
     *   if we're not discarding the result, duplicate the value to be
5457
     *   assigned, so that it's left on the stack after we're finished
5458
     *   (this is necessary because we'll consume one copy with the SETIND
5459
     *   instruction) 
5460
     */
5461
    if (!discard)
5462
    {
5463
        G_cg->write_op(OPC_DUP);
5464
        G_cg->note_push();
5465
    }
5466
    
5467
    /* generate the value to be subscripted - that's my left-hand side */
5468
    left_->gen_code(FALSE, FALSE);
5469
5470
    /* generate the index value - that's my right-hand side */
5471
    right_->gen_code(FALSE, FALSE);
5472
5473
    /* generate the assign-to-indexed-value opcode */
5474
    G_cg->write_op(OPC_SETIND);
5475
5476
    /* setind pops three and pushes one - net of pop 2 */
5477
    G_cg->note_pop(2);
5478
5479
    /*
5480
     *   The top value now on the stack is the new container value.  The new
5481
     *   container will be different from the old container in some cases
5482
     *   (with lists, for example, because we must create a new list object
5483
     *   to contain the modified list value).  Therefore, if my left-hand
5484
     *   side is an lvalue, we must assign the new container to the left-hand
5485
     *   side - this makes something like "x[1] = 5" actually change the
5486
     *   value in "x" if "x" is a local variable.  If my left-hand side isn't
5487
     *   an lvalue, don't bother with this step, and simply discard the new
5488
     *   container value.
5489
     *   
5490
     *   Regardless of whether we're keeping the result of the overall
5491
     *   expression, we're definitely not keeping the result of assigning the
5492
     *   new container - the result of the assignment is the value assigned,
5493
     *   not the container.  Thus, discard = true in this call.  
5494
     *   
5495
     *   There's a special case that's handled through the peep-hole
5496
     *   optimizer: if we are assigning to a local variable and indexing with
5497
     *   a constant integer value, we will have converted the whole operation
5498
     *   to a SETINDLCL1I8.  That instruction takes care of assigning the
5499
     *   value back to the rvalue, so we don't need to generate a separate
5500
     *   rvalue assignment.  
5501
     */
5502
    if (G_cg->get_last_op() == OPC_SETINDLCL1I8)
5503
    {
5504
        /* 
5505
         *   no assignment is necessary - we just need to account for the
5506
         *   difference in the stack arrangement with this form of the
5507
         *   assignment, which is that we don't leave the value on the stack 
5508
         */
5509
        G_cg->note_pop();
5510
    }
5511
    else if (!left_->gen_code_asi(TRUE, TC_ASI_SIMPLE, 0, TRUE))
5512
    {
5513
        /* no assignment is possible; discard the new container value */
5514
        G_cg->write_op(OPC_DISC);
5515
        G_cg->note_pop();
5516
    }
5517
5518
    /* handled */
5519
    return TRUE;
5520
}
5521
5522
/* ------------------------------------------------------------------------ */
5523
/*
5524
 *   conditional operator
5525
 */
5526
void CTPNIf::gen_code(int discard, int for_condition)
5527
{
5528
    CTcCodeLabel *lbl_else;
5529
    CTcCodeLabel *lbl_end;
5530
5531
    /* 
5532
     *   Generate the condition value - we need the value regardless of
5533
     *   whether the overall result is going to be used, because we need
5534
     *   it to determine which branch to take.  Generate the subexpression
5535
     *   for a condition, so that we don't perform any extra unnecessary
5536
     *   conversions on it.  
5537
     */
5538
    first_->gen_code(FALSE, TRUE);
5539
 
5540
    /* if the condition is false, jump to the 'else' expression part */
5541
    lbl_else = gen_jump_ahead(OPC_JF);
5542
5543
    /* JF pops a value */
5544
    G_cg->note_pop();
5545
5546
    /* 
5547
     *   Generate the 'then' expression part.  Only request a return value if
5548
     *   it has one AND we're not discarding it.  If it doesn't return a
5549
     *   value, and we actually need one, we'll supply a default 'nil' value
5550
     *   next.  This value will be our yielded value (in this branch,
5551
     *   anyway), so pass through the for-condition flag.  
5552
     */
5553
    second_->gen_code(discard || !second_->has_return_value(), for_condition);
5554
5555
    /* 
5556
     *   If this expression has no return value, and we need the return
5557
     *   value, supply nil as the result.
5558
     */
5559
    if (!discard && !second_->has_return_value())
5560
    {
5561
        G_cg->write_op(OPC_PUSHNIL);
5562
        G_cg->note_push();
5563
    }
5564
 
5565
    /* unconditionally jump over the 'else' part */
5566
    lbl_end = gen_jump_ahead(OPC_JMP);
5567
5568
    /* set the label for the 'else' part */
5569
    def_label_pos(lbl_else);
5570
5571
    /* 
5572
     *   Generate the 'else' part.  Only request a return value if it has one
5573
     *   AND we're not discarding it.  Pass through 'discard' and
5574
     *   'for_condition', since this result is our result.  
5575
     */
5576
    third_->gen_code(discard || !third_->has_return_value(), for_condition);
5577
5578
    /* 
5579
     *   If this expression has no return value, and we need the return
5580
     *   value, supply nil as the result.  
5581
     */
5582
    if (!discard && !third_->has_return_value())
5583
    {
5584
        G_cg->write_op(OPC_PUSHNIL);
5585
        G_cg->note_push();
5586
    }
5587
 
5588
    /* 
5589
     *   Because of the jump, we only evaluate one of the two expressions
5590
     *   we generated, so note an extra pop for the branch we didn't take.
5591
     *   Note that if either one pushes a value, both will, since we'll
5592
     *   explicitly have pushed nil for the one that doesn't generate a
5593
     *   value to keep the stack balanced on both branches.
5594
     *   
5595
     *   If neither of our expressions yields a value, don't pop anything
5596
     *   extra, since we won't think we've pushed two values in the course
5597
     *   of generating the two expressions.  
5598
     */
5599
    if (second_->has_return_value() || third_->has_return_value())
5600
        G_cg->note_pop();
5601
 
5602
    /* set the label for the end of the expression */
5603
    def_label_pos(lbl_end);
5604
}
5605
5606
/* ------------------------------------------------------------------------ */
5607
/*
5608
 *   symbol
5609
 */
5610
void CTPNSym::gen_code(int discard, int)
5611
{
5612
    /* 
5613
     *   Look up the symbol; if it's undefined, add a default property
5614
     *   symbol entry if possible.  Then ask the symbol to generate the
5615
     *   code.  
5616
     */
5617
    G_cs->get_symtab()
5618
        ->find_or_def_prop_implied(get_sym_text(), get_sym_text_len(),
5619
                                   FALSE, G_cs->is_self_available())
5620
        ->gen_code(discard);
5621
}
5622
5623
/*
5624
 *   assign to a symbol
5625
 */
5626
int CTPNSym::gen_code_asi(int discard, tc_asitype_t typ, CTcPrsNode *rhs,
5627
                          int ignore_errors)
5628
{
5629
    /* 
5630
     *   Look up the symbol; if it's undefined and there's a "self" object
5631
     *   available, define it as a property by default, since a property
5632
     *   is the only kind of symbol that we could possibly assign to
5633
     *   without having defined anywhere in the program.  Once we have the
5634
     *   symbol, tell it to generate the code for assigning to it.  
5635
     */
5636
    return G_cs->get_symtab()
5637
        ->find_or_def_prop_implied(get_sym_text(), get_sym_text_len(),
5638
                                   FALSE, G_cs->is_self_available())
5639
        ->gen_code_asi(discard, typ, rhs, ignore_errors);
5640
}
5641
5642
/*
5643
 *   take the address of the symbol 
5644
 */
5645
void CTPNSym::gen_code_addr()
5646
{
5647
    /* 
5648
     *   Look up our symbol in the symbol table, then ask the resulting
5649
     *   symbol to generate the appropriate code.  If the symbol isn't
5650
     *   defined, and we have a "self" object available (i.e., we're in
5651
     *   method code), define the symbol by default as a property.
5652
     *   
5653
     *   Note that we look only in the global symbol table, because local
5654
     *   symbols have no address value.  So, even if the symbol is defined
5655
     *   in the local table, ignore the local definition and look at the
5656
     *   global definition.  
5657
     */
5658
    G_prs->get_global_symtab()
5659
        ->find_or_def_prop_explicit(get_sym_text(), get_sym_text_len(),
5660
                                    FALSE)
5661
        ->gen_code_addr();
5662
}
5663
5664
/*
5665
 *   call the symbol 
5666
 */
5667
void CTPNSym::gen_code_call(int discard, int argc, int varargs)
5668
{
5669
    /*
5670
     *   Look up our symbol in the symbol table, then ask the resulting
5671
     *   symbol to generate the appropriate call.  The symbol is
5672
     *   implicitly a property (if in a method context), since that's the
5673
     *   only kind of undefined symbol that we could be calling.  
5674
     */
5675
    G_cs->get_symtab()
5676
        ->find_or_def_prop_implied(get_sym_text(), get_sym_text_len(),
5677
                                   FALSE, G_cs->is_self_available())
5678
        ->gen_code_call(discard, argc, varargs);
5679
}
5680
5681
/*
5682
 *   generate code for 'new' 
5683
 */
5684
void CTPNSym::gen_code_new(int discard, int argc, int varargs,
5685
                           int /*from_call*/, int is_transient)
5686
{
5687
    /*
5688
     *   Look up our symbol, then ask the resulting symbol to generate the
5689
     *   'new' code.  If the symbol is undefined, add an 'undefined' entry
5690
     *   to the table; we can't implicitly create an object symbol. 
5691
     */
5692
    G_cs->get_symtab()
5693
        ->find_or_def_undef(get_sym_text(), get_sym_text_len(), FALSE)
5694
        ->gen_code_new(discard, argc, varargs, is_transient);
5695
}
5696
5697
/*
5698
 *   generate a property ID expression 
5699
 */
5700
vm_prop_id_t CTPNSym::gen_code_propid(int check_only, int is_expr)
5701
{
5702
    CTcSymbol *sym;
5703
    CTcPrsSymtab *symtab;
5704
5705
    /*
5706
     *   Figure out where to look for the symbol.  If the symbol was given
5707
     *   as an expression (in other words, it was explicitly enclosed in
5708
     *   parentheses), look it up in the local symbol table, since it
5709
     *   could refer to a local.  Otherwise, it must refer to a property,
5710
     *   so look only in the global table.
5711
     *   
5712
     *   If the symbol isn't defined already, define it as a property now.
5713
     *   Because the symbol is explicitly on the right side of a member
5714
     *   evaluation, we can define it as a property whether or not there's
5715
     *   a valid "self" in this context.  
5716
     */
5717
    if (is_expr)
5718
    {
5719
        /* it's an expression - look it up in the local symbol table */
5720
        symtab = G_cs->get_symtab();
5721
    }
5722
    else
5723
    {
5724
        /* it's a simple symbol - look only in the global symbol table */
5725
        symtab = G_prs->get_global_symtab();
5726
    }
5727
5728
    /* 
5729
     *   look it up (note that this will always return a valid symbol,
5730
     *   since it will create one if we can't find an existing entry) 
5731
     */
5732
    sym = symtab->find_or_def_prop(get_sym_text(), get_sym_text_len(), FALSE);
5733
5734
    /* ask the symbol to generate the property reference */
5735
    return sym->gen_code_propid(check_only, is_expr);
5736
}
5737
5738
/*
5739
 *   generate code for a member expression 
5740
 */
5741
void CTPNSym::gen_code_member(int discard, CTcPrsNode *prop_expr,
5742
                              int prop_is_expr, int argc, int varargs)
5743
{
5744
    /* 
5745
     *   Look up the symbol, and let it do the work.  There's no
5746
     *   appropriate default for the symbol, so leave it undefined if we
5747
     *   can't find it. 
5748
     */
5749
    G_cs->get_symtab()
5750
        ->find_or_def_undef(get_sym_text(), get_sym_text_len(), FALSE)
5751
        ->gen_code_member(discard, prop_expr, prop_is_expr, argc, varargs);
5752
}
5753
5754
/*
5755
 *   generate code for an object before a '.'  
5756
 */
5757
vm_obj_id_t CTPNSym::gen_code_obj_predot(int *is_self)
5758
{
5759
    /* 
5760
     *   Look up the symbol, and let it do the work.  There's no default
5761
     *   type for the symbol, so leave it undefined if we don't find it. 
5762
     */
5763
    return G_cs->get_symtab()
5764
        ->find_or_def_undef(get_sym_text(), get_sym_text_len(), FALSE)
5765
        ->gen_code_obj_predot(is_self);
5766
}
5767
5768
/* ------------------------------------------------------------------------ */
5769
/*
5770
 *   resolved symbol 
5771
 */
5772
void CTPNSymResolved::gen_code(int discard, int)
5773
{
5774
    /* let the symbol handle it */
5775
    sym_->gen_code(discard);
5776
}
5777
5778
/*
5779
 *   assign to a symbol 
5780
 */
5781
int CTPNSymResolved::gen_code_asi(int discard, tc_asitype_t typ,
5782
                                  CTcPrsNode *rhs,
5783
                                  int ignore_errors)
5784
{
5785
    /* let the symbol handle it */
5786
    return sym_->gen_code_asi(discard, typ, rhs, ignore_errors);
5787
}
5788
5789
/*
5790
 *   take the address of the symbol 
5791
 */
5792
void CTPNSymResolved::gen_code_addr()
5793
{
5794
    /* let the symbol handle it */
5795
    sym_->gen_code_addr();
5796
}
5797
5798
/*
5799
 *   call the symbol 
5800
 */
5801
void CTPNSymResolved::gen_code_call(int discard, int argc, int varargs)
5802
{
5803
    /* let the symbol handle it */
5804
    sym_->gen_code_call(discard, argc, varargs);
5805
}
5806
5807
/*
5808
 *   generate code for 'new' 
5809
 */
5810
void CTPNSymResolved::gen_code_new(int discard, int argc, int varargs,
5811
                                   int /*from_call*/, int is_transient)
5812
{
5813
    /* let the symbol handle it */
5814
    sym_->gen_code_new(discard, argc, varargs, is_transient);
5815
}
5816
5817
/*
5818
 *   generate a property ID expression 
5819
 */
5820
vm_prop_id_t CTPNSymResolved::gen_code_propid(int check_only, int is_expr)
5821
{
5822
    /* let the symbol handle it */
5823
    return sym_->gen_code_propid(check_only, is_expr);
5824
}
5825
5826
/*
5827
 *   generate code for a member expression 
5828
 */
5829
void CTPNSymResolved::gen_code_member(int discard, 
5830
                                      CTcPrsNode *prop_expr, int prop_is_expr,
5831
                                      int argc, int varargs)
5832
{
5833
    /* let the symbol handle it */
5834
    sym_->gen_code_member(discard, prop_expr, prop_is_expr, argc, varargs);
5835
}
5836
5837
/*
5838
 *   generate code for an object before a '.'  
5839
 */
5840
vm_obj_id_t CTPNSymResolved::gen_code_obj_predot(int *is_self)
5841
{
5842
    /* let the symbol handle it */
5843
    return sym_->gen_code_obj_predot(is_self);
5844
}
5845
5846
/* ------------------------------------------------------------------------ */
5847
/*
5848
 *   Debugger local variable symbol 
5849
 */
5850
5851
/*
5852
 *   generate code to evaluate the variable
5853
 */
5854
void CTPNSymDebugLocal::gen_code(int discard, int for_condition)
5855
{
5856
    /* if we're not discarding the value, push the local */
5857
    if (!discard)
5858
    {
5859
        /* generate the debugger local/parameter variable instruction */
5860
        G_cg->write_op(is_param_ ? OPC_GETDBARG : OPC_GETDBLCL);
5861
        G_cs->write2(var_id_);
5862
        G_cs->write2(frame_idx_);
5863
5864
        /* note that we pushed the value */
5865
        G_cg->note_push();
5866
5867
        /* if it's a context local, get the value from the context array */
5868
        if (ctx_arr_idx_ != 0)
5869
        {
5870
            CTPNConst::s_gen_code_int(ctx_arr_idx_);
5871
            G_cg->write_op(OPC_INDEX);
5872
5873
            /* 
5874
             *   the 'index' operation pops two values and pushes one, for a
5875
             *   net of one pop 
5876
             */
5877
            G_cg->note_pop();
5878
        }
5879
    }
5880
}
5881
5882
/*
5883
 *   generate code for assigning to this variable 
5884
 */
5885
int CTPNSymDebugLocal::gen_code_asi(int discard, tc_asitype_t typ,
5886
                                    CTcPrsNode *rhs, int ignore_error)    
5887
{
5888
    /* 
5889
     *   if this isn't a simple assignment, use the generic combination
5890
     *   assignment computation 
5891
     */
5892
    if (typ != TC_ASI_SIMPLE)
5893
        return FALSE;
5894
5895
    /* generate the value to be assigned */
5896
    if (rhs != 0)
5897
        rhs->gen_code(FALSE, FALSE);
5898
5899
    /* 
5900
     *   if we're not discarding the result, duplicate the value so we'll
5901
     *   have a copy after the assignment 
5902
     */
5903
    if (!discard)
5904
    {
5905
        G_cg->write_op(OPC_DUP);
5906
        G_cg->note_push();
5907
    }
5908
5909
    /* check for a context property */
5910
    if (ctx_arr_idx_ == 0)
5911
    {
5912
        /* 
5913
         *   generate the debug-local-set instruction - the operands are
5914
         *   the variable number and the stack frame index 
5915
         */
5916
        G_cg->write_op(is_param_ ? OPC_SETDBARG : OPC_SETDBLCL);
5917
        G_cs->write2(var_id_);
5918
        G_cs->write2(frame_idx_);
5919
    }
5920
    else
5921
    {
5922
        /* get the local containing our context object */
5923
        G_cg->write_op(OPC_GETDBLCL);
5924
        G_cs->write2(var_id_);
5925
        G_cs->write2(frame_idx_);
5926
5927
        /* set the actual variable value in the context object */
5928
        CTPNConst::s_gen_code_int(ctx_arr_idx_);
5929
        G_cg->write_op(OPC_SETIND);
5930
        G_cg->write_op(OPC_DISC);
5931
5932
        /* 
5933
         *   we did three pops (SETIND), then a push (SETIND), then a pop
5934
         *   (DISC) - this is a net of three extra pops
5935
         */
5936
        G_cg->note_pop(3);
5937
    }
5938
5939
    /* the debug-local-set removes the rvalue from the stack */
5940
    G_cg->note_pop();
5941
5942
    /* handled */
5943
    return TRUE;
5944
}
5945
5946
/* ------------------------------------------------------------------------ */
5947
/*
5948
 *   Double-quoted string.  The 'discard' status is irrelevant, because we
5949
 *   evaluate double-quoted strings for their side effects.  
5950
 */
5951
void CTPNDstr::gen_code(int discard, int)
5952
{
5953
    /* if we're not discarding the value, it's an error */
5954
    if (!discard)
5955
        G_tok->log_error(TCERR_DQUOTE_IN_EXPR, (int)len_, str_);
5956
5957
    /* generate the instruction to display it */
5958
    G_cg->write_op(OPC_SAY);
5959
5960
    /* add the string to the constant pool, creating a fixup here */
5961
    G_cg->add_const_str(str_, len_, G_cs, G_cs->get_ofs());
5962
5963
    /* write a placeholder value, which will be corrected by the fixup */
5964
    G_cs->write4(0);
5965
}
5966
5967
/* ------------------------------------------------------------------------ */
5968
/*
5969
 *   Double-quoted debug string 
5970
 */
5971
void CTPNDebugDstr::gen_code(int, int)
5972
{
5973
    /* generate code to push the in-line string */
5974
    G_cg->write_op(OPC_PUSHSTRI);
5975
    G_cs->write2(len_);
5976
    G_cs->write(str_, len_);
5977
5978
    /* write code to display the value */
5979
    G_cg->write_op(OPC_SAYVAL);
5980
5981
    /* note that we pushed the string and then popped it */
5982
    G_cg->note_push();
5983
    G_cg->note_pop();
5984
}
5985
5986
/* ------------------------------------------------------------------------ */
5987
/*
5988
 *   Double-quoted string embedding 
5989
 */
5990
5991
/*
5992
 *   create an embedding 
5993
 */
5994
CTPNDstrEmbed::CTPNDstrEmbed(CTcPrsNode *sub)
5995
    : CTPNDstrEmbedBase(sub)
5996
{
5997
}
5998
5999
/*
6000
 *   Generate code for a double-quoted string embedding 
6001
 */
6002
void CTPNDstrEmbed::gen_code(int, int)
6003
{
6004
    int orig_depth;
6005
6006
    /* note the stack depth before generating the expression */
6007
    orig_depth = G_cg->get_sp_depth();
6008
    
6009
    /* 
6010
     *   Generate code for the embedded expression.  If the expression has a
6011
     *   return value, generate the value so that it can be displayed in the
6012
     *   string; but don't request a value if it doesn't have one, as a
6013
     *   return value is optional in this context.  This is a normal value
6014
     *   invocation, not a conditional, so we need any applicable normal
6015
     *   value conversions.  
6016
     */
6017
    sub_->gen_code(!sub_->has_return_value(), FALSE);
6018
6019
    /* 
6020
     *   If the code generation left anything on the stack, generate code
6021
     *   to display the value via the default display function.  
6022
     */
6023
    if (G_cg->get_sp_depth() > orig_depth)
6024
    {
6025
        /* add a SAYVAL instruction */
6026
        G_cg->write_op(OPC_SAYVAL);
6027
6028
        /* SAYVAL pops the argument value */
6029
        G_cg->note_pop();
6030
    }
6031
}
6032
6033
6034
/* ------------------------------------------------------------------------ */
6035
/*
6036
 *   Argument list
6037
 */
6038
void CTPNArglist::gen_code_arglist(int *varargs)
6039
{
6040
    CTPNArg *arg;
6041
    int i;
6042
    int fixed_cnt;
6043
    int pushed_varargs_counter;
6044
6045
    /* 
6046
     *   scan the argument list for varargs - if we have any, we must
6047
     *   treat all of them as varargs 
6048
     */
6049
    for (*varargs = FALSE, fixed_cnt = 0, arg = get_arg_list_head() ;
6050
         arg != 0 ; arg = arg->get_next_arg())
6051
    {
6052
        /* if this is a varargs argument, we have varargs */
6053
        if (arg->is_varargs())
6054
        {
6055
            /* note it */
6056
            *varargs = TRUE;
6057
        }
6058
        else
6059
        {
6060
            /* count another fixed argument */
6061
            ++fixed_cnt;
6062
        }
6063
    }
6064
6065
    /* 
6066
     *   Push each argument in the list - start with the last element and
6067
     *   work backwards through the list to the first element.  The parser
6068
     *   builds the list in reverse order, so we must merely follow the
6069
     *   list from head to tail.
6070
     *   
6071
     *   We need each argument value to be pushed (hence discard = false),
6072
     *   and we need the assignable value of each argument expression
6073
     *   (hence for_condition = false).  
6074
     */
6075
    for (pushed_varargs_counter = FALSE, i = argc_,
6076
         arg = get_arg_list_head() ; arg != 0 ;
6077
         arg = arg->get_next_arg(), --i)
6078
    {
6079
        int depth;
6080
6081
        /* note the stack depth before generating the value */
6082
        depth = G_cg->get_sp_depth();
6083
6084
        /* 
6085
         *   check for varargs - if this is first varargs argument, push
6086
         *   the counter placeholder 
6087
         */
6088
        if (arg->is_varargs() && !pushed_varargs_counter)
6089
        {
6090
            /* 
6091
             *   write code to push the fixed argument count - we can use
6092
             *   this as a starting point, since we always know we have
6093
             *   this many argument to start with; we'll dynamically add
6094
             *   in the variable count at run-time 
6095
             */
6096
            CTPNConst::s_gen_code_int(fixed_cnt);
6097
            
6098
            /* note that we've pushed the counter */
6099
            pushed_varargs_counter = TRUE;
6100
6101
            /* 
6102
             *   we will take the extra value off when we evaluate the
6103
             *   varargs counter, so simply count it as removed now 
6104
             */
6105
            G_cg->note_pop();
6106
        }
6107
6108
        /* generate the argument's code */
6109
        arg->gen_code(FALSE, FALSE);
6110
6111
        /* 
6112
         *   if we've pushed the variable argument counter value onto the
6113
         *   stack, and this a fixed argument, swap the top two stack
6114
         *   elements to get the argument counter back to the top of the
6115
         *   stack; if this is a varargs argument there's no need, since
6116
         *   it will have taken care of this 
6117
         */
6118
        if (pushed_varargs_counter && !arg->is_varargs())
6119
            G_cg->write_op(OPC_SWAP);
6120
6121
        /* ensure that it generated something */
6122
        if (G_cg->get_sp_depth() <= depth)
6123
            G_tok->log_error(TCERR_ARG_EXPR_HAS_NO_VAL, i);
6124
    }
6125
}
6126
6127
/* ------------------------------------------------------------------------ */
6128
/*
6129
 *   argument list entry
6130
 */
6131
void CTPNArg::gen_code(int, int)
6132
{
6133
    /* 
6134
     *   Generate the argument expression.  We need the value (hence
6135
     *   discard = false), and we need the assignable value (hence
6136
     *   for_condition = false). 
6137
     */
6138
    get_arg_expr()->gen_code(FALSE, FALSE);
6139
6140
    /* 
6141
     *   if this is a list-to-varargs conversion, generate the conversion
6142
     *   instruction 
6143
     */
6144
    if (is_varargs_)
6145
    {
6146
        /* write the opcode */
6147
        G_cg->write_op(OPC_MAKELSTPAR);
6148
6149
        /* note the extra push and pop for the argument count */
6150
        G_cg->note_push();
6151
        G_cg->note_pop();
6152
    }
6153
}
6154
6155
/* ------------------------------------------------------------------------ */
6156
/*
6157
 *   function/method call
6158
 */
6159
6160
/*
6161
 *   create 
6162
 */
6163
CTPNCall::CTPNCall(CTcPrsNode *func, class CTPNArglist *arglist)
6164
    : CTPNCallBase(func, arglist)
6165
{
6166
    /* the T3 instruction set limits calls to 127 arguments */
6167
    if (arglist->get_argc() > 127)
6168
        G_tok->log_error(TCERR_TOO_MANY_CALL_ARGS);
6169
}
6170
6171
6172
/*
6173
 *   generate code 
6174
 */
6175
void CTPNCall::gen_code(int discard, int)
6176
{
6177
    int varargs;
6178
    
6179
    /* push the argument list */
6180
    get_arg_list()->gen_code_arglist(&varargs);
6181
6182
    /* generate an appropriate call instruction */
6183
    get_func()->gen_code_call(discard, get_arg_list()->get_argc(),
6184
                              varargs);
6185
}
6186
6187
/*
6188
 *   Generate code for operator 'new'.  A 'new' with an argument list
6189
 *   looks like a function call: NEW(CALL(object-contents, ARGLIST(...))).
6190
 */
6191
void CTPNCall::gen_code_new(int discard, int argc, int varargs,
6192
                            int from_call, int is_transient)
6193
{
6194
    /* 
6195
     *   if this is a recursive call from another 'call' node, it's not
6196
     *   allowed - we'd be trying to use the result of a call as the base
6197
     *   class of the 'new', which is illegal 
6198
     */
6199
    if (from_call)
6200
    {
6201
        G_tok->log_error(TCERR_INVAL_NEW_EXPR);
6202
        return;
6203
    }
6204
    
6205
    /* generate the argument list */
6206
    get_arg_list()->gen_code_arglist(&varargs);
6207
6208
    /* generate the code for the 'new' call */
6209
    get_func()->gen_code_new(discard, get_arg_list()->get_argc(), varargs,
6210
                             TRUE, is_transient);
6211
}
6212
6213
6214
/* ------------------------------------------------------------------------ */
6215
/*
6216
 *   member property evaluation
6217
 */
6218
void CTPNMember::gen_code(int discard, int)
6219
{
6220
    /* ask the object expression to generate the code */
6221
    get_obj_expr()->gen_code_member(discard, get_prop_expr(), prop_is_expr_,
6222
                                    0, FALSE);
6223
}
6224
6225
/*
6226
 *   assign to member expression
6227
 */
6228
int CTPNMember::gen_code_asi(int discard, tc_asitype_t typ, CTcPrsNode *rhs,
6229
                             int ignore_errors)
6230
{
6231
    int is_self;
6232
    vm_obj_id_t obj;
6233
    vm_prop_id_t prop;
6234
6235
    /* 
6236
     *   if it's not a simple assignment, tell the caller to generate the
6237
     *   generic code to compute the composite value, and then call us
6238
     *   again for a simple assignment 
6239
     */
6240
    if (typ != TC_ASI_SIMPLE)
6241
        return FALSE;
6242
6243
    /* generate the right-hand side, unless the caller has already done so */
6244
    if (rhs != 0)
6245
        rhs->gen_code(FALSE, FALSE);
6246
    
6247
    /* 
6248
     *   if the caller wants to use the assigned value, push a copy --
6249
     *   we'll consume one copy in the SETPROP or related instruction, so
6250
     *   we'll need another copy for the caller 
6251
     */
6252
    if (!discard)
6253
    {
6254
        G_cg->write_op(OPC_DUP);
6255
        G_cg->note_push();
6256
    }
6257
6258
    /* 
6259
     *   Determine what we have on the left: we could have self, a
6260
     *   constant object value, or any other expression.  
6261
     */
6262
    obj = get_obj_expr()->gen_code_obj_predot(&is_self);
6263
6264
    /* 
6265
     *   determine what kind of property expression we have - don't
6266
     *   generate any code for now, since we may need to generate some
6267
     *   more code ahead of the property generation 
6268
     */
6269
    prop = get_prop_expr()->gen_code_propid(TRUE, prop_is_expr_);
6270
6271
    /* determine what we need to do based on the operands */
6272
    if (prop == VM_INVALID_PROP)
6273
    {
6274
        /* 
6275
         *   We're assigning through a property pointer -- we must
6276
         *   generate a PTRSETPROP instruction.
6277
         *   
6278
         *   Before we generate the property expression, we must generate
6279
         *   the object expression.  If we got a constant object, we must
6280
         *   generate code to push that object value; otherwise, the code
6281
         *   to generate the object value is already generated. 
6282
         */
6283
        if (is_self)
6284
        {
6285
            /* self - generate code to push the "self" value */
6286
            G_cg->write_op(OPC_PUSHSELF);
6287
            G_cg->note_push();
6288
        }
6289
        else if (obj != VM_INVALID_OBJ)
6290
        {
6291
            /* constant object - generate code to push the value */
6292
            G_cg->write_op(OPC_PUSHOBJ);
6293
            G_cs->write_obj_id(obj);
6294
            G_cg->note_push();
6295
        }
6296
6297
        /* generate the property value expression */
6298
        get_prop_expr()->gen_code_propid(FALSE, prop_is_expr_);
6299
6300
        /* generate the PTRSETPROP instruction */
6301
        G_cg->write_op(OPC_PTRSETPROP);
6302
6303
        /* ptrsetprop removes three elements */
6304
        G_cg->note_pop(3);
6305
    }
6306
    else
6307
    {
6308
        /* 
6309
         *   We have a constant property value, so we have several
6310
         *   instructions to choose from.  If we're assigning to a
6311
         *   property of "self", use SETPROPSELF.  If we're assigning to a
6312
         *   constant object, use OBJSETPROP.  Otherwise, use the plain
6313
         *   SETPROP. 
6314
         */
6315
        if (is_self)
6316
        {
6317
            /* write the SETPROPSELF */
6318
            G_cg->write_op(OPC_SETPROPSELF);
6319
            G_cs->write_prop_id(prop);
6320
6321
            /* setpropself removes the value */
6322
            G_cg->note_pop();
6323
        }
6324
        else if (obj != VM_INVALID_OBJ)
6325
        {
6326
            /* write the OBJSETPROP */
6327
            G_cg->write_op(OPC_OBJSETPROP);
6328
            G_cs->write_obj_id(obj);
6329
            G_cs->write_prop_id(prop);
6330
6331
            /* objsetprop removes the value */
6332
            G_cg->note_pop();
6333
        }
6334
        else
6335
        {
6336
            /* 
6337
             *   write the normal SETPROP; we already generated the code
6338
             *   to push the object value, so it's where it should be 
6339
             */
6340
            G_cg->write_op(OPC_SETPROP);
6341
            G_cs->write_prop_id(prop);
6342
6343
            /* setprop removes the value and the object */
6344
            G_cg->note_pop(2);
6345
        }
6346
    }
6347
6348
    /* handled */
6349
    return TRUE;
6350
}
6351
6352
/* ------------------------------------------------------------------------ */
6353
/*
6354
 *   member with argument list
6355
 */
6356
void CTPNMemArg::gen_code(int discard, int)
6357
{
6358
    int varargs;
6359
    
6360
    /* push the argument list */
6361
    get_arg_list()->gen_code_arglist(&varargs);
6362
6363
    /* ask the object expression to generate the code */
6364
    get_obj_expr()->gen_code_member(discard, get_prop_expr(), prop_is_expr_,
6365
                                    get_arg_list()->get_argc(),
6366
                                    varargs);
6367
}
6368
6369
/* ------------------------------------------------------------------------ */
6370
/*
6371
 *   construct a list
6372
 */
6373
void CTPNList::gen_code(int discard, int for_condition)
6374
{
6375
    CTPNListEle *ele;
6376
    
6377
    /*
6378
     *   Before we construct the list dynamically, check to see if the
6379
     *   list is constant.  If it is, we need only built the list in the
6380
     *   constant pool, and push its offset.  
6381
     */
6382
    if (is_const())
6383
    {
6384
        /* push the value only if we're not discarding it */
6385
        if (!discard)
6386
        {
6387
            /* write the instruction */
6388
            G_cg->write_op(OPC_PUSHLST);
6389
6390
            /* add the list to the constant pool */
6391
            G_cg->add_const_list(this, G_cs, G_cs->get_ofs());
6392
6393
            /* 
6394
             *   write a placeholder address, which will be corrected by
6395
             *   the fixup that add_const_list() created 
6396
             */
6397
            G_cs->write4(0);
6398
6399
            /* note the push */
6400
            G_cg->note_push();
6401
        }
6402
6403
        /* done */
6404
        return;
6405
    }
6406
6407
    /*
6408
     *   It's not a constant list, so we must generate code to construct a
6409
     *   list dynamically.  Push each element of the list.  We need each
6410
     *   value (hence discard = false), and we require the assignable
6411
     *   value of each expression (hence for_condition = false).  Push the
6412
     *   argument list in reverse order, since the run-time metaclass
6413
     *   requires this ordering.  
6414
     */
6415
    for (ele = get_tail() ; ele != 0 ; ele = ele->get_prev())
6416
        ele->gen_code(FALSE, FALSE);
6417
6418
    /* generate a NEW instruction for an object of metaclass LIST */
6419
    if (get_count() <= 255)
6420
    {
6421
        /* the count will fit in one byte - use the short form */
6422
        G_cg->write_op(OPC_NEW1);
6423
        G_cs->write((char)get_count());
6424
        G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_LIST));
6425
    }
6426
    else
6427
    {
6428
        /* count doesn't fit in one byte - use the long form */
6429
        G_cg->write_op(OPC_NEW2);
6430
        G_cs->write2(get_count());
6431
        G_cs->write2(G_cg->get_predef_meta_idx(TCT3_METAID_LIST));
6432
    }
6433
    
6434
    /* new1/new2 remove arguments */
6435
    G_cg->note_pop(get_count());
6436
6437
    /* if we're not discarding the value, push it */
6438
    if (!discard)
6439
    {
6440
        G_cg->write_op(OPC_GETR0);
6441
        G_cg->note_push();
6442
    }
6443
}
6444
6445
/* ------------------------------------------------------------------------ */
6446
/*
6447
 *   list element
6448
 */
6449
void CTPNListEle::gen_code(int discard, int for_condition)
6450
{
6451
    /* generate the subexpression */
6452
    expr_->gen_code(discard, for_condition);
6453
}
6454
6455
/* ------------------------------------------------------------------------ */
6456
/*
6457
 *   Basic T3-specific symbol class 
6458
 */
6459
6460
/*
6461
 *   generate code to take the address of a symbol - in general, we cannot
6462
 *   take the address of a symbol, so we'll just log an error
6463
 */
6464
void CTcSymbol::gen_code_addr()
6465
{
6466
    G_tok->log_error(TCERR_NO_ADDR_SYM, (int)get_sym_len(), get_sym());
6467
}
6468
6469
/*
6470
 *   generate code to assign to the symbol - in general, we cannot assign
6471
 *   to a symbol, so we'll just log an error 
6472
 */
6473
int CTcSymbol::gen_code_asi(int, tc_asitype_t, class CTcPrsNode *,
6474
                            int ignore_error)
6475
{
6476
    /* 
6477
     *   if we're ignoring errors, simply return false to indicate that
6478
     *   nothing happened 
6479
     */
6480
    if (ignore_error)
6481
        return FALSE;
6482
6483
    /* log the error */
6484
    G_tok->log_error(TCERR_CANNOT_ASSIGN_SYM, (int)get_sym_len(), get_sym());
6485
6486
    /* 
6487
     *   even though we didn't generate anything, this has been fully
6488
     *   handled - the caller shouldn't attempt to generate any additional
6489
     *   code for this 
6490
     */
6491
    return TRUE;
6492
}
6493
6494
/*
6495
 *   Generate code for calling the symbol.  By default, we can't call a
6496
 *   symbol. 
6497
 */
6498
void CTcSymbol::gen_code_call(int, int, int)
6499
{
6500
    /* log an error */
6501
    G_tok->log_error(TCERR_CANNOT_CALL_SYM, (int)get_sym_len(), get_sym());
6502
}
6503
6504
/*
6505
 *   Generate code for operator 'new' 
6506
 */
6507
void CTcSymbol::gen_code_new(int, int, int, int)
6508
{
6509
    G_tok->log_error(TCERR_INVAL_NEW_EXPR);
6510
}
6511
6512
/* 
6513
 *   evaluate a property ID 
6514
 */
6515
vm_prop_id_t CTcSymbol::gen_code_propid(int check_only, int is_expr)
6516
{
6517
    /* by default, a symbol cannot be used as a property ID */
6518
    if (!check_only)
6519
        G_tok->log_error(TCERR_SYM_NOT_PROP, (int)get_sym_len(), get_sym());
6520
6521
    /* we can't return a valid property ID */
6522
    return VM_INVALID_PROP;
6523
}
6524
6525
/*
6526
 *   evaluate a member expression 
6527
 */
6528
void CTcSymbol::gen_code_member(int discard,
6529
                                CTcPrsNode *prop_expr, int prop_is_expr,
6530
                                int argc, int varargs)
6531
{
6532
    /* by default, a symbol cannot be used as an object expression */
6533
    G_tok->log_error(TCERR_SYM_NOT_OBJ, (int)get_sym_len(), get_sym());
6534
}
6535
6536
/*
6537
 *   generate code for an object expression before a '.' 
6538
 */
6539
vm_obj_id_t CTcSymbol::gen_code_obj_predot(int *is_self)
6540
{
6541
    /* by default, a symbol cannot be used as an object expression */
6542
    G_tok->log_error(TCERR_SYM_NOT_OBJ, (int)get_sym_len(), get_sym());
6543
6544
    /* indicate that we don't have a constant object */
6545
    *is_self = FALSE;
6546
    return VM_INVALID_OBJ;
6547
}
6548
6549
6550
6551
/* ------------------------------------------------------------------------ */
6552
/*
6553
 *   T3-specific function symbol class 
6554
 */
6555
6556
/*
6557
 *   evaluate the symbol 
6558
 */
6559
void CTcSymFunc::gen_code(int discard)
6560
{
6561
    /* 
6562
     *   function address are always unknown during code generation;
6563
     *   generate a placeholder instruction and add a fixup record for it 
6564
     */
6565
    G_cg->write_op(OPC_PUSHFNPTR);
6566
6567
    /* add a fixup for the current code location */
6568
    add_abs_fixup(G_cs);
6569
6570
    /* write a placeholder offset - arbitrarily use zero */
6571
    G_cs->write4(0);
6572
6573
    /* note the push */
6574
    G_cg->note_push();
6575
}
6576
6577
/*
6578
 *   take the address of the function 
6579
 */
6580
void CTcSymFunc::gen_code_addr()
6581
{
6582
    /* 
6583
     *   the address of a function cannot be taken - using the name alone
6584
     *   yields the address 
6585
     */
6586
    G_tok->log_error(TCERR_INVAL_FUNC_ADDR, (int)get_sym_len(), get_sym());
6587
}
6588
6589
6590
/*
6591
 *   call the symbol 
6592
 */
6593
void CTcSymFunc::gen_code_call(int discard, int argc, int varargs)
6594
{
6595
    /*
6596
     *   If this is a multi-method base function, a call to the function is
6597
     *   actually a call to _multiMethodCall('name', args).  
6598
     */
6599
    if (is_multimethod_base_)
6600
    {
6601
        /* make a list out of the arguments */
6602
        if (varargs)
6603
        {
6604
            G_cg->write_op(OPC_VARARGC);
6605
            G_cs->write((char)argc);
6606
        }
6607
        else if (argc <= 255)
6608
        {
6609
            G_cg->write_op(OPC_NEW1);
6610
            G_cs->write((char)argc);
6611
        }
6612
        else
6613
        {
6614
            G_cg->write_op(OPC_NEW2);
6615
            G_cs->write2(argc);
6616
        }
6617
        G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_LIST));
6618
        G_cg->note_pop(argc);
6619
6620
        G_cg->write_op(OPC_GETR0);
6621
        G_cg->note_push();
6622
6623
        /* add the base function pointer argument */
6624
        CTcConstVal funcval;
6625
        funcval.set_funcptr(this);
6626
        CTPNConst func(&funcval);
6627
        func.gen_code(FALSE, FALSE);
6628
6629
        /* look up _multiMethodCall */
6630
        static const char mmc_name[] = "_multiMethodCall";
6631
        static const size_t mmc_len = sizeof(mmc_name) - 1;
6632
        CTcSymFunc *mmc = (CTcSymFunc *)G_prs->get_global_symtab()->find(
6633
            mmc_name, mmc_len);
6634
        if (mmc == 0)
6635
        {
6636
            /* it's not defined - add an implied declaration for it */
6637
            mmc = new CTcSymFunc(mmc_name, mmc_len, FALSE, 1, TRUE, TRUE,
6638
                                 FALSE, FALSE, TRUE);
6639
            G_prs->get_global_symtab()->add_entry(mmc);
6640
        }
6641
        else if(mmc->get_type() != TC_SYM_FUNC)
6642
        {
6643
            /* it's defined, but not as a function - this is an error */
6644
            G_tok->log_error(TCERR_REDEF_AS_FUNC, (int)mmc_len, mmc_name);
6645
            return;
6646
        }
6647
6648
        /* 
6649
         *   Generate the call.  Note that there are always two arguments at
6650
         *   this point: the base function pointer, and the argument list.
6651
         *   The argument list is just one argument because we've already
6652
         *   constructed a list out of it.  
6653
         */
6654
        mmc->gen_code_call(discard, 2, FALSE);
6655
    }
6656
    else
6657
    {
6658
        /* write the varargs modifier if appropriate */
6659
        if (varargs)
6660
            G_cg->write_op(OPC_VARARGC);
6661
6662
        /* generate the call instruction and argument count */
6663
        G_cg->write_op(OPC_CALL);
6664
        G_cs->write((char)argc);
6665
6666
        /* check the mode */
6667
        if (G_cg->is_eval_for_debug())
6668
        {
6669
            /* 
6670
             *   debugger expression compilation - we know the absolute
6671
             *   address already, since all symbols are pre-resolved in the
6672
             *   debugger 
6673
             */
6674
            G_cs->write4(get_code_pool_addr());
6675
        }
6676
        else
6677
        {
6678
            /* 
6679
             *   Normal compilation - we won't know the function's address
6680
             *   until after generation is completed, so add a fixup for the
6681
             *   current location, then write a placeholder for the offset
6682
             *   field.  
6683
             */
6684
            add_abs_fixup(G_cs);
6685
            G_cs->write4(0);
6686
        }
6687
6688
        /* call removes arguments */
6689
        G_cg->note_pop(argc);
6690
6691
        /* make sure the argument count is correct */
6692
        if (varargs_ ? argc < argc_ : argc != argc_)
6693
            G_tok->log_error(TCERR_WRONG_ARGC_FOR_FUNC,
6694
                             (int)get_sym_len(), get_sym(), argc_, argc);
6695
6696
        /* if we're not discarding, push the return value from R0 */
6697
        if (!discard)
6698
        {
6699
            G_cg->write_op(OPC_GETR0);
6700
            G_cg->note_push();
6701
        }
6702
    }
6703
}
6704
6705
/*
6706
 *   Get my code pool address.  Valid only after linking. 
6707
 */
6708
ulong CTcSymFunc::get_code_pool_addr() const
6709
{
6710
    /* check for an absolute address */
6711
    if (abs_addr_valid_)
6712
    {
6713
        /* 
6714
         *   we have an absolute address - this means the symbol was
6715
         *   loaded from a fully-linked image file (specifically, from the
6716
         *   debug records) 
6717
         */
6718
        return abs_addr_;
6719
    }
6720
    else
6721
    {
6722
        /* 
6723
         *   we don't have an absolute address, so our address must have
6724
         *   been determined through a linking step - get the final
6725
         *   address from the anchor 
6726
         */
6727
        return anchor_->get_addr();
6728
    }
6729
}
6730
6731
/*
6732
 *   add a runtime symbol table entry 
6733
 */
6734
void CTcSymFunc::add_runtime_symbol(CVmRuntimeSymbols *symtab)
6735
{
6736
    vm_val_t val;
6737
    
6738
    /* add an entry for our absolute address */
6739
    val.set_fnptr(get_code_pool_addr());
6740
    symtab->add_sym(get_sym(), get_sym_len(), &val);
6741
}
6742
6743
6744
/* ------------------------------------------------------------------------ */
6745
/*
6746
 *   T3-specific object symbol class 
6747
 */
6748
6749
/*
6750
 *   evaluate the symbol 
6751
 */
6752
void CTcSymObj::gen_code(int discard)
6753
{
6754
    /* write code to push the object ID */
6755
    if (!discard)
6756
    {
6757
        /* push the object */
6758
        G_cg->write_op(OPC_PUSHOBJ);
6759
        G_cs->write_obj_id(obj_id_);
6760
6761
        /* note the push */
6762
        G_cg->note_push();
6763
    }
6764
}
6765
6766
/*
6767
 *   take the address of the object
6768
 */
6769
void CTcSymObj::gen_code_addr()
6770
{
6771
    /* act as though we were pushing the object ID directly */
6772
    gen_code(FALSE);
6773
}
6774
6775
/*
6776
 *   Generate a 'new' expression 
6777
 */
6778
void CTcSymObj::gen_code_new(int discard, int argc, int varargs,
6779
                             int is_transient)
6780
{
6781
    /* use our static generator */
6782
    s_gen_code_new(discard, obj_id_, metaclass_, argc, varargs, is_transient);
6783
}
6784
6785
/*
6786
 *   Generate a 'new' expression.  (This is a static method so that this
6787
 *   code can be used by all of the possible expression types to which
6788
 *   'new' can be applied.)
6789
 *   
6790
 *   This type of generation applies only to objects of metaclass TADS
6791
 *   Object.  
6792
 */
6793
void CTcSymObj::s_gen_code_new(int discard, vm_obj_id_t obj_id,
6794
                               tc_metaclass_t meta,
6795
                               int argc, int varargs, int is_transient)
6796
{
6797
    /* 
6798
     *   push the base class object - this is always the first argument
6799
     *   (hence last pushed) to the metaclass constructor 
6800
     */
6801
    G_cg->write_op(OPC_PUSHOBJ);
6802
    G_cs->write_obj_id(obj_id);
6803
6804
    /* note the push */
6805
    G_cg->note_push();
6806
6807
    /* 
6808
     *   note that we can only allow 126 arguments to a constructor,
6809
     *   because we must add the implicit superclass argument 
6810
     */
6811
    if (argc > 126)
6812
        G_tok->log_error(TCERR_TOO_MANY_CTOR_ARGS);
6813
6814
    /* 
6815
     *   if we have varargs, swap the top stack elements to get the
6816
     *   argument count back on top, and then generate the varargs
6817
     *   modifier opcode 
6818
     */
6819
    if (varargs)
6820
    {
6821
        /* swap the top stack elements to get argc back to the top */
6822
        G_cg->write_op(OPC_SWAP);
6823
6824
        /* 
6825
         *   increment the argument count to account for the superclass
6826
         *   object argument 
6827
         */
6828
        G_cg->write_op(OPC_INC);
6829
6830
        /* write the varargs modifier opcode */
6831
        G_cg->write_op(OPC_VARARGC);
6832
    }
6833
6834
    /* figure the metaclass index - the compiler can only generate known
6835
6836
    /* 
6837
     *   write the NEW instruction - since we always add TADS Object to
6838
     *   our metaclass table before we start compiling any code, we know
6839
     *   it always has a small metaclass number and will always fit in the
6840
     *   short form of the instruction
6841
     *   
6842
     *   Note that the actual argument count we generate is one higher
6843
     *   than the source code argument list, because we add the implicit
6844
     *   first argument to the metaclass constructor 
6845
     */
6846
    G_cg->write_op(is_transient ? OPC_TRNEW1 : OPC_NEW1);
6847
    G_cs->write((char)(argc + 1));
6848
6849
    /* write out the dependency table index for the metaclass */
6850
    switch (meta)
6851
    {
6852
    case TC_META_TADSOBJ:
6853
        G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_TADSOBJ));
6854
        break;
6855
6856
    default:
6857
        /* we can't use 'new' on symbols of other metaclasses */
6858
        G_tok->log_error(TCERR_BAD_META_FOR_NEW);
6859
        G_cs->write(0);
6860
        break;
6861
    }
6862
6863
    /* new1 removes the arguments */
6864
    G_cg->note_pop(argc + 1);
6865
6866
    /* 
6867
     *   if they're not discarding the value, push the new object
6868
     *   reference, which will be in R0 when the constructor returns 
6869
     */
6870
    if (!discard)
6871
    {
6872
        G_cg->write_op(OPC_GETR0);
6873
        G_cg->note_push();
6874
    }
6875
}
6876
6877
/*
6878
 *   Generate code for a member expression 
6879
 */
6880
void CTcSymObj::gen_code_member(int discard,
6881
                                CTcPrsNode *prop_expr, int prop_is_expr,
6882
                                int argc, int varargs)
6883
{
6884
    s_gen_code_member(discard, prop_expr, prop_is_expr,
6885
                      argc, obj_id_, varargs);
6886
}
6887
6888
/*
6889
 *   Static method to generate code for a member expression.  This is
6890
 *   static so that constant object nodes can share it.  
6891
 */
6892
void CTcSymObj::s_gen_code_member(int discard,
6893
                                  CTcPrsNode *prop_expr, int prop_is_expr,
6894
                                  int argc, vm_obj_id_t obj_id, int varargs)
6895
{
6896
    vm_prop_id_t prop;
6897
6898
    /* 
6899
     *   generate the property expression - don't generate the code right
6900
     *   now even if code generation is necessary, because this isn't the
6901
     *   right place for it; for now, simply check to determine if we're
6902
     *   going to need to generate any code for the property expression 
6903
     */
6904
    prop = prop_expr->gen_code_propid(TRUE, prop_is_expr);
6905
6906
    /* don't allow method calls with arguments in speculative mode */
6907
    if (argc != 0 && G_cg->is_speculative())
6908
        err_throw(VMERR_BAD_SPEC_EVAL);
6909
    
6910
    /* check for a constant property value */
6911
    if (prop != VM_INVALID_PROP)
6912
    {
6913
        /* generate an OBJGETPROP or OBJCALLPROP as appropriate */
6914
        if (G_cg->is_speculative())
6915
        {
6916
            /* speculative evaluation - use GETPROPDATA */
6917
            G_cg->write_op(OPC_PUSHOBJ);
6918
            G_cs->write_obj_id(obj_id);
6919
            G_cg->write_op(OPC_GETPROPDATA);
6920
            G_cs->write_prop_id(prop);
6921
6922
            /* we pushed the object, then popped it */
6923
            G_cg->note_push();
6924
            G_cg->note_pop();
6925
        }
6926
        else if (argc == 0)
6927
        {
6928
            /* no arguments - use OBJGETPROP */
6929
            G_cg->write_op(OPC_OBJGETPROP);
6930
            G_cs->write_obj_id(obj_id);
6931
            G_cs->write_prop_id(prop);
6932
        }
6933
        else
6934
        {
6935
            /* generate a varargs modifier if needed */
6936
            if (varargs)
6937
                G_cg->write_op(OPC_VARARGC);
6938
            
6939
            /* arguments - use OBJCALLPROP */
6940
            G_cg->write_op(OPC_OBJCALLPROP);
6941
            G_cs->write((char)argc);
6942
            G_cs->write_obj_id(obj_id);
6943
            G_cs->write_prop_id(prop);
6944
6945
            /* objcallprop removes arguments */
6946
            G_cg->note_pop(argc);
6947
        }
6948
    }
6949
    else
6950
    {
6951
        /* 
6952
         *   non-constant property value - we must first push the object
6953
         *   value, then push the property value, then write a PTRCALLPROP
6954
         *   instruction 
6955
         */
6956
6957
        /* generate the object push */
6958
        G_cg->write_op(OPC_PUSHOBJ);
6959
        G_cs->write_obj_id(obj_id);
6960
6961
        /* note the pushes */
6962
        G_cg->note_push();
6963
6964
        /* keep the argument counter on top if necessary */
6965
        if (varargs)
6966
            G_cg->write_op(OPC_SWAP);
6967
6968
        /* generate the property push */
6969
        prop_expr->gen_code_propid(FALSE, prop_is_expr);
6970
6971
        /* generate the PTRCALLPROP or PTRGETPROPDATA */
6972
        if (G_cg->is_speculative())
6973
        {
6974
            /* speculative - use the data-only property evaluation */
6975
            G_cg->write_op(OPC_PTRGETPROPDATA);
6976
        }
6977
        else
6978
        {
6979
            /* 
6980
             *   if we have a varargs list, modify the call instruction
6981
             *   that follows to make it a varargs call 
6982
             */
6983
            if (varargs)
6984
            {
6985
                /* swap to get the arg counter back on top */
6986
                G_cg->write_op(OPC_SWAP);
6987
                
6988
                /* write the varargs modifier */
6989
                G_cg->write_op(OPC_VARARGC);
6990
            }
6991
            
6992
            /* normal - call the property */
6993
            G_cg->write_op(OPC_PTRCALLPROP);
6994
            G_cs->write((int)argc);
6995
        }
6996
6997
        /* ptrcallprop removes the arguments, the object, and the property */
6998
        G_cg->note_pop(argc + 2);
6999
    }
7000
7001
    /* if they want the result, push it onto the stack */
7002
    if (!discard)
7003
    {
7004
        G_cg->write_op(OPC_GETR0);
7005
        G_cg->note_push();
7006
    }
7007
}
7008
7009
/*
7010
 *   generate code for an object before a '.'  
7011
 */
7012
vm_obj_id_t CTcSymObj::gen_code_obj_predot(int *is_self)
7013
{
7014
    /* return our constant object reference */
7015
    *is_self = FALSE;
7016
    return obj_id_;
7017
}
7018
7019
/*
7020
 *   add a runtime symbol table entry 
7021
 */
7022
void CTcSymObj::add_runtime_symbol(CVmRuntimeSymbols *symtab)
7023
{
7024
    vm_val_t val;
7025
7026
    /* add our entry */
7027
    val.set_obj(obj_id_);
7028
    symtab->add_sym(get_sym(), get_sym_len(), &val);
7029
}
7030
7031
/* ------------------------------------------------------------------------ */
7032
/*
7033
 *   T3-specific property symbol class 
7034
 */
7035
7036
/*
7037
 *   evaluate the symbol 
7038
 */
7039
void CTcSymProp::gen_code(int discard)
7040
{
7041
    /* 
7042
     *   Evaluating a property is equivalent to calling the property on
7043
     *   the "self" object with no arguments.  If there's no "self"
7044
     *   object, an unqualified property evaluation is not possible, so
7045
     *   log an error if this is the case.  
7046
     */
7047
    if (!G_cs->is_self_available())
7048
    {
7049
        G_tok->log_error(TCERR_PROP_NEEDS_OBJ, (int)get_sym_len(), get_sym());
7050
        return;
7051
    }
7052
7053
    if (G_cg->is_speculative())
7054
    {
7055
        /* push 'self', then evaluate the property in data-only mode */
7056
        G_cg->write_op(OPC_PUSHSELF);
7057
        G_cg->write_op(OPC_GETPROPDATA);
7058
        G_cs->write_prop_id(prop_);
7059
7060
        /* we pushed the 'self' value then popped it again */
7061
        G_cg->note_push();
7062
        G_cg->note_pop();
7063
    }
7064
    else
7065
    {
7066
        /* generate the call to 'self' */
7067
        G_cg->write_op(OPC_GETPROPSELF);
7068
        G_cs->write_prop_id(prop_);
7069
    }
7070
7071
    /* if they're not discarding the value, push the result */
7072
    if (!discard)
7073
    {
7074
        G_cg->write_op(OPC_GETR0);
7075
        G_cg->note_push();
7076
    }
7077
}
7078
7079
/*
7080
 *   evaluate a member expression 
7081
 */
7082
void CTcSymProp::gen_code_member(int discard,
7083
                                 CTcPrsNode *prop_expr, int prop_is_expr,
7084
                                 int argc, int varargs)
7085
{
7086
    /* generate code to evaluate the property */
7087
    gen_code(FALSE);
7088
7089
    /* if we have an argument counter, put it back on top */
7090
    if (varargs)
7091
        G_cg->write_op(OPC_SWAP);
7092
7093
    /* use the standard member generation */
7094
    CTcPrsNode::s_gen_member_rhs(discard, prop_expr, prop_is_expr,
7095
                                 argc, varargs);
7096
}
7097
7098
/*
7099
 *   take the address of the property
7100
 */
7101
void CTcSymProp::gen_code_addr()
7102
{
7103
    /* write code to push the property ID */
7104
    G_cg->write_op(OPC_PUSHPROPID);
7105
    G_cs->write_prop_id(prop_);
7106
7107
    /* note the push */
7108
    G_cg->note_push();
7109
}
7110
7111
/*
7112
 *   assign to a property, implicitly of the "self" object 
7113
 */
7114
int CTcSymProp::gen_code_asi(int discard, tc_asitype_t typ,
7115
                             class CTcPrsNode *rhs, int /*ignore_errors*/)
7116
{
7117
    /* if there's no "self" object, we can't make this assignment */
7118
    if (!G_cs->is_self_available())
7119
    {
7120
        /* log an error */
7121
        G_tok->log_error(TCERR_SETPROP_NEEDS_OBJ,
7122
                         (int)get_sym_len(), get_sym());
7123
7124
        /* 
7125
         *   indicate that we're finished, since there's nothing more we
7126
         *   can do here 
7127
         */
7128
        return TRUE;
7129
    }
7130
7131
    /* 
7132
     *   if it's not a simple assignment, tell the caller to do the
7133
     *   composite work and get back to us with the value to store 
7134
     */
7135
    if (typ != TC_ASI_SIMPLE)
7136
        return FALSE;
7137
7138
    /* 
7139
     *   generate the right-hand side's expression for assignment, unless
7140
     *   the caller has already done so 
7141
     */
7142
    if (rhs != 0)
7143
        rhs->gen_code(FALSE, FALSE);
7144
7145
    /* 
7146
     *   if we're not discarding the value, make a copy - we'll consume a
7147
     *   copy in the SETPROP instruction, so we need one more copy to
7148
     *   return to the enclosing expression 
7149
     */
7150
    if (!discard)
7151
    {
7152
        G_cg->write_op(OPC_DUP);
7153
        G_cg->note_push();
7154
    }
7155
7156
    /* 
7157
     *   write the SETPROP instruction - use the special form to assign to
7158
     *   "self" 
7159
     */
7160
    G_cg->write_op(OPC_SETPROPSELF);
7161
    G_cs->write_prop_id(prop_);
7162
7163
    /* setpropself removes the value */
7164
    G_cg->note_pop();
7165
7166
    /* handled */
7167
    return TRUE;
7168
}
7169
7170
/*
7171
 *   call the symbol 
7172
 */
7173
void CTcSymProp::gen_code_call(int discard, int argc, int varargs)
7174
{
7175
    /* 
7176
     *   if there's no "self", we can't invoke a property without an
7177
     *   explicit object reference 
7178
     */
7179
    if (!G_cs->is_self_available())
7180
    {
7181
        G_tok->log_error(TCERR_PROP_NEEDS_OBJ, (int)get_sym_len(), get_sym());
7182
        return;
7183
    }
7184
7185
    /* don't allow calling with arguments in speculative mode */
7186
    if (argc != 0 && G_cg->is_speculative())
7187
        err_throw(VMERR_BAD_SPEC_EVAL);
7188
7189
    /* generate code to invoke the property of "self" */
7190
    if (G_cg->is_speculative())
7191
    {
7192
        /* push 'self', then get the property in data-only mode */
7193
        G_cg->write_op(OPC_PUSHSELF);
7194
        G_cg->write_op(OPC_GETPROPDATA);
7195
        G_cs->write_prop_id(get_prop());
7196
7197
        /* we pushed 'self' then popped it again */
7198
        G_cg->note_push();
7199
        G_cg->note_pop();
7200
    }
7201
    else if (argc == 0)
7202
    {
7203
        /* use the instruction with no arguments */
7204
        G_cg->write_op(OPC_GETPROPSELF);
7205
        G_cs->write_prop_id(get_prop());
7206
    }
7207
    else
7208
    {
7209
        /* write the varargs modifier if appropriate */
7210
        if (varargs)
7211
            G_cg->write_op(OPC_VARARGC);
7212
        
7213
        /* use the instruction with arguments */
7214
        G_cg->write_op(OPC_CALLPROPSELF);
7215
        G_cs->write((char)argc);
7216
        G_cs->write_prop_id(get_prop());
7217
7218
        /* callpropself removes arguments */
7219
        G_cg->note_pop(argc);
7220
    }
7221
7222
    /* if we're not discarding, push the return value from R0 */
7223
    if (!discard)
7224
    {
7225
        G_cg->write_op(OPC_GETR0);
7226
        G_cg->note_push();
7227
    }
7228
}
7229
7230
/*
7231
 *   generate a property ID expression 
7232
 */
7233
vm_prop_id_t CTcSymProp::gen_code_propid(int check_only, int is_expr)
7234
{
7235
    /*
7236
     *   If I'm to be treated as an expression (which indicates that the
7237
     *   property symbol is explicitly enclosed in parentheses in the
7238
     *   original source code expression), then I must evaluate this
7239
     *   property of self.  Otherwise, I yield literally the property ID. 
7240
     */
7241
    if (is_expr)
7242
    {
7243
        /* generate code unless we're only checking */
7244
        if (!check_only)
7245
        {
7246
            /* evaluate this property of self */
7247
            G_cg->write_op(OPC_GETPROPSELF);
7248
            G_cs->write_prop_id(get_prop());
7249
7250
            /* leave the result on the stack */
7251
            G_cg->write_op(OPC_GETR0);
7252
            G_cg->note_push();
7253
        }
7254
7255
        /* tell the caller to use the stack value */
7256
        return VM_INVALID_PROP;
7257
    }
7258
    else
7259
    {
7260
        /* simple '.prop' - return my property ID as a constant value */
7261
        return get_prop();
7262
    }
7263
}
7264
7265
/*
7266
 *   add a runtime symbol table entry 
7267
 */
7268
void CTcSymProp::add_runtime_symbol(CVmRuntimeSymbols *symtab)
7269
{
7270
    vm_val_t val;
7271
7272
    /* add our entry */
7273
    val.set_propid(get_prop());
7274
    symtab->add_sym(get_sym(), get_sym_len(), &val);
7275
}
7276
7277
/* ------------------------------------------------------------------------ */
7278
/*
7279
 *   Enumerator symbol 
7280
 */
7281
7282
/*
7283
 *   evaluate the symbol 
7284
 */
7285
void CTcSymEnum::gen_code(int discard)
7286
{
7287
    if (!discard)
7288
    {
7289
        /* generate code to push the enum value */
7290
        G_cg->write_op(OPC_PUSHENUM);
7291
        G_cs->write_enum_id(get_enum_id());
7292
7293
        /* note the push */
7294
        G_cg->note_push();
7295
    }
7296
}
7297
7298
/*
7299
 *   add a runtime symbol table entry 
7300
 */
7301
void CTcSymEnum::add_runtime_symbol(CVmRuntimeSymbols *symtab)
7302
{
7303
    vm_val_t val;
7304
7305
    /* add our entry */
7306
    val.set_enum(get_enum_id());
7307
    symtab->add_sym(get_sym(), get_sym_len(), &val);
7308
}
7309
7310
7311
/* ------------------------------------------------------------------------ */
7312
/*
7313
 *   T3-specific local variable/parameter symbol class 
7314
 */
7315
7316
/*
7317
 *   generate code to evaluate the symbol 
7318
 */
7319
void CTcSymLocal::gen_code(int discard)
7320
{
7321
    /* generate code to push the local, if we're not discarding it */
7322
    if (!discard)
7323
    {
7324
        /* 
7325
         *   generate as a context local if required, otherwise as an
7326
         *   ordinary local variable 
7327
         */
7328
        if (is_ctx_local_)
7329
        {
7330
            /* generate the context array lookup */
7331
            if (ctx_var_num_ <= 255 && get_ctx_arr_idx() <= 255)
7332
            {
7333
                /* we can do this whole operation with one instruction */
7334
                G_cg->write_op(OPC_IDXLCL1INT8);
7335
                G_cs->write((uchar)ctx_var_num_);
7336
                G_cs->write((uchar)get_ctx_arr_idx());
7337
7338
                /* this pushes one value */
7339
                G_cg->note_push();
7340
            }
7341
            else
7342
            {
7343
                /* get our context array */
7344
                s_gen_code_getlcl(ctx_var_num_, FALSE);
7345
                
7346
                /* get our value from the context array */
7347
                CTPNConst::s_gen_code_int(get_ctx_arr_idx());
7348
                G_cg->write_op(OPC_INDEX);
7349
                
7350
                /* the INDEX operation removes two values and pushes one */
7351
                G_cg->note_pop();
7352
            }
7353
        }
7354
        else
7355
        {
7356
            /* generate as an ordinary local */
7357
            s_gen_code_getlcl(get_var_num(), is_param());
7358
        }
7359
    }
7360
7361
    /* 
7362
     *   Mark the value as referenced, whether or not we're generating the
7363
     *   code - the value has been logically referenced in the program
7364
     *   even if the result of evaluating it isn't needed.  
7365
     */
7366
    set_val_used(TRUE);
7367
}
7368
7369
/*
7370
 *   generate code to push a local onto the stack 
7371
 */
7372
void CTcSymLocal::s_gen_code_getlcl(int var_num, int is_param)
7373
{
7374
    /* use the shortest form of the instruction that we can */
7375
    if (var_num <= 255)
7376
    {
7377
        /* 8-bit local number - use the one-byte form */
7378
        G_cg->write_op(is_param ? OPC_GETARG1 : OPC_GETLCL1);
7379
        G_cs->write((char)var_num);
7380
    }
7381
    else
7382
    {
7383
        /* local number won't fit in 8 bits - use the two-byte form */
7384
        G_cg->write_op(is_param ? OPC_GETARG2 : OPC_GETLCL2);
7385
        G_cs->write2(var_num);
7386
    }
7387
7388
    /* note the push */
7389
    G_cg->note_push();
7390
}
7391
7392
/*
7393
 *   assign a value 
7394
 */
7395
int CTcSymLocal::gen_code_asi(int discard, tc_asitype_t typ,
7396
                              class CTcPrsNode *rhs, int ignore_errors)
7397
{
7398
    int adding;
7399
    
7400
    /* mark the variable as having had a value assigned to it */
7401
    set_val_assigned(TRUE);
7402
7403
    /* 
7404
     *   if the assignment is anything but simple, this references the
7405
     *   value as well 
7406
     */
7407
    if (typ != TC_ASI_SIMPLE)
7408
        set_val_used(TRUE);
7409
7410
    /* 
7411
     *   If this is a context variable, use standard assignment (i.e.,
7412
     *   generate the result first, then generate a simple assignment to the
7413
     *   variable).  Otherwise, we might be able to generate a fancy
7414
     *   combined calculate-and-assign sequence, depending on the type of
7415
     *   assignment calculation we're performing.
7416
     */
7417
    if (is_ctx_local_ && typ != TC_ASI_SIMPLE)
7418
    {
7419
        /* 
7420
         *   it's a context local and it's not a simple assignment, so we
7421
         *   can't perform any special calculate-and-assign sequence - tell
7422
         *   the caller to calculate the full result first and then try
7423
         *   again using simple assignment 
7424
         */
7425
        return FALSE;
7426
    }
7427
7428
    /* 
7429
     *   check the type of assignment - we can optimize the code
7430
     *   generation to use more compact instruction sequences for certain
7431
     *   types of assignments 
7432
     */
7433
    switch(typ)
7434
    {
7435
    case TC_ASI_SIMPLE:
7436
        /* 
7437
         *   Simple assignment to local/parameter.  Check for some special
7438
         *   cases: when assigning a constant value of 0, 1, or nil to a
7439
         *   local, we can generate a short instruction 
7440
         */
7441
        if (!is_param() && !is_ctx_local_ && rhs != 0 && rhs->is_const())
7442
        {
7443
            CTcConstVal *cval;
7444
7445
            /* get the constant value */
7446
            cval = rhs->get_const_val();
7447
            
7448
            /* check for nil and 0 or 1 values */
7449
            if (cval->get_type() == TC_CVT_NIL)
7450
            {
7451
                /* it's nil - generate NILLCL1 or NILLCL2 */
7452
                if (get_var_num() <= 255)
7453
                {
7454
                    G_cg->write_op(OPC_NILLCL1);
7455
                    G_cs->write((char)get_var_num());
7456
                }
7457
                else
7458
                {
7459
                    G_cg->write_op(OPC_NILLCL2);
7460
                    G_cs->write2(get_var_num());
7461
                }
7462
7463
                /* if not discarding, leave nil on the stack */
7464
                if (!discard)
7465
                {
7466
                    G_cg->write_op(OPC_PUSHNIL);
7467
                    G_cg->note_push();
7468
                }
7469
7470
                /* handled */
7471
                return TRUE;
7472
            }
7473
            else if (cval->get_type() == TC_CVT_INT
7474
                     && (cval->get_val_int() == 0 
7475
                         || cval->get_val_int() == 1))
7476
            {
7477
                int ival;
7478
7479
                /* get the integer value */
7480
                ival = cval->get_val_int();
7481
                
7482
                /* 0 or 1 - generate ZEROLCLn or ONELCLn */
7483
                if (get_var_num() <= 255)
7484
                {
7485
                    G_cg->write_op(ival == 0 ? OPC_ZEROLCL1 : OPC_ONELCL1);
7486
                    G_cs->write((char)get_var_num());
7487
                }
7488
                else
7489
                {
7490
                    G_cg->write_op(ival == 0 ? OPC_ZEROLCL2 : OPC_ONELCL2);
7491
                    G_cs->write2(get_var_num());
7492
                }
7493
7494
                /* if not discarding, leave the value on the stack */
7495
                if (!discard)
7496
                {
7497
                    G_cg->write_op(ival == 0 ? OPC_PUSH_0 : OPC_PUSH_1);
7498
                    G_cg->note_push();
7499
                }
7500
7501
                /* handled */
7502
                return TRUE;
7503
            }
7504
        }
7505
7506
        /* 
7507
         *   If we got here, we can't generate a specialized constant
7508
         *   assignment - so, first, generate the right-hand side's value
7509
         *   normally.  (If no 'rhs' is specified, the value is already on
7510
         *   the stack.)  
7511
         */
7512
        if (rhs != 0)
7513
            rhs->gen_code(FALSE, FALSE);
7514
7515
        /* leave an extra copy of the value on the stack if not discarding */
7516
        if (!discard)
7517
        {
7518
            G_cg->write_op(OPC_DUP);
7519
            G_cg->note_push();
7520
        }
7521
7522
        /* now assign the value at top of stack to the variable */
7523
        gen_code_setlcl();
7524
7525
        /* handled */
7526
        return TRUE;
7527
7528
    case TC_ASI_ADD:
7529
        adding = TRUE;
7530
        goto add_or_sub;
7531
        
7532
    case TC_ASI_SUB:
7533
        adding = FALSE;
7534
7535
    add_or_sub:
7536
        /* if this is a parameter, there's nothing special we can do */
7537
        if (is_param())
7538
            return FALSE;
7539
        
7540
        /* 
7541
         *   Add/subtract to a local/parameter.  If the right-hand side is a
7542
         *   constant integer value, we might be able to generate a special
7543
         *   instruction to add/subtract it.  
7544
         */
7545
        if (rhs != 0
7546
            && adding
7547
            && rhs->is_const()
7548
            && rhs->get_const_val()->get_type() == TC_CVT_INT)
7549
        {
7550
            long ival;
7551
7552
            /* get the integer value to assign */
7553
            ival = rhs->get_const_val()->get_val_int();
7554
7555
            /* 
7556
             *   if the right-hand side's integer value fits in one byte,
7557
             *   generate the short (8-bit) instruction; otherwise,
7558
             *   generate the long (32-bit) format 
7559
             */
7560
            if (ival == 1)
7561
            {
7562
                /* adding one - increment the local */
7563
                G_cg->write_op(OPC_INCLCL);
7564
                G_cs->write2(get_var_num());
7565
            }
7566
            else if (ival == -1)
7567
            {
7568
                /* subtracting one - decrement the local */
7569
                G_cg->write_op(OPC_DECLCL);
7570
                G_cs->write2(get_var_num());
7571
            }
7572
            else if (ival <= 127 && ival >= -128
7573
                     && get_var_num() <= 255)
7574
            {
7575
                /* fits in 8 bits - use the 8-bit format */
7576
                G_cg->write_op(OPC_ADDILCL1);
7577
                G_cs->write((char)get_var_num());
7578
                G_cs->write((char)ival);
7579
            }
7580
            else
7581
            {
7582
                /* 
7583
                 *   either the value or the variable number doesn't fit
7584
                 *   in 8 bits - use the 32-bit format 
7585
                 */
7586
                G_cg->write_op(OPC_ADDILCL4);
7587
                G_cs->write2(get_var_num());
7588
                G_cs->write4(ival);
7589
            }
7590
        }
7591
        else
7592
        {
7593
            /* 
7594
             *   We don't have a special instruction for the right side,
7595
             *   so generate it normally and add/subtract the value.  (If
7596
             *   there's no 'rhs' value specified, it means that the value
7597
             *   is already on the stack, so there's nothing extra for us
7598
             *   to generate.)  
7599
             */
7600
            if (rhs != 0)
7601
                rhs->gen_code(FALSE, FALSE);
7602
            
7603
            /* write the ADDTOLCL instruction */
7604
            G_cg->write_op(adding ? OPC_ADDTOLCL : OPC_SUBFROMLCL);
7605
            G_cs->write2(get_var_num());
7606
7607
            /* addtolcl/subfromlcl remove the rvalue */
7608
            G_cg->note_pop();
7609
        }
7610
7611
        /* 
7612
         *   if not discarding, push the result onto the stack; do this by
7613
         *   simply evaluating the local, which is the simplest and most
7614
         *   efficient way to obtain the result of the computation 
7615
         */
7616
        if (!discard)
7617
            gen_code(FALSE);
7618
7619
        /* handled */
7620
        return TRUE;
7621
7622
    case TC_ASI_PREINC:
7623
        /* if this is a parameter, there's nothing special we can do */
7624
        if (is_param())
7625
            return FALSE;
7626
7627
        /* generate code to increment the local */
7628
        G_cg->write_op(OPC_INCLCL);
7629
        G_cs->write2(get_var_num());
7630
7631
        /* if we're not discarding, push the local's new value */
7632
        if (!discard)
7633
            gen_code(FALSE);
7634
7635
        /* handled */
7636
        return TRUE;
7637
7638
    case TC_ASI_POSTINC:
7639
        /* if this is a parameter, there's nothing special we can do */
7640
        if (is_param())
7641
            return FALSE;
7642
7643
        /* 
7644
         *   if we're not discarding, push the local's value prior to
7645
         *   incrementing it - this will be the result we'll leave on the
7646
         *   stack 
7647
         */
7648
        if (!discard)
7649
            gen_code(FALSE);
7650
7651
        /* generate code to increment the local */
7652
        G_cg->write_op(OPC_INCLCL);
7653
        G_cs->write2(get_var_num());
7654
7655
        /* handled */
7656
        return TRUE;
7657
7658
    case TC_ASI_PREDEC:
7659
        /* if this is a parameter, there's nothing special we can do */
7660
        if (is_param())
7661
            return FALSE;
7662
7663
        /* generate code to decrement the local */
7664
        G_cg->write_op(OPC_DECLCL);
7665
        G_cs->write2(get_var_num());
7666
7667
        /* if we're not discarding, push the local's new value */
7668
        if (!discard)
7669
            gen_code(FALSE);
7670
7671
        /* handled */
7672
        return TRUE;
7673
7674
    case TC_ASI_POSTDEC:
7675
        /* if this is a parameter, there's nothing special we can do */
7676
        if (is_param())
7677
            return FALSE;
7678
7679
        /* 
7680
         *   if we're not discarding, push the local's value prior to
7681
         *   decrementing it - this will be the result we'll leave on the
7682
         *   stack 
7683
         */
7684
        if (!discard)
7685
            gen_code(FALSE);
7686
7687
        /* generate code to decrement the local */
7688
        G_cg->write_op(OPC_DECLCL);
7689
        G_cs->write2(get_var_num());
7690
7691
        /* handled */
7692
        return TRUE;
7693
7694
    default:
7695
        /* we can't do anything special with other assignment types */
7696
        return FALSE;
7697
    }
7698
}
7699
7700
/*
7701
 *   generate code to assigin the value at top of stack to the local
7702
 *   variable 
7703
 */
7704
void CTcSymLocal::gen_code_setlcl()
7705
{
7706
    /* check to see if we're a context local (as opposed to a stack local) */
7707
    if (is_ctx_local_)
7708
    {
7709
        /* generate the assignment using the appropriate sequence */
7710
        if (ctx_var_num_ <= 255 && get_ctx_arr_idx() <= 255)
7711
        {
7712
            /* we can fit this in a single instruction */
7713
            G_cg->write_op(OPC_SETINDLCL1I8);
7714
            G_cs->write((uchar)ctx_var_num_);
7715
            G_cs->write((uchar)get_ctx_arr_idx());
7716
7717
            /* this pops the value being assigned */
7718
            G_cg->note_pop();
7719
        }
7720
        else
7721
        {
7722
            /* get our context array */
7723
            s_gen_code_getlcl(ctx_var_num_, FALSE);
7724
            
7725
            /* set our value in the context array */
7726
            CTPNConst::s_gen_code_int(get_ctx_arr_idx());
7727
            G_cg->write_op(OPC_SETIND);
7728
            G_cg->write_op(OPC_DISC);
7729
            
7730
            /* 
7731
             *   the SETIND pops three values and pushes one (for a net two
7732
             *   pops), and the DISC pops one more value, so our total is
7733
             *   three pops 
7734
             */
7735
            G_cg->note_pop(3);
7736
        }
7737
    }
7738
    else
7739
    {
7740
        /* we're just a plain stack variable */
7741
        gen_code_setlcl_stk();
7742
    }
7743
}
7744
7745
/*
7746
 *   Generate code to store the value at the top of the stack into the given
7747
 *   local stack slot.  Note that this routine will not work with a context
7748
 *   local - it only works if the variable is known to be a stack variable.  
7749
 */
7750
void CTcSymLocal::s_gen_code_setlcl_stk(int var_num, int is_param)
7751
{
7752
    /* use the shortest form that will fit our variable index */
7753
    if (var_num <= 255)
7754
    {
7755
        /* use the one-byte instruction */
7756
        G_cg->write_op(is_param ? OPC_SETARG1 : OPC_SETLCL1);
7757
        G_cs->write((char)var_num);
7758
    }
7759
    else
7760
    {
7761
        /* big number - use the two-byte instruction */
7762
        G_cg->write_op(is_param ? OPC_SETARG2 : OPC_SETLCL2);
7763
        G_cs->write2(var_num);
7764
    }
7765
7766
    /* the setarg/setlcl ops remove the rvalue */
7767
    G_cg->note_pop();
7768
}
7769
7770
/*
7771
 *   call the symbol 
7772
 */
7773
void CTcSymLocal::gen_code_call(int discard, int argc, int varargs)
7774
{
7775
    /* 
7776
     *   to call a local, we'll simply evaluate the local normally, then
7777
     *   call through the resulting (presumed) property or function
7778
     *   pointer value 
7779
     */
7780
    gen_code(FALSE);
7781
7782
    /* 
7783
     *   if we have a varargs list, modify the call instruction that
7784
     *   follows to make it a varargs call 
7785
     */
7786
    if (varargs)
7787
    {
7788
        /* swap the top of the stack to get the arg counter back on top */
7789
        G_cg->write_op(OPC_SWAP);
7790
7791
        /* write the varargs modifier */
7792
        G_cg->write_op(OPC_VARARGC);
7793
    }
7794
7795
    /* don't allow this at all in speculative mode */
7796
    if (G_cg->is_speculative())
7797
        err_throw(VMERR_BAD_SPEC_EVAL);
7798
7799
    /* call the result as a function or method pointer */
7800
    G_cg->write_op(OPC_PTRCALL);
7801
    G_cs->write((char)argc);
7802
7803
    /* ptrcall removes the arguments and the function pointer */
7804
    G_cg->note_pop(argc + 1);
7805
7806
    /* if we're not discarding the value, push the result */
7807
    if (!discard)
7808
    {
7809
        G_cg->write_op(OPC_GETR0);
7810
        G_cg->note_push();
7811
    }
7812
}
7813
7814
/*
7815
 *   generate a property ID expression 
7816
 */
7817
vm_prop_id_t CTcSymLocal::gen_code_propid(int check_only, int /*is_expr*/)
7818
{
7819
    /*
7820
     *   treat the local as a property-valued expression; generate the
7821
     *   code for the local, then tell the caller that no constant value
7822
     *   is available, since the local's property ID value should be on
7823
     *   the stack 
7824
     */
7825
    if (!check_only)
7826
        gen_code(FALSE);
7827
7828
    /* tell the caller to use the stack value */
7829
    return VM_INVALID_PROP;
7830
}
7831
7832
/*
7833
 *   evaluate a member expression 
7834
 */
7835
void CTcSymLocal::gen_code_member(int discard,
7836
                                  CTcPrsNode *prop_expr, int prop_is_expr,
7837
                                  int argc, int varargs)
7838
{
7839
    /* generate code to evaluate the local */
7840
    gen_code(FALSE);
7841
7842
    /* if we have an argument counter, put it back on top */
7843
    if (varargs)
7844
        G_cg->write_op(OPC_SWAP);
7845
7846
    /* use the standard member generation */
7847
    CTcPrsNode::s_gen_member_rhs(discard, prop_expr, prop_is_expr,
7848
                                 argc, varargs);
7849
}
7850
7851
/*
7852
 *   write to a debug record 
7853
 */
7854
int CTcSymLocal::write_to_debug_frame()
7855
{
7856
    int flags;
7857
    
7858
    /* 
7859
     *   write my ID - if we're a context variable, we want to write the
7860
     *   context variable ID; otherwise write our stack location as normal 
7861
     */
7862
    if (is_ctx_local_)
7863
        G_cs->write2(ctx_var_num_);
7864
    else
7865
        G_cs->write2(var_num_);
7866
7867
    /* compute my flags */
7868
    flags = 0;
7869
    if (is_param_)
7870
        flags |= 1;
7871
    if (is_ctx_local_)
7872
        flags |= 2;
7873
7874
    /* write my flags */
7875
    G_cs->write2(flags);
7876
7877
    /* write my local context array index */
7878
    G_cs->write2(get_ctx_arr_idx());
7879
7880
    /* write the length of my symbol name */
7881
    G_cs->write2(len_);
7882
    G_cs->write(str_, len_);
7883
7884
    /* we did write this symbol */
7885
    return TRUE;
7886
}
7887
7888
/* ------------------------------------------------------------------------ */
7889
/*
7890
 *   Built-in function symbol
7891
 */
7892
7893
/*
7894
 *   Evaluate the symbol.  Invoking a built-in function without an
7895
 *   argument list is simply a call to the built-in function with no
7896
 *   arguments.  
7897
 */
7898
void CTcSymBif::gen_code(int discard)
7899
{
7900
    /* generate a call */
7901
    gen_code_call(discard, 0, FALSE);
7902
}
7903
7904
/*
7905
 *   Generate code to call the built-in function 
7906
 */
7907
void CTcSymBif::gen_code_call(int discard, int argc, int varargs)
7908
{
7909
    /* don't allow calling built-in functions in speculative mode */
7910
    if (G_cg->is_speculative())
7911
        err_throw(VMERR_BAD_SPEC_EVAL);
7912
    
7913
    /* check for minimum and maximum arguments */
7914
    if (argc < min_argc_)
7915
    {
7916
        G_tok->log_error(TCERR_TOO_FEW_FUNC_ARGS,
7917
                         (int)get_sym_len(), get_sym());
7918
    }
7919
    else if (!varargs_ && argc > max_argc_)
7920
    {
7921
        G_tok->log_error(TCERR_TOO_MANY_FUNC_ARGS,
7922
                         (int)get_sym_len(), get_sym());
7923
    }
7924
7925
    /* write the varargs modifier if appropriate */
7926
    if (varargs)
7927
        G_cg->write_op(OPC_VARARGC);
7928
7929
    /* generate the call */
7930
    if (get_func_set_id() < 4 && get_func_idx() < 256)
7931
    {
7932
        uchar short_ops[] =
7933
            { OPC_BUILTIN_A, OPC_BUILTIN_B, OPC_BUILTIN_C, OPC_BUILTIN_D };
7934
        
7935
        /* 
7936
         *   it's one of the first 256 functions in one of the first four
7937
         *   function sets - we can generate a short instruction 
7938
         */
7939
        G_cg->write_op(short_ops[get_func_set_id()]);
7940
        G_cs->write((char)argc);
7941
        G_cs->write((char)get_func_idx());
7942
    }
7943
    else
7944
    {
7945
        /* it's not in the default set - use the longer instruction */
7946
        if (get_func_idx() < 256)
7947
        {
7948
            /* low function index - write the short form */
7949
            G_cg->write_op(OPC_BUILTIN1);
7950
            G_cs->write((char)argc);
7951
            G_cs->write((char)get_func_idx());
7952
        }
7953
        else
7954
        {
7955
            /* big function index - write the long form */
7956
            G_cg->write_op(OPC_BUILTIN2);
7957
            G_cs->write((char)argc);
7958
            G_cs->write2(get_func_idx());
7959
        }
7960
7961
        /* write the function set ID */
7962
        G_cs->write((char)get_func_set_id());
7963
    }
7964
7965
    /* the built-in functions always remove arguments */
7966
    G_cg->note_pop(argc);
7967
7968
    /* 
7969
     *   if they're not discarding the value, push it - the value is
7970
     *   sitting in R0 after the call returns
7971
     */
7972
    if (!discard)
7973
    {
7974
        G_cg->write_op(OPC_GETR0);
7975
        G_cg->note_push();
7976
    }
7977
}
7978
7979
7980
/* ------------------------------------------------------------------------ */
7981
/*
7982
 *   External function symbol 
7983
 */
7984
7985
/*
7986
 *   evaluate the symbol 
7987
 */
7988
void CTcSymExtfn::gen_code(int /*discard*/)
7989
{
7990
    //$$$ to be implemented
7991
    assert(FALSE);
7992
}
7993
7994
/*
7995
 *   generate a call to the symbol
7996
 */
7997
void CTcSymExtfn::gen_code_call(int /*discard*/, int /*argc*/, int /*varargs*/)
7998
{
7999
    //$$$ to be implemented
8000
    assert(FALSE);
8001
}
8002
8003
8004
/* ------------------------------------------------------------------------ */
8005
/*
8006
 *   Code Label symbol 
8007
 */
8008
8009
/*
8010
 *   evaluate the symbol 
8011
 */
8012
void CTcSymLabel::gen_code(int discard)
8013
{
8014
    /* it's not legal to evaluate a code label; log an error */
8015
    G_tok->log_error(TCERR_CANNOT_EVAL_LABEL,
8016
                     (int)get_sym_len(), get_sym());
8017
}
8018
8019
/* ------------------------------------------------------------------------ */
8020
/*
8021
 *   Metaclass symbol 
8022
 */
8023
8024
/*
8025
 *   generate code for evaluating the symbol 
8026
 */
8027
void CTcSymMetaclass::gen_code(int discard)
8028
{
8029
    /* 
8030
     *   the metaclass name refers to the IntrinsicClass instance
8031
     *   associated with the metaclass 
8032
     */
8033
    G_cg->write_op(OPC_PUSHOBJ);
8034
    G_cs->write_obj_id(class_obj_);
8035
8036
    /* note the push */
8037
    G_cg->note_push();
8038
}
8039
8040
/*
8041
 *   generate code for operator 'new' applied to the metaclass 
8042
 */
8043
void CTcSymMetaclass::gen_code_new(int discard, int argc, int varargs,
8044
                                   int is_transient)
8045
{
8046
    /* if we have varargs, write the modifier */
8047
    if (varargs)
8048
        G_cg->write_op(OPC_VARARGC);
8049
    
8050
    if (meta_idx_ <= 255 && argc <= 255)
8051
    {
8052
        G_cg->write_op(is_transient ? OPC_TRNEW1 : OPC_NEW1);
8053
        G_cs->write((char)argc);
8054
        G_cs->write((char)meta_idx_);
8055
    }
8056
    else
8057
    {
8058
        G_cg->write_op(is_transient ? OPC_TRNEW1 : OPC_NEW2);
8059
        G_cs->write2(argc);
8060
        G_cs->write2(meta_idx_);
8061
    }
8062
8063
    /* new1/new2 remove arguments */
8064
    G_cg->note_pop(argc);
8065
8066
    /* if we're not discarding the value, push it */
8067
    if (!discard)
8068
    {
8069
        G_cg->write_op(OPC_GETR0);
8070
        G_cg->note_push();
8071
    }
8072
}
8073
8074
/* 
8075
 *   generate a member expression 
8076
 */
8077
void CTcSymMetaclass::gen_code_member(int discard, CTcPrsNode *prop_expr,
8078
                                      int prop_is_expr,
8079
                                      int argc, int varargs)
8080
{
8081
    /* generate code to push our class object onto the stack */
8082
    gen_code(FALSE);
8083
8084
    /* if we have an argument counter, put it back on top */
8085
    if (varargs)
8086
        G_cg->write_op(OPC_SWAP);
8087
8088
    /* use the standard member generation */
8089
    CTcPrsNode::s_gen_member_rhs(discard, prop_expr, prop_is_expr,
8090
                                 argc, varargs);
8091
}
8092
8093
/*
8094
 *   add a runtime symbol table entry 
8095
 */
8096
void CTcSymMetaclass::add_runtime_symbol(CVmRuntimeSymbols *symtab)
8097
{
8098
    vm_val_t val;
8099
8100
    /* add our entry */
8101
    val.set_obj(get_class_obj());
8102
    symtab->add_sym(get_sym(), get_sym_len(), &val);
8103
}
8104
8105
8106
/* ------------------------------------------------------------------------ */
8107
/*
8108
 *   Exception Table 
8109
 */
8110
8111
/*
8112
 *   create 
8113
 */
8114
CTcT3ExcTable::CTcT3ExcTable()
8115
{
8116
    /* allocate an initial table */
8117
    exc_alloced_ = 1024;
8118
    table_ = (CTcT3ExcEntry *)t3malloc(exc_alloced_ * sizeof(table_[0]));
8119
8120
    /* no entries are in use yet */
8121
    exc_used_ = 0;
8122
8123
    /* method offset is not yet known */
8124
    method_ofs_ = 0;
8125
}
8126
8127
8128
/*
8129
 *   add an entry to our table 
8130
 */
8131
void CTcT3ExcTable::add_catch(ulong protected_start_ofs,
8132
                              ulong protected_end_ofs,
8133
                              ulong exc_obj_id, ulong catch_block_ofs)
8134
{
8135
    CTcT3ExcEntry *entry;
8136
8137
    /* if necessary, expand our table */
8138
    if (exc_used_ == exc_alloced_)
8139
    {
8140
        /* expand the table a bit */
8141
        exc_alloced_ += 1024;
8142
8143
        /* reallocate the table at the larger size */
8144
        table_ = (CTcT3ExcEntry *)
8145
                 t3realloc(table_, exc_alloced_ * sizeof(table_[0]));
8146
    }
8147
8148
    /* 
8149
     *   set up the new entry - store the offsets relative to the method
8150
     *   header start address 
8151
     */
8152
    entry = table_ + exc_used_;
8153
    entry->start_ofs = protected_start_ofs - method_ofs_;
8154
    entry->end_ofs = protected_end_ofs - method_ofs_;
8155
    entry->exc_obj_id = exc_obj_id;
8156
    entry->catch_ofs = catch_block_ofs - method_ofs_;
8157
8158
    /* consume the new entry */
8159
    ++exc_used_;
8160
}
8161
8162
/*
8163
 *   write our exception table to the code stream 
8164
 */
8165
void CTcT3ExcTable::write_to_code_stream()
8166
{
8167
    CTcT3ExcEntry *entry;
8168
    size_t i;
8169
8170
    /* write the number of entries as a UINT2 */
8171
    G_cs->write2(exc_used_);
8172
8173
    /* write the entries */
8174
    for (i = 0, entry = table_ ; i < exc_used_ ; ++i, ++entry)
8175
    {
8176
        /* write this entry */
8177
        G_cs->write2(entry->start_ofs);
8178
        G_cs->write2(entry->end_ofs);
8179
        G_cs->write_obj_id(entry->exc_obj_id);
8180
        G_cs->write2(entry->catch_ofs);
8181
    }
8182
}
8183
8184
8185
/* ------------------------------------------------------------------------ */
8186
/*
8187
 *   Code body 
8188
 */
8189
8190
/*
8191
 *   generate code 
8192
 */
8193
void CTPNCodeBody::gen_code(int, int)
8194
{
8195
    CTcCodeBodyCtx *cur_ctx;
8196
    int ctx_idx;
8197
    tct3_method_gen_ctx gen_ctx;
8198
8199
    /* if I've been replaced, don't bother generating any code */
8200
    if (replaced_)
8201
        return;
8202
8203
    /* 
8204
     *   Open the method header.
8205
     *   
8206
     *   Generate to the static stream if this is a static initializer
8207
     *   method, otherwise to the main stream. 
8208
     *   
8209
     *   Anchor the fixups in the associated symbol table entry, if any.  We
8210
     *   maintain our own fixup list if we don't have a symbol, otherwise we
8211
     *   use the one from our symbol table entry - in either case, we have to
8212
     *   keep track of it ourselves, because a code body might be reachable
8213
     *   through multiple references (a function, for example, has a global
8214
     *   symbol table entry - fixups referencing us might already have been
8215
     *   created by the time we generate our code).  
8216
     */
8217
    G_cg->open_method(is_static_ ? G_cs_static : G_cs_main,
8218
                      fixup_owner_sym_, fixup_list_anchor_,
8219
                      this, gototab_,
8220
                      argc_, varargs_, is_constructor_, self_valid_,
8221
                      &gen_ctx);
8222
8223
    /* 
8224
     *   Add each local symbol table enclosing the code body's primary
8225
     *   local symbol table to the frame list.  The outermost code body
8226
     *   table can be outside the primary code body table for situations
8227
     *   such as anonymous functions.  Since these tables are outside of
8228
     *   any statements, we must explicitly add them to ensure that they
8229
     *   are assigned debugging frame ID's and are written to the debug
8230
     *   data.
8231
     */
8232
    if (lcltab_ != 0)
8233
    {
8234
        CTcPrsSymtab *tab;
8235
8236
        /* add each frame outside the primary frame to the code gen list */
8237
        for (tab = lcltab_->get_parent() ; tab != 0 ; tab = tab->get_parent())
8238
            G_cs->set_local_frame(tab);
8239
    }
8240
8241
    /* the method's local symbol table is now the active symbol table */
8242
    G_cs->set_local_frame(lcltab_);
8243
8244
    /* if we have a local context, initialize it */
8245
    if (has_local_ctx_)
8246
    {
8247
        /* write code to create the new Vector to store the context locals */
8248
        CTPNConst::s_gen_code_int(local_ctx_arr_size_);
8249
        G_cg->write_op(OPC_DUP);
8250
        G_cg->write_op(OPC_NEW1);
8251
        G_cs->write(2);
8252
        G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_VECTOR));
8253
8254
        /* retrieve the object value */
8255
        G_cg->write_op(OPC_GETR0);
8256
8257
        /*
8258
         *   we duplicated the vector size argument, then we popped it and
8259
         *   pushed the object; so we have a maximum of one extra push and a
8260
         *   net of zero 
8261
         */
8262
        G_cg->note_push();
8263
        G_cg->note_pop();
8264
8265
        /* store the new object in the context local variable */
8266
        CTcSymLocal::s_gen_code_setlcl_stk(local_ctx_var_, FALSE);
8267
8268
        /* 
8269
         *   go through our symbol table, and copy each parameter that's
8270
         *   also a context local into its context local slot 
8271
         */
8272
        if (lcltab_ != 0)
8273
            lcltab_->enum_entries(&enum_for_param_ctx, this);
8274
    }
8275
8276
    /* 
8277
     *   If we have a varargs-list parameter, generate the code to set up
8278
     *   the list value from the actual parameters.  Note that we must do
8279
     *   this after we set up the local context, in case the varargs list
8280
     *   parameter variable is a context local, in which case it will need
8281
     *   to be stored in the context, in which case we need the context to
8282
     *   be initialized first.  
8283
     */
8284
    if (varargs_list_)
8285
    {
8286
        /* generate the PUSHPARLST instruction to create the list */
8287
        G_cg->write_op(OPC_PUSHPARLST);
8288
        G_cs->write((uchar)argc_);
8289
8290
        /* 
8291
         *   we pushed at least one value (the list); we don't know how many
8292
         *   others we might have pushed, but it doesn't matter because the
8293
         *   interpreter is responsible for checking for stack space 
8294
         */
8295
        G_cg->note_push();
8296
8297
        /* store the list in our varargs parameter list local */
8298
        varargs_list_local_->gen_code_setlcl();
8299
    }
8300
8301
    /* 
8302
     *   Generate code to initialize each enclosing-context-pointer local -
8303
     *   these variables allow us to find the context objects while we're
8304
     *   running inside this function.
8305
     *   
8306
     *   We *have to* generate context level 1 last.  Context level 1 does a
8307
     *   set-self to re-establish the method context (if there is one), and
8308
     *   once we've changed to the method context 'self', we can no longer
8309
     *   access the anonymous function pointer context, which is in 'self'
8310
     *   until we change it.  So, we have to wait and do level 1 last, so
8311
     *   that we're completely done with the anonymous function context
8312
     *   before we lose it.
8313
     *   
8314
     *   To ensure we generate level 1 last, make two passes: in the first
8315
     *   pass, generate everything except level 1; on the second pass,
8316
     *   generate only level 1.  This two-pass approach guarantees that level
8317
     *   1 will be the last one generated, regardless of where it appears in
8318
     *   the list.  (We can't just rearrange the list - not easily, at least
8319
     *   - because the list is in order of function object ('self') context
8320
     *   slot index.)  
8321
     */
8322
    for (int ctx_pass = 1 ; ctx_pass <= 2 ; ++ctx_pass)
8323
    {
8324
        /* loop over each context entry */
8325
        for (ctx_idx = 0, cur_ctx = ctx_head_ ; cur_ctx != 0 ;
8326
             cur_ctx = cur_ctx->nxt_, ++ctx_idx)
8327
        {
8328
            /* 
8329
             *   Context level 1 *must* be generated last.  If we're on pass
8330
             *   1 and this is level 1, skip it for now; if we're on pass 2,
8331
             *   skip everything *except* level 1. 
8332
             */
8333
            if ((ctx_pass == 1 && cur_ctx->level_ == 1)
8334
                || (ctx_pass == 2 && cur_ctx->level_ != 1))
8335
                continue;
8336
            
8337
            /* 
8338
             *   Get this context value, stored in the function object
8339
             *   ('self') at index value 2+n (n=0,1,...).  Note that the
8340
             *   context object indices start at 2 because the code pointer
8341
             *   for the function is at index 1.  
8342
             */
8343
            G_cg->write_op(OPC_PUSHSELF);
8344
            CTPNConst::s_gen_code_int(ctx_idx + 2);
8345
            G_cg->write_op(OPC_INDEX);
8346
8347
            /* 
8348
             *   we pushed the object, then popped the object and index and
8349
             *   pushed the indexed value - this is a net of no change with
8350
             *   one maximum push 
8351
             */
8352
            G_cg->note_push();
8353
            G_cg->note_pop();
8354
8355
            /*
8356
             *   If this is context level 1, and this context has a 'self',
8357
             *   and we need either 'self' or the full method context from
8358
             *   the lexically enclosing scope, generate code to load the
8359
             *   self or the full method context (as appropriate) from our
8360
             *   local context.
8361
             *   
8362
             *   The enclosing method context is always stored in the context
8363
             *   at level 1, because this is inherently shared context for
8364
             *   all enclosed lexical scopes.  We thus only have to worry
8365
             *   about this for context level 1.  
8366
             */
8367
            if (cur_ctx->level_ == 1
8368
                && self_valid_
8369
                && (self_referenced_ || full_method_ctx_referenced_))
8370
            {
8371
                CTPNCodeBody *outer;
8372
                
8373
                /* 
8374
                 *   we just put our context object on the stack in
8375
                 *   preparation for storing it - make a duplicate copy of it
8376
                 *   for our own purposes 
8377
                 */
8378
                G_cg->write_op(OPC_DUP);
8379
                G_cg->note_push();
8380
                
8381
                /* get the saved method context from the context object */
8382
                CTPNConst::s_gen_code_int(TCPRS_LOCAL_CTX_METHODCTX);
8383
                G_cg->write_op(OPC_INDEX);
8384
                
8385
                /* 
8386
                 *   Load the context.  We must check the outermost context
8387
                 *   to determine what it stored, because we must load
8388
                 *   whatever it stored.  
8389
                 */
8390
                if ((outer = get_outermost_enclosing()) != 0
8391
                    && outer->local_ctx_needs_full_method_ctx())
8392
                {
8393
                    /* load the full method context */
8394
                    G_cg->write_op(OPC_LOADCTX);
8395
                }
8396
                else
8397
                {
8398
                    /* load the 'self' object */
8399
                    G_cg->write_op(OPC_SETSELF);
8400
                }
8401
                
8402
                /* 
8403
                 *   we popped two values and pushed one in the INDEX, then
8404
                 *   popped a value in the LOADCTX or SETSELF: the net is
8405
                 *   removal of two elements and no additional maximum depth 
8406
                 */
8407
                G_cg->note_pop(2);
8408
            }
8409
8410
            /* store the context value in the appropriate local variable */
8411
            CTcSymLocal::s_gen_code_setlcl_stk(cur_ctx->var_num_, FALSE);
8412
8413
            /* 
8414
             *   if we just did context level 1, and this is pass 2, we're
8415
             *   done - pass 2's only function is to do level 1, so once we
8416
             *   reach it, there's nothing left to do 
8417
             */
8418
            if (ctx_pass == 2 && cur_ctx->level_ == 1)
8419
                break;
8420
        }
8421
    }
8422
8423
    /* 
8424
     *   if we created our own local context, and we have a 'self' object,
8425
     *   and we need access to the 'self' object or the full method context
8426
     *   from anonymous functions that refer to the local context, generate
8427
     *   code to store the appropriate data in the local context 
8428
     */
8429
    if (has_local_ctx_ && self_valid_
8430
        && (local_ctx_needs_self_ || local_ctx_needs_full_method_ctx_))
8431
    {
8432
        /* check to see what we need */
8433
        if (local_ctx_needs_full_method_ctx_)
8434
        {
8435
            /* 
8436
             *   we need the full method context - generate code to store it
8437
             *   and push a reference to it onto the stack 
8438
             */
8439
            G_cg->write_op(OPC_STORECTX);
8440
        }
8441
        else
8442
        {
8443
            /* we only need 'self' - push it */
8444
            G_cg->write_op(OPC_PUSHSELF);
8445
        }
8446
8447
        /* we just pushed one value */
8448
        G_cg->note_push();
8449
8450
        /* assign the value to the context variable */
8451
        if (local_ctx_var_ <= 255 && TCPRS_LOCAL_CTX_METHODCTX <= 255)
8452
        {
8453
            /* we can make the assignment with a single instruction */
8454
            G_cg->write_op(OPC_SETINDLCL1I8);
8455
            G_cs->write((uchar)local_ctx_var_);
8456
            G_cs->write(TCPRS_LOCAL_CTX_METHODCTX);
8457
8458
            /* that pops one value */
8459
            G_cg->note_pop();
8460
        }
8461
        else
8462
        {
8463
            /* get the context object */
8464
            CTcSymLocal::s_gen_code_getlcl(local_ctx_var_, FALSE);
8465
            
8466
            /* store the data in the local context object */
8467
            CTPNConst::s_gen_code_int(TCPRS_LOCAL_CTX_METHODCTX);
8468
            G_cg->write_op(OPC_SETIND);
8469
8470
            /* discard the indexed result */
8471
            G_cg->write_op(OPC_DISC);
8472
        
8473
            /* 
8474
             *   the SETIND pops three values and pushes one, then we pop one
8475
             *   more with the DISC - this is a net three pops with no extra
8476
             *   maximum depth 
8477
             */
8478
            G_cg->note_pop(3);
8479
        }
8480
    }
8481
8482
    /* generate the compound statement, if we have one */
8483
    if (stm_ != 0)
8484
        stm_->gen_code(TRUE, TRUE);
8485
8486
#ifdef T3_DEBUG
8487
    if (G_cg->get_sp_depth() != 0)
8488
    {
8489
        printf("---> stack depth is %d after block codegen!\n",
8490
               G_cg->get_sp_depth());
8491
        if (fixup_owner_sym_ != 0)
8492
            printf("---> code block for %.*s\n",
8493
                   (int)fixup_owner_sym_->get_sym_len(),
8494
                   fixup_owner_sym_->get_sym());
8495
    }
8496
#endif
8497
8498
    /* close the method */
8499
    G_cg->close_method(local_cnt_, end_desc_, end_linenum_, &gen_ctx);
8500
8501
    /* remember the head of the nested symbol table list */
8502
    first_nested_symtab_ = G_cs->get_first_frame();
8503
8504
    /* generate debug records if appropriate */
8505
    if (G_debug)
8506
        build_debug_table(gen_ctx.method_ofs);
8507
8508
    /* check for unreferenced labels and issue warnings */
8509
    check_unreferenced_labels();
8510
8511
    /* show the disassembly of the code block if desired */
8512
    if (G_disasm_out != 0)
8513
        show_disassembly(gen_ctx.method_ofs,
8514
                         gen_ctx.code_start_ofs, gen_ctx.code_end_ofs);
8515
8516
    /* clean up globals for the end of the method */
8517
    G_cg->close_method_cleanup(&gen_ctx);
8518
}
8519
8520
/*
8521
 *   disassembly stream source implementation 
8522
 */
8523
class CTcUnasSrcCodeBody: public CTcUnasSrc
8524
{
8525
public:
8526
    CTcUnasSrcCodeBody(CTcCodeStream *str,
8527
                       unsigned long code_start_ofs,
8528
                       unsigned long code_end_ofs)
8529
    {
8530
        /* remember the stream */
8531
        str_ = str;
8532
8533
        /* start at the starting offset */
8534
        cur_ofs_ = code_start_ofs;
8535
8536
        /* remember the ending offset */
8537
        end_ofs_ = code_end_ofs;
8538
    }
8539
8540
    /* read the next byte */
8541
    int next_byte(char *ch)
8542
    {
8543
        /* if there's anything left, return it */
8544
        if (cur_ofs_ < end_ofs_)
8545
        {
8546
            /* return the next byte */
8547
            *ch = str_->get_byte_at(cur_ofs_);
8548
            ++cur_ofs_;
8549
            return 0;
8550
        }
8551
        else
8552
        {
8553
            /* indicate end of file */
8554
            return 1;
8555
        }
8556
    }
8557
8558
    /* get the current offset */
8559
    ulong get_ofs() const { return cur_ofs_; }
8560
8561
protected:
8562
    /* code stream */
8563
    CTcCodeStream *str_;
8564
8565
    /* current offset */
8566
    unsigned long cur_ofs_;
8567
8568
    /* offset of end of code stream */
8569
    unsigned long end_ofs_;
8570
};
8571
8572
/*
8573
 *   Show the disassembly of this code block 
8574
 */
8575
void CTPNCodeBody::show_disassembly(unsigned long start_ofs,
8576
                                    unsigned long code_start_ofs,
8577
                                    unsigned long code_end_ofs)
8578
{
8579
    int argc;
8580
    int locals;
8581
    int total_stk;
8582
    unsigned exc_rel;
8583
    unsigned dbg_rel;
8584
8585
    /* first, dump the header */
8586
    argc = (unsigned char)G_cs->get_byte_at(start_ofs);
8587
    locals = G_cs->readu2_at(start_ofs + 2);
8588
    total_stk = G_cs->readu2_at(start_ofs + 4);
8589
    exc_rel = G_cs->readu2_at(start_ofs + 6);
8590
    dbg_rel = G_cs->readu2_at(start_ofs + 8);
8591
    G_disasm_out->print("%8lx .code\n", start_ofs);
8592
    G_disasm_out->print("         .argcount %d%s\n",
8593
                        (argc & 0x7f),
8594
                        (argc & 0x80) != 0 ? "+" : "");
8595
    G_disasm_out->print("         .locals %d\n", locals);
8596
    G_disasm_out->print("         .maxstack %d\n", total_stk);
8597
8598
    /* set up a code stream reader and dump the code stream */
8599
    CTcUnasSrcCodeBody src(G_cs, code_start_ofs, code_end_ofs);
8600
    CTcT3Unasm::disasm(&src, G_disasm_out);
8601
8602
    /* show the exception table, if there is one */
8603
    if (exc_rel != 0)
8604
    {
8605
        unsigned long exc_ofs;
8606
        unsigned long exc_end_ofs;
8607
8608
        /* get the starting address */
8609
        exc_ofs = start_ofs + exc_rel;
8610
8611
        /* 
8612
         *   get the length - it's the part up to the debug records, or the
8613
         *   part up to the current code offset if there are no debug records
8614
         */
8615
        exc_end_ofs = (dbg_rel != 0 ? start_ofs + dbg_rel : G_cs->get_ofs());
8616
8617
        /* show the table */
8618
        G_disasm_out->print(".exceptions\n");
8619
        CTcUnasSrcCodeBody exc_src(G_cs, exc_ofs, exc_end_ofs);
8620
        CTcT3Unasm::show_exc_table(&exc_src, G_disasm_out, start_ofs);
8621
    }
8622
8623
    /* add a blank line at the end */
8624
    G_disasm_out->print("\n");
8625
}
8626
8627
/*
8628
 *   Check for unreferenced local variables 
8629
 */
8630
void CTPNCodeBody::check_locals()
8631
{
8632
    CTcPrsSymtab *tab;
8633
    
8634
    /* check for unreferenced locals in each nested scope */
8635
    for (tab = first_nested_symtab_ ; tab != 0 ; tab = tab->get_list_next())
8636
    {
8637
        /* check this table */
8638
        tab->check_unreferenced_locals();
8639
    }
8640
}
8641
8642
/* 
8643
 *   local symbol table enumerator for checking for parameter symbols that
8644
 *   belong in the local context 
8645
 */
8646
void CTPNCodeBody::enum_for_param_ctx(void *, class CTcSymbol *sym)
8647
{
8648
    /* if this is a local, check it further */
8649
    if (sym->get_type() == TC_SYM_LOCAL || sym->get_type() == TC_SYM_PARAM)
8650
    {
8651
        CTcSymLocal *lcl = (CTcSymLocal *)sym;
8652
8653
        /* 
8654
         *   if it's a parameter, and it's also a context variable, its
8655
         *   value needs to be moved into the context 
8656
         */
8657
        if (lcl->is_param() && lcl->is_ctx_local())
8658
        {
8659
            /* get the actual parameter value from the stack */
8660
            CTcSymLocal::s_gen_code_getlcl(lcl->get_var_num(), TRUE);
8661
8662
            /* store the value in the context variable */
8663
            lcl->gen_code_asi(TRUE, TC_ASI_SIMPLE, 0, TRUE);
8664
        }
8665
    }
8666
}
8667
8668
8669
/* ------------------------------------------------------------------------ */
8670
/*
8671
 *   'return' statement 
8672
 */
8673
8674
/*
8675
 *   generate code 
8676
 */
8677
void CTPNStmReturn::gen_code(int, int)
8678
{
8679
    int val_on_stack;
8680
    int need_gen;
8681
8682
    /* add a line record */
8683
    add_debug_line_rec();
8684
8685
    /* presume we'll generate a value */
8686
    need_gen = TRUE;
8687
    val_on_stack = FALSE;
8688
8689
    /* generate the return value expression, if appropriate */
8690
    if (expr_ != 0)
8691
    {
8692
        /* 
8693
         *   it's an error if we're in a constructor, because a
8694
         *   constructor implicitly always returns 'self' 
8695
         */
8696
        if (G_cg->is_in_constructor())
8697
            log_error(TCERR_CONSTRUCT_CANNOT_RET_VAL);
8698
8699
        /* check for a constant expression */
8700
        if (expr_->is_const())
8701
        {
8702
            switch(expr_->get_const_val()->get_type())
8703
            {
8704
            case TC_CVT_NIL:
8705
            case TC_CVT_TRUE:
8706
                /* 
8707
                 *   we can use special constant return instructions for
8708
                 *   these, so there's no need to generate the value 
8709
                 */
8710
                need_gen = FALSE;
8711
                break;
8712
8713
            default:
8714
                /* 
8715
                 *   other types don't have constant-return opcodes, so we
8716
                 *   must generate the expression code 
8717
                 */
8718
                need_gen = TRUE;
8719
                break;
8720
            }
8721
        }
8722
8723
        /* if necessary, generate the value */
8724
        if (need_gen)
8725
        {
8726
            int depth;
8727
8728
            /* note the initial stack depth */
8729
            depth = G_cg->get_sp_depth();
8730
8731
            /*  
8732
             *   Generate the value.  We are obviously not discarding the
8733
             *   value, and since returning a value is equivalent to
8734
             *   assigning the value, we must use the stricter assignment
8735
             *   (not 'for condition') rules for logical expressions 
8736
             */
8737
            expr_->gen_code(FALSE, FALSE);
8738
8739
            /* note whether we actually left a value on the stack */
8740
            val_on_stack = (G_cg->get_sp_depth() > depth);
8741
        }
8742
        else
8743
        {
8744
            /* 
8745
             *   we obviously aren't leaving a value on the stack if we
8746
             *   don't generate anything 
8747
             */
8748
            val_on_stack = FALSE;
8749
        }
8750
    }
8751
8752
    /* 
8753
     *   Before we return, let any enclosing statements generate any code
8754
     *   necessary to leave their scope (in particular, we must invoke
8755
     *   'finally' handlers in any enclosing 'try' blocks).
8756
     *   
8757
     *   Note that we generated the expression BEFORE we call any
8758
     *   'finally' handlers.  This is necessary because something we call
8759
     *   in the course of evaluating the return value could have thrown an
8760
     *   exception; if we were to call the 'finally' clauses before
8761
     *   generating the return value, we could invoke the 'finally' clause
8762
     *   twice (once explicitly, once in the handling of the thrown
8763
     *   exception), which would be incorrect.  By generating the
8764
     *   'finally' calls after the return expression, we're sure that the
8765
     *   'finally' blocks are invoked only once - either through the
8766
     *   throw, or else now, after there's no more possibility of a
8767
     *   'throw' before the return.  
8768
     */
8769
    if (G_cs->get_enclosing() != 0)
8770
    {
8771
        int did_save_retval;
8772
        uint fin_ret_lcl;
8773
8774
        /* 
8775
         *   if we're going to generate any subroutine calls, and we have
8776
         *   a return value on the stack, we need to save the return value
8777
         *   in a local to make sure the calculated value isn't affected
8778
         *   by the subroutine call 
8779
         */
8780
        if (val_on_stack
8781
            && G_cs->get_enclosing()->will_gen_code_unwind_for_return()
8782
            && G_cs->get_code_body() != 0)
8783
        {
8784
            /* allocate a local variable to save the return value */
8785
            fin_ret_lcl = G_cs->get_code_body()->alloc_fin_ret_lcl();
8786
8787
            /* save the return value in a stack temporary for a moment */
8788
            CTcSymLocal::s_gen_code_setlcl_stk(fin_ret_lcl, FALSE);
8789
8790
            /* 
8791
             *   note that we saved the return value, so we can retrieve
8792
             *   it later 
8793
             */
8794
            did_save_retval = TRUE;
8795
        }
8796
        else
8797
        {
8798
            /* note that we didn't save the return value */
8799
            did_save_retval = FALSE;
8800
        }
8801
8802
        /* generate the unwind */
8803
        G_cs->get_enclosing()->gen_code_unwind_for_return();
8804
8805
        /* if we saved the return value, retrieve it */
8806
        if (did_save_retval)
8807
            CTcSymLocal::s_gen_code_getlcl(fin_ret_lcl, FALSE);
8808
    }
8809
8810
    /* check for an expression to return */
8811
    if (G_cg->is_in_constructor())
8812
    {
8813
        /* we're in a constructor - return 'self' */
8814
        G_cg->write_op(OPC_PUSHSELF);
8815
        G_cg->write_op(OPC_RETVAL);
8816
    }
8817
    else if (expr_ == 0)
8818
    {
8819
        /* 
8820
         *   there's no expression - generate a simple void return (but
8821
         *   explicitly return nil, so we don't return something left in
8822
         *   R0 from a previous function call we made) 
8823
         */
8824
        G_cg->write_op(OPC_RETNIL);
8825
    }
8826
    else
8827
    {
8828
        /* check for a constant expression */
8829
        if (expr_->is_const())
8830
        {
8831
            switch(expr_->get_const_val()->get_type())
8832
            {
8833
            case TC_CVT_NIL:
8834
                /* generate a RETNIL instruction */
8835
                G_cg->write_op(OPC_RETNIL);
8836
                break;
8837
8838
            case TC_CVT_TRUE:
8839
                /* generate a RETTRUE instruction */
8840
                G_cg->write_op(OPC_RETTRUE);
8841
                break;
8842
8843
            default:
8844
                break;
8845
            }
8846
        }
8847
8848
        /* 
8849
         *   if we needed code generation to evaluate the return value, we
8850
         *   now need to return the value 
8851
         */
8852
        if (need_gen)
8853
        {
8854
            /* 
8855
             *   Other types don't have constant-return opcodes.  We
8856
             *   already generated the expression value (before invoking
8857
             *   the enclosing 'finally' handlers, if any), so the value
8858
             *   is on the stack, and all we need to do is return it.
8859
             *   
8860
             *   If we didn't actually leave a value on the stack, we'll
8861
             *   just return nil.  
8862
             */
8863
            if (val_on_stack)
8864
            {
8865
                /* generate the return-value opcode */
8866
                G_cg->write_op(OPC_RETVAL);
8867
                
8868
                /* RETVAL removes an element from the stack */
8869
                G_cg->note_pop();
8870
            }
8871
            else
8872
            {
8873
                /* 
8874
                 *   The depth didn't change - they must have evaluated an
8875
                 *   expression involving a dstring or void function.
8876
                 *   Return nil instead of the non-existent value.  
8877
                 */
8878
                G_cg->write_op(OPC_RETNIL);
8879
            }
8880
        }
8881
    }
8882
}
8883
    
8884
/* ------------------------------------------------------------------------ */
8885
/*
8886
 *   Static property initializer statement 
8887
 */
8888
void CTPNStmStaticPropInit::gen_code(int, int)
8889
{
8890
    int depth;
8891
    
8892
    /* add a line record */
8893
    add_debug_line_rec();
8894
8895
    /* note the initial stack depth */
8896
    depth = G_cg->get_sp_depth();
8897
8898
    /* generate the expression, keeping the generated value */
8899
    expr_->gen_code(FALSE, FALSE);
8900
8901
    /* ensure that we generated a value; if we didn't, push nil by default */
8902
    if (G_cg->get_sp_depth() <= depth)
8903
    {
8904
        /* push a default nil value */
8905
        G_cg->write_op(OPC_PUSHNIL);
8906
        G_cg->note_push();
8907
    }
8908
8909
    /* 
8910
     *   duplicate the value on the stack, so we can assign it to
8911
     *   initialize the property and also return it 
8912
     */
8913
    G_cg->write_op(OPC_DUP);
8914
    G_cg->note_push();
8915
8916
    /* write the SETPROPSELF to initialize the property */
8917
    G_cg->write_op(OPC_SETPROPSELF);
8918
    G_cs->write_prop_id(prop_);
8919
8920
    /* SETPROPSELF removes the value */
8921
    G_cg->note_pop();
8922
8923
    /* return the value (which we duplicated on the stack) */
8924
    G_cg->write_op(OPC_RETVAL);
8925
8926
    /* RETVAL removes the value */
8927
    G_cg->note_pop();
8928
}
8929
8930
8931
/* ------------------------------------------------------------------------ */
8932
/*
8933
 *   Object Definition Statement 
8934
 */
8935
8936
/*
8937
 *   generate code 
8938
 */
8939
void CTPNStmObject::gen_code(int, int)
8940
{
8941
    CTPNSuperclass *sc;
8942
    CTPNObjProp *prop;
8943
    int sc_cnt;
8944
    ulong start_ofs;
8945
    uint internal_flags;
8946
    uint obj_flags;
8947
    CTcDataStream *str;
8948
    int bad_sc;
8949
8950
    /* if this object has been replaced, don't generate any code for it */
8951
    if (replaced_)
8952
        return;
8953
8954
    /* add an implicit constructor if necessary */
8955
    add_implicit_constructor();
8956
8957
    /* get the appropriate stream for generating the data */
8958
    str = obj_sym_->get_stream();
8959
8960
    /* clear the internal flags */
8961
    internal_flags = 0;
8962
8963
    /* 
8964
     *   if we're a modified object, set the 'modified' flag in the object
8965
     *   header 
8966
     */
8967
    if (modified_)
8968
        internal_flags |= TCT3_OBJ_MODIFIED;
8969
8970
    /* set the 'transient' flag if appropriate */
8971
    if (transient_)
8972
        internal_flags |= TCT3_OBJ_TRANSIENT;
8973
8974
    /* clear the object flags */
8975
    obj_flags = 0;
8976
8977
    /* 
8978
     *   If we're specifically marked as a 'class' object, or we're a
8979
     *   modified object, set the 'class' flag in the object flags.  
8980
     */
8981
    if (is_class_ || modified_)
8982
        obj_flags |= TCT3_OBJFLG_CLASS;
8983
8984
    /* remember our starting offset in the object stream */
8985
    start_ofs = str->get_ofs();
8986
8987
    /* 
8988
     *   store our stream offset in our defining symbol, for storage in
8989
     *   the object file 
8990
     */
8991
    obj_sym_->set_stream_ofs(start_ofs);
8992
8993
    /* write our internal flags */
8994
    str->write2(internal_flags);
8995
8996
    /* 
8997
     *   First, write the per-object image file "OBJS" header - each
8998
     *   object starts with its object ID and the number of bytes in the
8999
     *   object's metaclass-specific data.  For now, write zero as a
9000
     *   placeholder for our data size.  Note that this is a
9001
     *   self-reference: it must be modified if the object is renumbered.  
9002
     */
9003
    str->write_obj_id_selfref(obj_sym_);
9004
    str->write2(0);
9005
9006
    /* write a placeholder for the superclass count */
9007
    str->write2(0);
9008
9009
    /* write the fixed property count */
9010
    str->write2(prop_cnt_);
9011
9012
    /* write the object flags */
9013
    str->write2(obj_flags);
9014
9015
    /*
9016
     *   First, go through the superclass list and verify that each
9017
     *   superclass is actually an object.  
9018
     */
9019
    for (bad_sc = FALSE, sc_cnt = 0, sc = first_sc_ ; sc != 0 ; sc = sc->nxt_)
9020
    {
9021
        CTcSymObj *sc_sym;
9022
9023
        /* look up the superclass in the global symbol table */
9024
        sc_sym = (CTcSymObj *)sc->get_sym();
9025
9026
        /* make sure it's defined, and that it's really an object */
9027
        if (sc_sym == 0)
9028
        {
9029
            /* not defined */
9030
            log_error(TCERR_UNDEF_SYM_SC,
9031
                      (int)sc->get_sym_len(), sc->get_sym_txt(),
9032
                      (int)obj_sym_->get_sym_len(), obj_sym_->get_sym());
9033
9034
            /* note that we have an invalid superclass */
9035
            bad_sc = TRUE;
9036
        }
9037
        else if (sc_sym->get_type() != TC_SYM_OBJ)
9038
        {
9039
            /* log an error */
9040
            log_error(TCERR_SC_NOT_OBJECT,
9041
                      (int)sc_sym->get_sym_len(), sc_sym->get_sym());
9042
9043
            /* note that we have an invalid superclass */
9044
            bad_sc = TRUE;
9045
        }
9046
        else
9047
        {
9048
            /* count the superclass */
9049
            ++sc_cnt;
9050
9051
            /* write the superclass to the object header */
9052
            str->write_obj_id(sc_sym->get_obj_id());
9053
        }
9054
    }
9055
9056
    /* 
9057
     *   If we detected a 'bad template' error when we were parsing the
9058
     *   object definition, and all of our superclasses are valid, report the
9059
     *   template error.
9060
     *   
9061
     *   Do not report this error if we have any undefined or invalid
9062
     *   superclasses, because (1) we've already reported one error for this
9063
     *   object definition (the bad superclass error), and (2) the missing
9064
     *   template is likely just a consequence of the bad superclass, since
9065
     *   we can't have scanned the proper superclass's list of templates if
9066
     *   they didn't tell us the correct superclass to start with.  When they
9067
     *   fix the superclass list and re-compile the code, it's likely that
9068
     *   this will fix the template problem as well, since we'll probably be
9069
     *   able to find the template give the corrected superclass list.
9070
     *   
9071
     *   If we found an undescribed class anywhere in our hierarchy, a
9072
     *   template simply cannot be used with this object; otherwise, the
9073
     *   error is that we failed to find a suitable template 
9074
     */
9075
    if (has_bad_template() && !bad_sc)
9076
        log_error(has_undesc_sc()
9077
                  ? TCERR_OBJ_DEF_CANNOT_USE_TEMPLATE
9078
                  : TCERR_OBJ_DEF_NO_TEMPLATE);
9079
9080
    /* go back and write the superclass count to the header */
9081
    str->write2_at(start_ofs + TCT3_TADSOBJ_HEADER_OFS, sc_cnt);
9082
9083
    /*
9084
     *   Write the properties.  We're required to write the properties in
9085
     *   sorted order of property ID, but we can't do that yet, because
9086
     *   the property ID's aren't finalized until after linking.  For now,
9087
     *   just write them out in the order in which they were defined.  
9088
     */
9089
    for (prop = first_prop_ ; prop != 0 ; prop = prop->nxt_)
9090
    {
9091
        /* make sure we have a valid property symbol */
9092
        if (prop->get_prop_sym() != 0)
9093
        {
9094
            /* write the property ID */
9095
            str->write_prop_id(prop->get_prop_sym()->get_prop());
9096
9097
            /* generate code for the property */
9098
            prop->gen_code(FALSE, FALSE);
9099
        }
9100
    }
9101
9102
    /* 
9103
     *   go back and write the size of our metaclass-specific data - this
9104
     *   goes at offset 4 in the T3 generic metaclass header
9105
     */
9106
    str->write2_at(start_ofs + TCT3_META_HEADER_OFS + 4,
9107
                   str->get_ofs() - (start_ofs + TCT3_META_HEADER_OFS + 6));
9108
}
9109
9110
/*
9111
 *   Check for unreferenced local variables 
9112
 */
9113
void CTPNStmObject::check_locals()
9114
{
9115
    CTPNObjProp *prop;
9116
9117
    /* check for unreferenced locals for each property */
9118
    for (prop = first_prop_ ; prop != 0 ; prop = prop->nxt_)
9119
        prop->check_locals();
9120
}