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

« back to all changes in this revision

Viewing changes to tads/tads3/tct3stm.cpp

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

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#ifdef RCSID
 
2
static char RCSid[] =
 
3
"$Header: d:/cvsroot/tads/tads3/TCT3STM.CPP,v 1.1 1999/07/11 00:46:57 MJRoberts Exp $";
 
4
#endif
 
5
 
 
6
/* 
 
7
 *   Copyright (c) 1999, 2002 Michael J. Roberts.  All Rights Reserved.
 
8
 *   
 
9
 *   Please see the accompanying license file, LICENSE.TXT, for information
 
10
 *   on using and copying this software.  
 
11
 */
 
12
/*
 
13
Name
 
14
  tct3stm.cpp - TADS 3 Compiler - T3 VM Code Generator - statement classes
 
15
Function
 
16
  Generate code for the T3 VM.  This file contains statement classes,
 
17
  in order to segregate the code generation classes required for the
 
18
  full compiler from those required for subsets that require only
 
19
  expression parsing (such as debuggers).
 
20
Notes
 
21
  
 
22
Modified
 
23
  05/08/99 MJRoberts  - Creation
 
24
*/
 
25
 
 
26
#include <stdio.h>
 
27
 
 
28
#include "t3std.h"
 
29
#include "os.h"
 
30
#include "tcprs.h"
 
31
#include "tct3.h"
 
32
#include "tcgen.h"
 
33
#include "vmtype.h"
 
34
#include "vmwrtimg.h"
 
35
#include "vmfile.h"
 
36
#include "tcmain.h"
 
37
#include "tcerr.h"
 
38
 
 
39
 
 
40
/* ------------------------------------------------------------------------ */
 
41
/*
 
42
 *   Code Body 
 
43
 */
 
44
 
 
45
/* callback context */
 
46
struct write_local_to_debug_frame_ctx
 
47
{
 
48
    /* number of symbols written so far */
 
49
    int count;
 
50
};
 
51
 
 
52
/*
 
53
 *   Callback for symbol table enumeration - write a local variable entry
 
54
 *   to the code stream for a debug frame record.  
 
55
 */
 
56
void CTPNCodeBody::write_local_to_debug_frame(void *ctx0, CTcSymbol *sym)
 
57
{
 
58
    write_local_to_debug_frame_ctx *ctx;
 
59
    
 
60
    /* cast our context */
 
61
    ctx = (write_local_to_debug_frame_ctx *)ctx0;
 
62
 
 
63
    /* write it out */
 
64
    if (sym->write_to_debug_frame())
 
65
    {
 
66
        /* we wrote the symbol - count it */
 
67
        ++(ctx->count);
 
68
    }
 
69
}
 
70
 
 
71
/*
 
72
 *   Build the debug information table for a code body 
 
73
 */
 
74
void CTPNCodeBody::build_debug_table(ulong start_ofs)
 
75
{
 
76
    size_t i;
 
77
    ulong post_ptr_ofs;
 
78
    ulong index_ofs;
 
79
    CTcPrsSymtab *frame;
 
80
 
 
81
    /* fix up the debug record offset in the prolog to point here */
 
82
    G_cs->write2_at(start_ofs + 8, G_cs->get_ofs() - start_ofs);
 
83
 
 
84
    /* 
 
85
     *   Add this offset to our list of line records.  If we're creating
 
86
     *   an object file, upon re-loading the object file, we'll need to go
 
87
     *   through all of the line record tables and fix up the file
 
88
     *   references to the new numbering system after loading, so we need
 
89
     *   this memory of where the line record are.  
 
90
     */
 
91
    G_cg->add_debug_line_table(G_cs->get_ofs());
 
92
 
 
93
    /* write the number of line records */
 
94
    G_cs->write2(G_cs->get_line_rec_count());
 
95
 
 
96
    /* write the line records themselves */
 
97
    for (i = 0 ; i < G_cs->get_line_rec_count() ; ++i)
 
98
    {
 
99
        tcgen_line_t *rec;
 
100
 
 
101
        /* get this record */
 
102
        rec = G_cs->get_line_rec(i);
 
103
 
 
104
        /* write the offset of the statement's first opcode */
 
105
        G_cs->write2(rec->ofs);
 
106
 
 
107
        /* write the source file ID and line number */
 
108
        G_cs->write2(rec->source_id);
 
109
        G_cs->write4(rec->source_line);
 
110
 
 
111
        /* write the frame ID */
 
112
        G_cs->write2(rec->frame == 0 ? 0 : rec->frame->get_list_index());
 
113
    }
 
114
 
 
115
    /* 
 
116
     *   write a placeholder pointer to the next byte after the end of the
 
117
     *   frame table 
 
118
     */
 
119
    post_ptr_ofs = G_cs->get_ofs();
 
120
    G_cs->write2(0);
 
121
 
 
122
    /* write the frame count */
 
123
    G_cs->write2(G_cs->get_frame_count());
 
124
 
 
125
    /* 
 
126
     *   Write a placeholder frame index table.  We will come back and fix
 
127
     *   up this table as we actually write out the frames, but we don't
 
128
     *   actually know how big the individual frame records will be yet,
 
129
     *   so we can only write placeholders for them for now.  First, note
 
130
     *   where the frame index table begins.  
 
131
     */
 
132
    index_ofs = G_cs->get_ofs();
 
133
 
 
134
    /* write the placeholder index entries */
 
135
    for (i = 0 ; i < G_cs->get_frame_count() ; ++i)
 
136
        G_cs->write2(0);
 
137
 
 
138
    /* write the individual frames */
 
139
    for (frame = G_cs->get_first_frame() ; frame != 0 ;
 
140
         frame = frame->get_list_next())
 
141
    {
 
142
        ulong count_ofs;
 
143
        write_local_to_debug_frame_ctx cbctx;
 
144
 
 
145
        /* 
 
146
         *   go back and fill in the correct offset (from the entry
 
147
         *   itself) in the index table entry for this frame 
 
148
         */
 
149
        G_cs->write2_at(index_ofs, G_cs->get_ofs() - index_ofs);
 
150
 
 
151
        /* move on to the next index entry */
 
152
        index_ofs += 2;
 
153
 
 
154
        /* write the ID of the enclosing frame */
 
155
        G_cs->write2(frame->get_parent() == 0
 
156
                     ? 0 : frame->get_parent()->get_list_index());
 
157
 
 
158
        /* 
 
159
         *   write a placeholder for the count of the number of entries in
 
160
         *   the frame, and remember where the placeholder is so we can
 
161
         *   come back and fix it up later 
 
162
         */
 
163
        count_ofs = G_cs->get_ofs();
 
164
        G_cs->write2(0);
 
165
 
 
166
        /* initialize the enumeration callback context */
 
167
        cbctx.count = 0;
 
168
 
 
169
        /* write this frame table's entries */
 
170
        frame->enum_entries(&write_local_to_debug_frame, &cbctx);
 
171
 
 
172
        /* go back and fix up the symbol count */
 
173
        G_cs->write2_at(count_ofs, cbctx.count);
 
174
    }
 
175
 
 
176
    /* 
 
177
     *   go back and fill in the post-pointer offset - this is a pointer
 
178
     *   to the next byte after the end of the frame table; write the
 
179
     *   offset from the post-pointer field to the current location 
 
180
     */
 
181
    G_cs->write2_at(post_ptr_ofs, G_cs->get_ofs() - post_ptr_ofs);
 
182
 
 
183
    /* 
 
184
     *   write the required UINT4 zero value after the frame table - this
 
185
     *   is a placeholder for future expansion (if we add more information
 
186
     *   to the debug table later, this value will be non-zero to indicate
 
187
     *   the presence of the additional information) 
 
188
     */
 
189
    G_cs->write4(0);
 
190
}
 
191
 
 
192
/* ------------------------------------------------------------------------ */
 
193
/*
 
194
 *   'if' statement
 
195
 */
 
196
 
 
197
/*
 
198
 *   generate code 
 
199
 */
 
200
void CTPNStmIf::gen_code(int, int)
 
201
{
 
202
    /* add a line record */
 
203
    add_debug_line_rec();
 
204
 
 
205
    /* 
 
206
     *   if the condition has a constant value, don't bother generating
 
207
     *   code for both branches 
 
208
     */
 
209
    if (cond_expr_->is_const())
 
210
    {
 
211
        int val;
 
212
 
 
213
        /* determine whether it's true or false */
 
214
        val = cond_expr_->get_const_val()->get_val_bool();
 
215
        
 
216
        /* 
 
217
         *   Warn about it if it's always false (in which case the 'then'
 
218
         *   code is unreachable); or it's always true and we have an
 
219
         *   'else' part (since the 'else' part is unreachable).  Don't
 
220
         *   warn if it's true and there's no 'else' part, since this
 
221
         *   merely means that there's some redundant source code, but
 
222
         *   will have no effect on the generated code.  
 
223
         */
 
224
        if (!val)
 
225
        {
 
226
            /* it's false - the 'then' part cannot be executed */
 
227
            log_warning(TCERR_IF_ALWAYS_FALSE);
 
228
 
 
229
            /* generate the 'else' part if there is one */
 
230
            if (else_part_ != 0)
 
231
                gen_code_substm(else_part_);
 
232
        }
 
233
        else
 
234
        {
 
235
            /* it's true - the 'else' part cannot be executed */
 
236
            if (else_part_ != 0)
 
237
                log_warning(TCERR_IF_ALWAYS_TRUE);
 
238
 
 
239
            /* generate the 'then' part */
 
240
            if (then_part_ != 0)
 
241
                gen_code_substm(then_part_);
 
242
        }
 
243
 
 
244
        /* we're done */
 
245
        return;
 
246
    }
 
247
 
 
248
    /*
 
249
     *   If both the 'then' and 'else' parts are null statements, we're
 
250
     *   evaluating the condition purely for side effects.  Simply
 
251
     *   evaluate the condition in this case, since there's no need to so
 
252
     *   much as test the condition once evaluated. 
 
253
     */
 
254
    if (then_part_ == 0 && else_part_ == 0)
 
255
    {
 
256
        /* generate the condition, discarding the result */
 
257
        cond_expr_->gen_code(TRUE, TRUE);
 
258
 
 
259
        /* we're done */
 
260
        return;
 
261
    }
 
262
 
 
263
    /* 
 
264
     *   The condition is non-constant, and we have at least one subclause,
 
265
     *   so we must evaluate the condition expression.  To minimize the
 
266
     *   amount of jumping, check whether we have a true part, else part, or
 
267
     *   both, and generate the branching accordingly.  
 
268
     */
 
269
    if (then_part_ != 0)
 
270
    {
 
271
        CTcCodeLabel *lbl_else;
 
272
        CTcCodeLabel *lbl_end;
 
273
 
 
274
        /*
 
275
         *   We have a true part, so we will want to evaluate the expression
 
276
         *   and jump past the true part if the expression is false.  Create
 
277
         *   a label for the false branch.  
 
278
         */
 
279
        lbl_else = G_cs->new_label_fwd();
 
280
 
 
281
        /* generate the condition expression */
 
282
        cond_expr_->gen_code_cond(0, lbl_else);
 
283
 
 
284
        /* generate the 'then' part */
 
285
        gen_code_substm(then_part_);
 
286
 
 
287
        /* if there's an 'else' part, generate it */
 
288
        if (else_part_ != 0)
 
289
        {
 
290
            /* at the end of the 'then' part, jump past the 'else' part */
 
291
            lbl_end = gen_jump_ahead(OPC_JMP);
 
292
 
 
293
            /* this is the start of the 'else' part */
 
294
            def_label_pos(lbl_else);
 
295
 
 
296
            /* generate the 'else' part */
 
297
            gen_code_substm(else_part_);
 
298
 
 
299
            /* set the label for the jump over the 'else' part */
 
300
            def_label_pos(lbl_end);
 
301
        }
 
302
        else
 
303
        {
 
304
            /* 
 
305
             *   there's no 'else' part - set the label for the jump past the
 
306
             *   'then' part 
 
307
             */
 
308
            def_label_pos(lbl_else);
 
309
        }
 
310
    }
 
311
    else
 
312
    {
 
313
        CTcCodeLabel *lbl_end;
 
314
 
 
315
        /* 
 
316
         *   There's no 'then' part, so there must be an 'else' part (we
 
317
         *   wouldn't have gotten this far if both 'then' and 'else' are
 
318
         *   empty).  To minimize branching, evaluate the condition and jump
 
319
         *   past the 'else' part if the condition is true, falling through
 
320
         *   to the 'else' part otherwise.  Create a label for the end of the
 
321
         *   statement, which is also the empty 'then' part.  
 
322
         */
 
323
        lbl_end = G_cs->new_label_fwd();
 
324
 
 
325
        /* evaluate the condition and jump to the end if it's true */
 
326
        cond_expr_->gen_code_cond(lbl_end, 0);
 
327
 
 
328
        /* generate the 'else' part */
 
329
        gen_code_substm(else_part_);
 
330
 
 
331
        /* set the label for the jump over the 'else' part */
 
332
        def_label_pos(lbl_end);
 
333
    }
 
334
}
 
335
 
 
336
/* ------------------------------------------------------------------------ */
 
337
/*
 
338
 *   'for' statement 
 
339
 */
 
340
 
 
341
/*
 
342
 *   generate code 
 
343
 */
 
344
void CTPNStmFor::gen_code(int, int)
 
345
{
 
346
    CTcCodeLabel *top_lbl;
 
347
    CTcCodeLabel *end_lbl;
 
348
    CTcCodeLabel *cont_lbl;
 
349
    CTPNStmEnclosing *old_enclosing;
 
350
    CTcPrsSymtab *old_frame;
 
351
 
 
352
    /* set my local frame if necessary */
 
353
    old_frame = G_cs->set_local_frame(symtab_);
 
354
 
 
355
    /* 
 
356
     *   add a line record - note that we add the line record after
 
357
     *   setting up the local frame, so that the 'for' statement itself
 
358
     *   appears within its own inner scope 
 
359
     */
 
360
    add_debug_line_rec();
 
361
 
 
362
    /* push the enclosing statement */
 
363
    old_enclosing = G_cs->set_enclosing(this);
 
364
    
 
365
    /* if there's an initializer expression, generate it */
 
366
    if (init_expr_ != 0)
 
367
        init_expr_->gen_code(TRUE, TRUE);
 
368
 
 
369
    /* set the label for the top of the loop */
 
370
    top_lbl = new_label_here();
 
371
 
 
372
    /* allocate a forward label for 'continue' jumps */
 
373
    cont_lbl = G_cs->new_label_fwd();
 
374
 
 
375
    /* allocate a forward label for the end of the loop */
 
376
    end_lbl = G_cs->new_label_fwd();
 
377
 
 
378
    /* 
 
379
     *   If there's a condition, generate its code, jumping to the end of the
 
380
     *   loop if the condition is false.  
 
381
     */
 
382
    if (cond_expr_ != 0)
 
383
        cond_expr_->gen_code_cond(0, end_lbl);
 
384
 
 
385
    /* 
 
386
     *   set our labels, so that 'break' and 'continue' statements in our
 
387
     *   body will know where to go 
 
388
     */
 
389
    break_lbl_ = end_lbl;
 
390
    cont_lbl_ = cont_lbl;
 
391
 
 
392
    /* if we have a body, generate it */
 
393
    if (body_stm_ != 0)
 
394
        gen_code_substm(body_stm_);
 
395
 
 
396
    /* 
 
397
     *   add another line record - we're now generating code again for the
 
398
     *   original 'for' line, even though it's after the body 
 
399
     */
 
400
//$$$    add_debug_line_rec();
 
401
 
 
402
    /* this is where we come for 'continue' statements */
 
403
    def_label_pos(cont_lbl);
 
404
 
 
405
    /* generate the reinitializer expression, if we have one */
 
406
    if (reinit_expr_ != 0)
 
407
        reinit_expr_->gen_code(TRUE, TRUE);
 
408
 
 
409
    /* jump back to the top of the loop */
 
410
    G_cg->write_op(OPC_JMP);
 
411
    G_cs->write_ofs2(top_lbl, 0);
 
412
 
 
413
    /* 
 
414
     *   we're at the end of the loop - this is where we jump for 'break'
 
415
     *   and when the condition becomes false 
 
416
     */
 
417
    def_label_pos(end_lbl);
 
418
 
 
419
    /* restore the enclosing statement */
 
420
    G_cs->set_enclosing(old_enclosing);
 
421
 
 
422
    /* restore the enclosing local scope */
 
423
    G_cs->set_local_frame(old_frame);
 
424
}
 
425
 
 
426
/* ------------------------------------------------------------------------ */
 
427
/*
 
428
 *   'foreach' statement 
 
429
 */
 
430
 
 
431
/*
 
432
 *   generate code 
 
433
 */
 
434
void CTPNStmForeach::gen_code(int, int)
 
435
{
 
436
    CTcCodeLabel *top_lbl;
 
437
    CTcCodeLabel *end_lbl;
 
438
    CTPNStmEnclosing *old_enclosing;
 
439
    CTcPrsSymtab *old_frame;
 
440
    CTcSymMetaclass *iter_meta;
 
441
    CTcSymProp *get_next_prop = 0;
 
442
    CTcSymProp *is_next_avail_prop = 0;
 
443
 
 
444
    /* set my local frame if necessary */
 
445
    old_frame = G_cs->set_local_frame(symtab_);
 
446
 
 
447
    /* 
 
448
     *   add a line record - note that we add the line record after
 
449
     *   setting up the local frame, so that the 'for' statement itself
 
450
     *   appears within its own inner scope 
 
451
     */
 
452
    add_debug_line_rec();
 
453
 
 
454
    /* push the enclosing statement */
 
455
    old_enclosing = G_cs->set_enclosing(this);
 
456
 
 
457
    /* if there's a collection expression, generate it */
 
458
    if (coll_expr_ != 0)
 
459
    {
 
460
        CTcSymMetaclass *coll_meta;
 
461
        CTcSymProp *create_iter_prop = 0;
 
462
        
 
463
        /* 
 
464
         *   Look up the createIterator property of the Collection
 
465
         *   metaclass.  This property is defined by the Collection
 
466
         *   specification as the property in the first slot in the method
 
467
         *   table for Collection.  If Collection isn't defined, or this
 
468
         *   slot isn't defined, it's an error. 
 
469
         */
 
470
        coll_meta = G_cg->find_meta_sym("collection", 10);
 
471
        if (coll_meta != 0)
 
472
        {
 
473
            CTcSymMetaProp *mprop;
 
474
 
 
475
            /* get the first entry in the metaclass property list */
 
476
            mprop = coll_meta->get_nth_prop(0);
 
477
 
 
478
            /* if we got the entry, get its property */
 
479
            create_iter_prop = mprop->prop_;
 
480
        }
 
481
 
 
482
        /* if we didn't find the property, it's an error */
 
483
        if (create_iter_prop == 0)
 
484
        {
 
485
            /* tell them about the problem */
 
486
            G_tok->log_error(TCERR_FOREACH_NO_CREATEITER);
 
487
        }
 
488
        else
 
489
        {
 
490
            CTcPrsNode *prop_expr;
 
491
            
 
492
            /* construct an expression for the property */
 
493
            prop_expr = new CTPNSymResolved(create_iter_prop);
 
494
 
 
495
            /* 
 
496
             *   generate a call to the createIterator() property on the
 
497
             *   collection expression 
 
498
             */
 
499
            coll_expr_->gen_code_member(FALSE, prop_expr, FALSE, 0, FALSE);
 
500
 
 
501
            /* assign the result to the internal iterator stack local */
 
502
            CTcSymLocal::s_gen_code_setlcl_stk(iter_local_id_, FALSE);
 
503
        }
 
504
    }
 
505
 
 
506
    /* set the label for the top of the loop */
 
507
    top_lbl = new_label_here();
 
508
 
 
509
    /* get the Iterator metaclass */
 
510
    iter_meta = G_cg->find_meta_sym("iterator", 8);
 
511
    if (iter_meta != 0)
 
512
    {
 
513
        CTcSymMetaProp *mprop;
 
514
            
 
515
        /* get the getNext() property - it's in the first slot */
 
516
        if ((mprop = iter_meta->get_nth_prop(0)) != 0)
 
517
            get_next_prop = mprop->prop_;
 
518
 
 
519
        /* get the isNextAvailable() property - it's in the second slot */
 
520
        if ((mprop = iter_meta->get_nth_prop(1)) != 0)
 
521
            is_next_avail_prop = mprop->prop_;
 
522
    }
 
523
 
 
524
    /* generate the isNextAvailable test */
 
525
    if (is_next_avail_prop != 0)
 
526
    {
 
527
        CTcPrsNode *prop_expr;
 
528
        
 
529
        /* get the internal iterator local */
 
530
        CTcSymLocal::s_gen_code_getlcl(iter_local_id_, FALSE);
 
531
 
 
532
        /* create an expression for the property */
 
533
        prop_expr = new CTPNSymResolved(is_next_avail_prop);
 
534
 
 
535
        /* generate a call to the property */
 
536
        CTcPrsNode::s_gen_member_rhs(FALSE, prop_expr, FALSE, 0, FALSE);
 
537
 
 
538
        /* jump out of the loop if the expression is false */
 
539
        end_lbl = gen_jump_ahead(OPC_JF);
 
540
 
 
541
        /* the JF pops an element off the stack */
 
542
        G_cg->note_pop();
 
543
    }
 
544
    else
 
545
    {
 
546
        /* this property is required to be defined - this is an error */
 
547
        G_tok->log_error(TCERR_FOREACH_NO_ISNEXTAVAIL);
 
548
 
 
549
        /* 
 
550
         *   generate an arbitrary 'end' label - we're not going to end up
 
551
         *   generating valid code anyway, but since we're not going to abort
 
552
         *   code generation, it'll avoid problems elsewhere if we have a
 
553
         *   valid label assigned 
 
554
         */
 
555
        end_lbl = new_label_here();
 
556
    }
 
557
 
 
558
    /* generate the code to get the next element of the iteration */
 
559
    if (get_next_prop != 0)
 
560
    {
 
561
        CTcPrsNode *prop_expr;
 
562
 
 
563
        /* get the internal iterator local */
 
564
        CTcSymLocal::s_gen_code_getlcl(iter_local_id_, FALSE);
 
565
 
 
566
        /* create an expression for the property */
 
567
        prop_expr = new CTPNSymResolved(get_next_prop);
 
568
 
 
569
        /* generate a call to the property */
 
570
        CTcPrsNode::s_gen_member_rhs(FALSE, prop_expr, FALSE, 0, FALSE);
 
571
 
 
572
        /* assign the result to the iterator lvalue */
 
573
        if (iter_expr_ != 0)
 
574
            iter_expr_->gen_code_asi(TRUE, TC_ASI_SIMPLE, 0, FALSE);
 
575
    }
 
576
    else
 
577
    {
 
578
        /* this property is required to be defined - this is an error */
 
579
        G_tok->log_error(TCERR_FOREACH_NO_GETNEXT);
 
580
    }
 
581
 
 
582
    /* 
 
583
     *   set our labels, so that 'break' and 'continue' statements in our
 
584
     *   body will know where to go 
 
585
     */
 
586
    break_lbl_ = end_lbl;
 
587
    cont_lbl_ = top_lbl;
 
588
 
 
589
    /* if we have a body, generate it */
 
590
    if (body_stm_ != 0)
 
591
        gen_code_substm(body_stm_);
 
592
 
 
593
    /* 
 
594
     *   add another line record - we're now generating code again for the
 
595
     *   original 'foreach' line, even though it's after the body 
 
596
     */
 
597
//$$$    add_debug_line_rec();
 
598
 
 
599
    /* jump back to the top of the loop */
 
600
    G_cg->write_op(OPC_JMP);
 
601
    G_cs->write_ofs2(top_lbl, 0);
 
602
 
 
603
    /* 
 
604
     *   we're at the end of the loop - this is where we jump for 'break'
 
605
     *   and when the condition becomes false 
 
606
     */
 
607
    if (end_lbl != 0)
 
608
        def_label_pos(end_lbl);
 
609
 
 
610
    /* restore the enclosing statement */
 
611
    G_cs->set_enclosing(old_enclosing);
 
612
 
 
613
    /* restore the enclosing local scope */
 
614
    G_cs->set_local_frame(old_frame);
 
615
}
 
616
 
 
617
/* ------------------------------------------------------------------------ */
 
618
/*
 
619
 *   'while' statement 
 
620
 */
 
621
 
 
622
/*
 
623
 *   generate code 
 
624
 */
 
625
void CTPNStmWhile::gen_code(int, int)
 
626
{
 
627
    CTcCodeLabel *top_lbl;
 
628
    CTcCodeLabel *end_lbl;
 
629
    CTPNStmEnclosing *old_enclosing;
 
630
 
 
631
    /* add a line record */
 
632
    add_debug_line_rec();
 
633
 
 
634
    /* push the enclosing statement */
 
635
    old_enclosing = G_cs->set_enclosing(this);
 
636
 
 
637
    /* set the label for the top of the loop */
 
638
    top_lbl = new_label_here();
 
639
 
 
640
    /* generate a label for the end of the loop */
 
641
    end_lbl = G_cs->new_label_fwd();
 
642
 
 
643
    /* generate the condition, jumping to the end of the loop if false */
 
644
    cond_expr_->gen_code_cond(0, end_lbl);
 
645
 
 
646
    /* 
 
647
     *   set the 'break' and 'continue' label in our node, so that 'break'
 
648
     *   and 'continue' statements in subnodes can find the labels during
 
649
     *   code generation 
 
650
     */
 
651
    break_lbl_ = end_lbl;
 
652
    cont_lbl_ = top_lbl;
 
653
 
 
654
    /* if we have a body, generate it */
 
655
    if (body_stm_ != 0)
 
656
        gen_code_substm(body_stm_);
 
657
 
 
658
    /* 
 
659
     *   add another line record - the jump back to the top of the loop is
 
660
     *   part of the 'while' itself 
 
661
     */
 
662
//$$$    add_debug_line_rec();
 
663
 
 
664
    /* jump back to the top of the loop */
 
665
    G_cg->write_op(OPC_JMP);
 
666
    G_cs->write_ofs2(top_lbl, 0);
 
667
 
 
668
    /* 
 
669
     *   we're at the end of the loop - this is where we jump for 'break'
 
670
     *   and when the condition becomes false 
 
671
     */
 
672
    def_label_pos(end_lbl);
 
673
 
 
674
    /* restore the enclosing statement */
 
675
    G_cs->set_enclosing(old_enclosing);
 
676
}
 
677
 
 
678
 
 
679
/* ------------------------------------------------------------------------ */
 
680
/*
 
681
 *   'do-while' statement 
 
682
 */
 
683
 
 
684
/*
 
685
 *   generate code 
 
686
 */
 
687
void CTPNStmDoWhile::gen_code(int, int)
 
688
{
 
689
    CTcCodeLabel *top_lbl;
 
690
    CTcCodeLabel *end_lbl;
 
691
    CTcCodeLabel *cont_lbl;
 
692
    CTPNStmEnclosing *old_enclosing;
 
693
 
 
694
    /* add a line record */
 
695
    add_debug_line_rec();
 
696
 
 
697
    /* push the enclosing statement */
 
698
    old_enclosing = G_cs->set_enclosing(this);
 
699
 
 
700
    /* set the label for the top of the loop */
 
701
    top_lbl = new_label_here();
 
702
 
 
703
    /* create a label for after the loop, for any enclosed 'break's */
 
704
    end_lbl = G_cs->new_label_fwd();
 
705
 
 
706
    /* 
 
707
     *   create a label for just before the expression, for any enclosed
 
708
     *   'continue' statements 
 
709
     */
 
710
    cont_lbl = G_cs->new_label_fwd();
 
711
 
 
712
    /* set our 'break' and 'continue' labels in our node */
 
713
    break_lbl_ = end_lbl;
 
714
    cont_lbl_ = cont_lbl;
 
715
 
 
716
    /* if we have a body, generate it */
 
717
    if (body_stm_ != 0)
 
718
        gen_code_substm(body_stm_);
 
719
 
 
720
    /* set the debug source position to the 'while' clause's location */
 
721
    add_debug_line_rec(while_desc_, while_linenum_);
 
722
 
 
723
    /* put the 'continue' label here, just before the condition */
 
724
    def_label_pos(cont_lbl);
 
725
 
 
726
    /* 
 
727
     *   Generate the condition.  If the condition is true, jump back to the
 
728
     *   top label; otherwise fall through out of the loop structure.  
 
729
     */
 
730
    cond_expr_->gen_code_cond(top_lbl, 0);
 
731
 
 
732
    /* we're past the end of the loop - this is where we jump for 'break' */
 
733
    def_label_pos(end_lbl);
 
734
 
 
735
    /* restore the enclosing statement */
 
736
    G_cs->set_enclosing(old_enclosing);
 
737
}
 
738
 
 
739
 
 
740
/* ------------------------------------------------------------------------ */
 
741
/*
 
742
 *   'break' statement 
 
743
 */
 
744
 
 
745
/*
 
746
 *   generate code 
 
747
 */
 
748
void CTPNStmBreak::gen_code(int, int)
 
749
{
 
750
    /* add a line record */
 
751
    add_debug_line_rec();
 
752
 
 
753
    /* 
 
754
     *   ask the enclosing statement to do the work - if there's no
 
755
     *   enclosing statement, or none of the enclosing statements can
 
756
     *   perform the break, it's an error 
 
757
     */
 
758
    if (G_cs->get_enclosing() == 0
 
759
        || !G_cs->get_enclosing()->gen_code_break(lbl_, lbl_len_))
 
760
    {
 
761
        /* 
 
762
         *   log the error - if there's a label, the problem is that we
 
763
         *   couldn't find the label, otherwise it's that we can't perform
 
764
         *   a 'break' here at all 
 
765
         */
 
766
        if (lbl_ == 0)
 
767
            G_tok->log_error(TCERR_INVALID_BREAK);
 
768
        else
 
769
            G_tok->log_error(TCERR_INVALID_BREAK_LBL, (int)lbl_len_, lbl_);
 
770
    }
 
771
}
 
772
 
 
773
/* ------------------------------------------------------------------------ */
 
774
/*
 
775
 *   'continue' statement 
 
776
 */
 
777
 
 
778
/*
 
779
 *   generate code 
 
780
 */
 
781
void CTPNStmContinue::gen_code(int, int)
 
782
{
 
783
    /* add a line record */
 
784
    add_debug_line_rec();
 
785
 
 
786
    /* 
 
787
     *   ask the enclosing statement to do the work - if there's no
 
788
     *   enclosing statement, or none of the enclosing statements can
 
789
     *   perform the break, it's an error 
 
790
     */
 
791
    if (G_cs->get_enclosing() == 0
 
792
        || !G_cs->get_enclosing()->gen_code_continue(lbl_, lbl_len_))
 
793
    {
 
794
        /* 
 
795
         *   log the error - if there's a label, the problem is that we
 
796
         *   couldn't find the label, otherwise it's that we can't perform
 
797
         *   a 'break' here at all 
 
798
         */
 
799
        if (lbl_ == 0)
 
800
            G_tok->log_error(TCERR_INVALID_CONTINUE);
 
801
        else
 
802
            G_tok->log_error(TCERR_INVALID_CONT_LBL, (int)lbl_len_, lbl_);
 
803
    }
 
804
}
 
805
 
 
806
/* ------------------------------------------------------------------------ */
 
807
/*
 
808
 *   'switch' statement 
 
809
 */
 
810
 
 
811
/*
 
812
 *   generate code 
 
813
 */
 
814
void CTPNStmSwitch::gen_code(int, int)
 
815
{
 
816
    CTPNStmSwitch *enclosing_switch;
 
817
    int i;
 
818
    char buf[VMB_DATAHOLDER + VMB_UINT2];
 
819
    CTcCodeLabel *end_lbl;
 
820
    CTPNStmEnclosing *old_enclosing;
 
821
 
 
822
    /* add a line record */
 
823
    add_debug_line_rec();
 
824
 
 
825
    /* push the enclosing statement */
 
826
    old_enclosing = G_cs->set_enclosing(this);
 
827
 
 
828
    /* 
 
829
     *   Generate the controlling expression.  We want to keep the value,
 
830
     *   hence 'discard' is false, and we need assignment (not 'for
 
831
     *   condition') conversion rules, because we're going to use the
 
832
     *   value in direct comparisons 
 
833
     */
 
834
    expr_->gen_code(FALSE, FALSE);
 
835
 
 
836
    /* make myself the current innermost switch */
 
837
    enclosing_switch = G_cs->set_switch(this);
 
838
 
 
839
    /*
 
840
     *   if we can flow out of the switch, allocate a label for the end of
 
841
     *   the switch body 
 
842
     */
 
843
    if ((get_control_flow(FALSE) & TCPRS_FLOW_NEXT) != 0)
 
844
        end_lbl = G_cs->new_label_fwd();
 
845
    else
 
846
        end_lbl = 0;
 
847
 
 
848
    /* the end label is the 'break' location for subnodes */
 
849
    break_lbl_ = end_lbl;
 
850
 
 
851
    /*
 
852
     *   Write my SWITCH opcode, and the placeholder case table.  We'll
 
853
     *   fill in the case table with its real values as we encounter the
 
854
     *   cases in the course of generating the code.  For now, all we know
 
855
     *   is the number of cases we need to put into the table.  
 
856
     */
 
857
    G_cg->write_op(OPC_SWITCH);
 
858
 
 
859
    /* the SWITCH opcode pops the controlling expression value */
 
860
    G_cg->note_pop();
 
861
 
 
862
    /* write the number of cases */
 
863
    G_cs->write2(case_cnt_);
 
864
 
 
865
    /* 
 
866
     *   remember where the first case slot is - the 'case' parse nodes
 
867
     *   will use this to figure out where to write their slot data 
 
868
     */
 
869
    case_slot_ofs_ = G_cs->get_ofs();
 
870
 
 
871
    /* 
 
872
     *   Write the placeholder case slots - each case slot gets a
 
873
     *   DATA_HOLDER for the case value, plus an INT2 for the branch
 
874
     *   offset.  For now, completely zero each case slot.  
 
875
     */
 
876
    memset(buf, 0, VMB_DATAHOLDER + VMB_UINT2);
 
877
    for (i = 0 ; i < case_cnt_ ; ++i)
 
878
        G_cs->write(buf, VMB_DATAHOLDER + VMB_UINT2);
 
879
 
 
880
    /* write a placeholder for the default jump */
 
881
    if (has_default_)
 
882
    {
 
883
        /* 
 
884
         *   remember where the 'default' slot is, so that the 'default'
 
885
         *   parse node can figure out where to write its branch offset
 
886
         */
 
887
        default_slot_ofs_ = G_cs->get_ofs();
 
888
        
 
889
        /* 
 
890
         *   Write the placeholder for the 'default' slot - this just gets
 
891
         *   an INT2 for the 'default' jump offset.  As with the case
 
892
         *   labels, just zero it for now; we'll fill it in later when we
 
893
         *   encounter the 'default' case.  
 
894
         */
 
895
        G_cs->write2(0);
 
896
    }
 
897
    else
 
898
    {
 
899
        /* 
 
900
         *   there's no default slot, so the 'default' slot is simply a
 
901
         *   jump to the end of the switch body - generate a jump ahead to
 
902
         *   our end label 
 
903
         */
 
904
        G_cs->write_ofs2(end_lbl, 0);
 
905
    }
 
906
 
 
907
    /* 
 
908
     *   generate the switch body - this will fill in the case table as we
 
909
     *   encounter the 'case' nodes in the parse tree
 
910
     */
 
911
    if (body_ != 0)
 
912
        gen_code_substm(body_);
 
913
 
 
914
    /* 
 
915
     *   We're past the body - if we have an end label, set it here.  (We
 
916
     *   won't have created an end label if control can't flow out of the
 
917
     *   switch; this allows us to avoid generating unreachable instructions
 
918
     *   after the switch, which would only increase the code size for no
 
919
     *   reason.)  
 
920
     */
 
921
    if (end_lbl != 0)
 
922
        def_label_pos(end_lbl);
 
923
 
 
924
    /* restore the enclosing switch */
 
925
    G_cs->set_switch(enclosing_switch);
 
926
 
 
927
    /* restore the enclosing statement */
 
928
    G_cs->set_enclosing(old_enclosing);
 
929
}
 
930
 
 
931
/* ------------------------------------------------------------------------ */
 
932
/*
 
933
 *   'case' label statement 
 
934
 */
 
935
 
 
936
/*
 
937
 *   generate code 
 
938
 */
 
939
void CTPNStmCase::gen_code(int, int)
 
940
{
 
941
    ulong slot_ofs;
 
942
    ulong jump_ofs;
 
943
 
 
944
    /* 
 
945
     *   we must have an active 'switch' statement, and our expression
 
946
     *   value must be a constant -- if either of these is not true, we
 
947
     *   have an internal error of some kind, because we should never get
 
948
     *   this far if these conditions weren't true 
 
949
     */
 
950
    if (G_cs->get_switch() == 0 || !expr_->is_const())
 
951
        G_tok->throw_internal_error(TCERR_GEN_BAD_CASE);
 
952
 
 
953
    /* allocate our case slot from the enclosing 'switch' statement */
 
954
    slot_ofs = G_cs->get_switch()->alloc_case_slot();
 
955
 
 
956
    /* write the case table entry as a DATAHOLDER value */
 
957
    G_cg->write_const_as_dh(G_cs, slot_ofs, expr_->get_const_val());
 
958
 
 
959
    /*
 
960
     *   Add the jump offset.  This is the offset from this INT2 entry in
 
961
     *   our case slot to the current output offset.  The INT2 is offset
 
962
     *   from the start of our slot by the DATAHOLDER value.  
 
963
     */
 
964
    jump_ofs = G_cs->get_ofs() - (slot_ofs + VMB_DATAHOLDER);
 
965
    G_cs->write2_at(slot_ofs + VMB_DATAHOLDER, (int)jump_ofs);
 
966
 
 
967
    /* 
 
968
     *   because we can jump here (via the case table), we cannot allow
 
969
     *   peephole optimizations from past instructions - clear the
 
970
     *   peephole 
 
971
     */
 
972
    G_cg->clear_peephole();
 
973
 
 
974
    /* generate our substatement, if we have one */
 
975
    if (stm_ != 0)
 
976
        gen_code_substm(stm_);
 
977
}
 
978
 
 
979
/* ------------------------------------------------------------------------ */
 
980
/*
 
981
 *   'default' label statement 
 
982
 */
 
983
 
 
984
/*
 
985
 *   generate code 
 
986
 */
 
987
void CTPNStmDefault::gen_code(int, int)
 
988
{
 
989
    ulong slot_ofs;
 
990
    char buf[VMB_UINT2];
 
991
    ulong jump_ofs;
 
992
 
 
993
    /* 
 
994
     *   we must have an active 'switch' statement -- if we don't, we have
 
995
     *   an internal error of some kind, because we should never have
 
996
     *   gotten this far 
 
997
     */
 
998
    if (G_cs->get_switch() == 0)
 
999
        G_tok->throw_internal_error(TCERR_GEN_BAD_CASE);
 
1000
 
 
1001
    /* ask the switch where our slot goes */
 
1002
    slot_ofs = G_cs->get_switch()->get_default_slot();
 
1003
 
 
1004
    /*
 
1005
     *   Set the jump offset.  This is the offset from our slot entry in
 
1006
     *   the case table to the current output offset.  
 
1007
     */
 
1008
    jump_ofs = G_cs->get_ofs() - slot_ofs;
 
1009
    oswp2(buf, (int)jump_ofs);
 
1010
 
 
1011
    /* write our slot entry to the case table */
 
1012
    G_cs->write_at(slot_ofs, buf, VMB_UINT2);
 
1013
 
 
1014
    /* 
 
1015
     *   because we can jump here (via the case table), we cannot allow
 
1016
     *   peephole optimizations from past instructions - clear the
 
1017
     *   peephole 
 
1018
     */
 
1019
    G_cg->clear_peephole();
 
1020
 
 
1021
    /* generate our substatement, if we have one */
 
1022
    if (stm_ != 0)
 
1023
        gen_code_substm(stm_);
 
1024
}
 
1025
 
 
1026
/* ------------------------------------------------------------------------ */
 
1027
/*
 
1028
 *   code label statement 
 
1029
 */
 
1030
 
 
1031
/*
 
1032
 *   ininitialize 
 
1033
 */
 
1034
CTPNStmLabel::CTPNStmLabel(CTcSymLabel *lbl, CTPNStmEnclosing *enclosing)
 
1035
    : CTPNStmLabelBase(lbl, enclosing)
 
1036
{
 
1037
    /* 
 
1038
     *   we don't have a 'goto' label yet - we'll allocate it on demand
 
1039
     *   during code generation (labels are local in scope to a code body
 
1040
     *   so we can't allocate this until code generation begins for our
 
1041
     *   containing code body) 
 
1042
     */
 
1043
    goto_label_ = 0;
 
1044
 
 
1045
    /* 
 
1046
     *   we don't yet have a 'break' label - we'll allocate this when
 
1047
     *   someone first refers to it 
 
1048
     */
 
1049
    break_label_ = 0;
 
1050
}
 
1051
 
 
1052
/*
 
1053
 *   get our code label 
 
1054
 */
 
1055
CTcCodeLabel *CTPNStmLabel::get_goto_label()
 
1056
{
 
1057
    /* if we don't have a label already, allocate it */
 
1058
    if (goto_label_ == 0)
 
1059
        goto_label_ = G_cs->new_label_fwd();
 
1060
 
 
1061
    /* return the label */
 
1062
    return goto_label_;
 
1063
}
 
1064
 
 
1065
/*
 
1066
 *   generate code 
 
1067
 */
 
1068
void CTPNStmLabel::gen_code(int, int)
 
1069
{
 
1070
    CTPNStmEnclosing *old_enclosing;
 
1071
 
 
1072
    /* push the enclosing statement */
 
1073
    old_enclosing = G_cs->set_enclosing(this);
 
1074
    
 
1075
    /* 
 
1076
     *   Define our label position - this is where we come if someone does
 
1077
     *   a 'goto' to this label.  (Note that we might not have a 'goto'
 
1078
     *   label defined yet - if we weren't forward-referenced by a 'goto'
 
1079
     *   statement, we won't have a label defined.  Call get_goto_label()
 
1080
     *   to ensure we create a label if it doesn't already exist.)
 
1081
     */
 
1082
    def_label_pos(get_goto_label());
 
1083
 
 
1084
    /* 
 
1085
     *   add the source location of the label - this probably will have no
 
1086
     *   effect, since we don't generate any code for the label itself,
 
1087
     *   but it's harmless so do it anyway to guard against weird cases 
 
1088
     */
 
1089
    add_debug_line_rec();
 
1090
 
 
1091
    /* 
 
1092
     *   generate code for the labeled statement, discarding any
 
1093
     *   calculated value
 
1094
     */
 
1095
    if (stm_ != 0)
 
1096
        gen_code_substm(stm_);
 
1097
 
 
1098
    /* 
 
1099
     *   If we have a 'break' label, it means that code within our labeled
 
1100
     *   statement (i.e., nested within the label) did a 'break' to leave
 
1101
     *   the labeled statement.  The target of the break is the next
 
1102
     *   statement after the labeled statement, which comes next, so
 
1103
     *   define the label here.  
 
1104
     */
 
1105
    if (break_label_ != 0)
 
1106
        def_label_pos(break_label_);
 
1107
 
 
1108
    /* restore the enclosing statement */
 
1109
    G_cs->set_enclosing(old_enclosing);
 
1110
}
 
1111
 
 
1112
 
 
1113
/*
 
1114
 *   generate code for a 'break' 
 
1115
 */
 
1116
int CTPNStmLabel::gen_code_break(const textchar_t *lbl, size_t lbl_len)
 
1117
{
 
1118
    /* 
 
1119
     *   If the 'break' doesn't specify a label, inherit the default
 
1120
     *   handling, since we're not a default 'break' target.  If there's a
 
1121
     *   label, and the label isn't our label, also inherit the default,
 
1122
     *   since the target lies somewhere else.  
 
1123
     */
 
1124
    if (lbl == 0 || G_cs->get_goto_symtab() == 0
 
1125
        || G_cs->get_goto_symtab()->find(lbl, lbl_len) != lbl_)
 
1126
        return CTPNStmLabelBase::gen_code_break(lbl, lbl_len);
 
1127
 
 
1128
    /* 
 
1129
     *   if we don't yet have a 'break' label defined, define one now
 
1130
     *   (it's a forward declaration, because we won't know where it goes
 
1131
     *   until we finish generating the entire body of the statement
 
1132
     *   contained in the label)
 
1133
     */
 
1134
    if (break_label_ == 0)
 
1135
        break_label_ = G_cs->new_label_fwd();
 
1136
 
 
1137
    /* jump to the label */
 
1138
    G_cg->write_op(OPC_JMP);
 
1139
    G_cs->write_ofs2(break_label_, 0);
 
1140
 
 
1141
    /* handled */
 
1142
    return TRUE;
 
1143
}
 
1144
 
 
1145
 
 
1146
/*
 
1147
 *   generate code for a 'continue' 
 
1148
 */
 
1149
int CTPNStmLabel::gen_code_continue(const textchar_t *lbl, size_t lbl_len)
 
1150
{
 
1151
    /* 
 
1152
     *   If there's no label, inherit the default handling, since we're
 
1153
     *   not a default 'continue' target.  If there's a label, and the
 
1154
     *   label isn't our label, also inherit the default, since the target
 
1155
     *   lies somewhere else.  
 
1156
     */
 
1157
    if (lbl == 0 || G_cs->get_goto_symtab() == 0
 
1158
        || G_cs->get_goto_symtab()->find(lbl, lbl_len) != lbl_)
 
1159
        return CTPNStmLabelBase::gen_code_continue(lbl, lbl_len);
 
1160
 
 
1161
    /* 
 
1162
     *   It's a 'continue' with my label - ask my enclosed statement to do
 
1163
     *   the work; return failure if I have no enclosed statement.  Note
 
1164
     *   that we use a special call - generate a *labeled* continue - to
 
1165
     *   let the statement know that it must perform the 'continue'
 
1166
     *   itself and cannot defer to enclosing statements.  
 
1167
     */
 
1168
    if (stm_ != 0)
 
1169
        return stm_->gen_code_labeled_continue();
 
1170
    else
 
1171
        return FALSE;
 
1172
}
 
1173
 
 
1174
 
 
1175
/* ------------------------------------------------------------------------ */
 
1176
/*
 
1177
 *   'try' 
 
1178
 */
 
1179
 
 
1180
/*
 
1181
 *   generate code 
 
1182
 */
 
1183
void CTPNStmTry::gen_code(int, int)
 
1184
{
 
1185
    ulong start_ofs;
 
1186
    ulong end_ofs;
 
1187
    CTPNStmCatch *cur_catch;
 
1188
    CTcCodeLabel *end_lbl;
 
1189
    CTPNStmEnclosing *old_enclosing;
 
1190
    int finally_never_returns;
 
1191
 
 
1192
    /* we have no end label yet */
 
1193
    end_lbl = 0;
 
1194
 
 
1195
    /* 
 
1196
     *   add the source location of the 'try' - it probably won't be
 
1197
     *   needed, because we don't generate any code before the protected
 
1198
     *   body, but it's harmless and makes sure we have a good source
 
1199
     *   location in weird cases 
 
1200
     */
 
1201
    add_debug_line_rec();
 
1202
 
 
1203
    /* push the enclosing statement */
 
1204
    old_enclosing = G_cs->set_enclosing(this);
 
1205
 
 
1206
    /* 
 
1207
     *   If we have a 'finally' clause, we must allocate a
 
1208
     *   forward-reference code label for it.  We need to be able to reach
 
1209
     *   the 'finally' clause throughout generation of the protected code
 
1210
     *   and the 'catch' blocks. 
 
1211
     */
 
1212
    if (finally_stm_ != 0)
 
1213
        finally_lbl_ = G_cs->new_label_fwd();
 
1214
    else
 
1215
        finally_lbl_ = 0;
 
1216
 
 
1217
    /* note where the protected code begins */
 
1218
    start_ofs = G_cs->get_ofs();
 
1219
 
 
1220
    /* generate the protected code */
 
1221
    if (body_stm_ != 0)
 
1222
        gen_code_substm(body_stm_);
 
1223
 
 
1224
    /*
 
1225
     *   Check to see if we have a 'finally' block that never returns.  If we
 
1226
     *   have a 'finally' block, and it doesn't flow to its next statement,
 
1227
     *   then our LJSR's to the 'finally' block will never return.  
 
1228
     */
 
1229
    finally_never_returns =
 
1230
        (finally_stm_ != 0
 
1231
         && (finally_stm_->get_control_flow(FALSE) & TCPRS_FLOW_NEXT) == 0);
 
1232
 
 
1233
    /* 
 
1234
     *   if there's a "finally" clause, we must generate a local subroutine
 
1235
     *   call to the "finally" block
 
1236
     */
 
1237
    gen_jsr_finally();
 
1238
 
 
1239
    /* 
 
1240
     *   We must now jump past the "catch" and "finally" code blocks.  If the
 
1241
     *   "finally" block itself doesn't flow to the next statement, then
 
1242
     *   there's no need to do this, since we'll never be reached here.  If
 
1243
     *   there's no "finally" block, then we won't have LJSR'd anywhere, so
 
1244
     *   this code is definitely reachable.  
 
1245
     */
 
1246
    if (!finally_never_returns)
 
1247
        end_lbl = gen_jump_ahead(OPC_JMP);
 
1248
 
 
1249
    /* 
 
1250
     *   Note where the protected code ends - it ends at one byte below
 
1251
     *   the current write offset, because the current write offset is the
 
1252
     *   next byte we'll write.  The code range we store in the exception
 
1253
     *   table is inclusive of the endpoints. 
 
1254
     */
 
1255
    end_ofs = G_cs->get_ofs() - 1;
 
1256
 
 
1257
    /* generate the 'catch' blocks */
 
1258
    for (cur_catch = first_catch_stm_ ; cur_catch != 0 ;
 
1259
         cur_catch = cur_catch->get_next_catch())
 
1260
    {
 
1261
        /* generate the 'catch' block */
 
1262
        cur_catch->gen_code_catch(start_ofs, end_ofs);
 
1263
 
 
1264
        /* call the 'finally' block after the 'catch' finishes */
 
1265
        gen_jsr_finally();
 
1266
 
 
1267
        /* 
 
1268
         *   If there's a finally block, or there's another 'catch' after me,
 
1269
         *   generate a jump past the remaining catch/finally blocks.
 
1270
         *   
 
1271
         *   If we do have a finally that doesn't flow to the next statement
 
1272
         *   (i.e., we throw or return out of the finally), then there's no
 
1273
         *   need to generate a jump, since we'll never come back here from
 
1274
         *   the finally block.  
 
1275
         */
 
1276
        if (!finally_never_returns
 
1277
            && (finally_stm_ != 0 || cur_catch->get_next_catch() != 0))
 
1278
        {
 
1279
            /* 
 
1280
             *   if we have no end label yet, generate one now - we might
 
1281
             *   not have one because we might not have been able to reach
 
1282
             *   any previous jump to the end of the catch (because we threw
 
1283
             *   or returned out of the end of all blocks to this point, for
 
1284
             *   example) 
 
1285
             */
 
1286
            if (end_lbl == 0)
 
1287
            {
 
1288
                /* we have no label - generate a new one now */
 
1289
                end_lbl = gen_jump_ahead(OPC_JMP);
 
1290
            }
 
1291
            else
 
1292
            {
 
1293
                /* we already have an end label - generate a jump to it */
 
1294
                G_cg->write_op(OPC_JMP);
 
1295
                G_cs->write_ofs2(end_lbl, 0);
 
1296
            }
 
1297
        }
 
1298
    }
 
1299
 
 
1300
    /* 
 
1301
     *   Restore the enclosing statement.  We enclose the protected code
 
1302
     *   and all of the 'catch' blocks, because all of these must leave
 
1303
     *   through the 'finally' handler.  We do not, however, enclose the
 
1304
     *   'finally' handler itself - once it's entered, we do not invoke it
 
1305
     *   again as it leaves.  
 
1306
     */
 
1307
    G_cs->set_enclosing(old_enclosing);
 
1308
 
 
1309
    /* generate the 'finally' block, if we have one */
 
1310
    if (finally_stm_ != 0)
 
1311
    {
 
1312
        /* 
 
1313
         *   Generate the 'finally' code.  The 'finally' block is executed
 
1314
         *   for the 'try' block plus all of the 'catch' blocks, so the
 
1315
         *   ending offset is the current position (less one byte, since
 
1316
         *   the range is inclusive), which encompasses all of the 'catch'
 
1317
         *   blocks 
 
1318
         */
 
1319
        finally_stm_->gen_code_finally(start_ofs, G_cs->get_ofs() - 1, this);
 
1320
    }
 
1321
 
 
1322
    /* 
 
1323
     *   we're now past all of the "catch" and "finally" blocks, so we can
 
1324
     *   define the jump label for jumping past those blocks (we make this
 
1325
     *   jump from the end of the protected code) - note that we might not
 
1326
     *   have actually generated the label, since we might never have
 
1327
     *   reached any code which jumped to it 
 
1328
     */
 
1329
    if (end_lbl != 0)
 
1330
        def_label_pos(end_lbl);
 
1331
}
 
1332
 
 
1333
/*
 
1334
 *   Generate code for a 'break' within our protected code or a 'catch'.
 
1335
 *   We'll first generate a call to our 'finally' block, if we have one,
 
1336
 *   then let the enclosing statement handle the break.  
 
1337
 */
 
1338
int CTPNStmTry::gen_code_break(const textchar_t *lbl, size_t lbl_len)
 
1339
{
 
1340
    /* if we have a 'finally' block, invoke it as a local subroutine call */
 
1341
    gen_jsr_finally();
 
1342
 
 
1343
    /* 
 
1344
     *   If there's an enclosing statement, let it generate the break; if
 
1345
     *   there's not, return failure, because we're not a meaningful
 
1346
     *   target for break.
 
1347
     */
 
1348
    if (enclosing_)
 
1349
        return enclosing_->gen_code_break(lbl, lbl_len);
 
1350
    else
 
1351
        return FALSE;
 
1352
}
 
1353
 
 
1354
/*
 
1355
 *   Generate code for a 'break' within our protected code or a 'catch'.
 
1356
 *   We'll first generate a call to our 'finally' block, if we have one,
 
1357
 *   then let the enclosing statement handle the break.  
 
1358
 */
 
1359
int CTPNStmTry::gen_code_continue(const textchar_t *lbl, size_t lbl_len)
 
1360
{
 
1361
    /* if we have a 'finally' block, invoke it as a local subroutine call */
 
1362
    gen_jsr_finally();
 
1363
 
 
1364
    /* 
 
1365
     *   if there's an enclosing statement, let it generate the continue;
 
1366
     *   if there's not, return failure, because we're not a meaningful
 
1367
     *   target for continue
 
1368
     */
 
1369
    if (enclosing_)
 
1370
        return enclosing_->gen_code_continue(lbl, lbl_len);
 
1371
    else
 
1372
        return FALSE;
 
1373
}
 
1374
 
 
1375
/*
 
1376
 *   Generate a local subroutine call to our 'finally' block, if we have
 
1377
 *   one.  This should be used when executing a break, continue, goto, or
 
1378
 *   return out of the protected code or a 'catch' block, or when merely
 
1379
 *   falling off the end of the protected code or 'catch' block. 
 
1380
 */
 
1381
void CTPNStmTry::gen_jsr_finally()
 
1382
{
 
1383
    /* if we have a 'finally', call it */
 
1384
    if (finally_lbl_ != 0)
 
1385
    {
 
1386
        /* generate the local subroutine call */
 
1387
        G_cg->write_op(OPC_LJSR);
 
1388
        G_cs->write_ofs2(finally_lbl_, 0);
 
1389
 
 
1390
        /* 
 
1391
         *   the LJSR pushes a value, which is then immediately popped (we
 
1392
         *   must note the push and pop because it affects our maximum
 
1393
         *   stack depth requirement) 
 
1394
         */
 
1395
        G_cg->note_push();
 
1396
        G_cg->note_pop();
 
1397
 
 
1398
        /* 
 
1399
         *   whatever follows the LJSR is logically at the end of the
 
1400
         *   'finally' block 
 
1401
         */
 
1402
        add_debug_line_rec(finally_stm_->get_end_desc(),
 
1403
                           finally_stm_->get_end_linenum());
 
1404
    }
 
1405
}
 
1406
 
 
1407
 
 
1408
/* ------------------------------------------------------------------------ */
 
1409
/*
 
1410
 *   'catch'
 
1411
 */
 
1412
 
 
1413
/*
 
1414
 *   generate code 
 
1415
 */
 
1416
void CTPNStmCatch::gen_code(int, int)
 
1417
{
 
1418
    /* this can't be called directly - use gen_code_catch() instead */
 
1419
    G_tok->throw_internal_error(TCERR_CATCH_FINALLY_GEN_CODE);
 
1420
}
 
1421
 
 
1422
/*
 
1423
 *   generate code for the 'catch' 
 
1424
 */
 
1425
void CTPNStmCatch::gen_code_catch(ulong start_prot_ofs, ulong end_prot_ofs)
 
1426
{
 
1427
    CTcSymbol *sym;
 
1428
    ulong exc_obj_id;
 
1429
    CTcPrsSymtab *old_frame;
 
1430
 
 
1431
    /* add the source location of the 'catch' clause */
 
1432
    add_debug_line_rec();
 
1433
 
 
1434
    /* set the local scope */
 
1435
    old_frame = G_cs->set_local_frame(symtab_);
 
1436
 
 
1437
    /* look up the object defining the class of exceptions to catch */
 
1438
    sym = G_cs->get_symtab()->find_or_def_undef(exc_class_,
 
1439
                                                exc_class_len_, FALSE);
 
1440
 
 
1441
    /* assume we won't find a valid object ID */
 
1442
    exc_obj_id = VM_INVALID_OBJ;
 
1443
 
 
1444
    /* if it's an object, get its ID */
 
1445
    if (sym->get_type() == TC_SYM_OBJ)
 
1446
    {
 
1447
        /* get its object ID */
 
1448
        exc_obj_id = ((CTcSymObj *)sym)->get_obj_id();
 
1449
    }
 
1450
    else if (sym->get_type() != TC_SYM_UNKNOWN)
 
1451
    {
 
1452
        /* 
 
1453
         *   it's defined, but it's not an object - log an error (note
 
1454
         *   that we don't log an error if the symbol is undefined,
 
1455
         *   because find_or_def_undef() will already have logged an error
 
1456
         *   for us) 
 
1457
         */
 
1458
        log_error(TCERR_CATCH_EXC_NOT_OBJ, (int)exc_class_len_, exc_class_);
 
1459
    }
 
1460
 
 
1461
    /* add our exception table entry */
 
1462
    G_cg->get_exc_table()->add_catch(start_prot_ofs, end_prot_ofs,
 
1463
                                     exc_obj_id, G_cs->get_ofs());
 
1464
 
 
1465
    /* don't allow any peephole optimizations to affect this offset */
 
1466
    G_cg->clear_peephole();
 
1467
 
 
1468
    /* 
 
1469
     *   the VM automatically pushes a value onto the stack to perform the
 
1470
     *   'catch' 
 
1471
     */
 
1472
    G_cg->note_push();
 
1473
 
 
1474
    /* 
 
1475
     *   generate a SETLCL for our formal parameter, so that the exception
 
1476
     *   object is stored in our local variable
 
1477
     */
 
1478
    exc_var_->gen_code_setlcl();
 
1479
    
 
1480
    /* generate code for our statement, if we have one */
 
1481
    if (body_ != 0)
 
1482
        gen_code_substm(body_);
 
1483
 
 
1484
    /* restore the enclosing local scope */
 
1485
    G_cs->set_local_frame(old_frame);
 
1486
}
 
1487
 
 
1488
/* ------------------------------------------------------------------------ */
 
1489
/*
 
1490
 *   'finally' 
 
1491
 */
 
1492
 
 
1493
/*
 
1494
 *   generate code 
 
1495
 */
 
1496
void CTPNStmFinally::gen_code(int, int)
 
1497
{
 
1498
    /* this can't be called directly - use gen_code_finally() instead */
 
1499
    G_tok->throw_internal_error(TCERR_CATCH_FINALLY_GEN_CODE);
 
1500
}
 
1501
 
 
1502
 
 
1503
/*
 
1504
 *   generate code for the 'finally' 
 
1505
 */
 
1506
void CTPNStmFinally::gen_code_finally(ulong start_prot_ofs,
 
1507
                                      ulong end_prot_ofs,
 
1508
                                      CTPNStmTry *try_stm)
 
1509
{
 
1510
    CTPNStmEnclosing *old_enclosing;
 
1511
 
 
1512
    /* 
 
1513
     *   set the source location for our prolog code to the 'finally'
 
1514
     *   clause's start 
 
1515
     */
 
1516
    add_debug_line_rec();
 
1517
 
 
1518
    /* push the enclosing statement */
 
1519
    old_enclosing = G_cs->set_enclosing(this);
 
1520
 
 
1521
    /* 
 
1522
     *   add our exception table entry - use the invalid object ID as a
 
1523
     *   special flag to indicate that we catch all exceptions 
 
1524
     */
 
1525
    G_cg->get_exc_table()->add_catch(start_prot_ofs, end_prot_ofs,
 
1526
                                     VM_INVALID_OBJ, G_cs->get_ofs());
 
1527
 
 
1528
    /* don't allow any peephole optimizations to affect this offset */
 
1529
    G_cg->clear_peephole();
 
1530
 
 
1531
    /* the VM pushes the exception onto the stack before calling us */
 
1532
    G_cg->note_push();
 
1533
 
 
1534
    /*
 
1535
     *   When we get called due to an exception, we want to run the
 
1536
     *   'finally' code block and then re-throw the exception.  First, store
 
1537
     *   the exception parameter in our special local stack slot that we
 
1538
     *   allocated specifically for the purpose of being a temporary holder
 
1539
     *   for this value.  
 
1540
     */
 
1541
    CTcSymLocal::s_gen_code_setlcl_stk(exc_local_id_, FALSE);
 
1542
 
 
1543
    /* call the 'finally' block */
 
1544
    try_stm->gen_jsr_finally();
 
1545
 
 
1546
    /*
 
1547
     *   After the 'finally' block returns, we must re-throw the
 
1548
     *   exception.  Retrieve the contents of our local where we stashed
 
1549
     *   the exception object and re-throw the exception.  
 
1550
     */
 
1551
    CTcSymLocal::s_gen_code_getlcl(exc_local_id_, FALSE);
 
1552
 
 
1553
    /* re-throw the exception - this pops the exception object */
 
1554
    G_cg->write_op(OPC_THROW);
 
1555
    G_cg->note_pop();
 
1556
 
 
1557
    /* 
 
1558
     *   set the source location to the 'finally' clause once again, since
 
1559
     *   we changed the source location in the course of generating the
 
1560
     *   catch-all handler 
 
1561
     */
 
1562
    add_debug_line_rec();
 
1563
 
 
1564
    /* this is where the 'finally' code block begins - define our label */
 
1565
    def_label_pos(try_stm->get_finally_lbl());
 
1566
 
 
1567
    /*
 
1568
     *   The 'finally' block is the target of LJSR instructions, since we
 
1569
     *   must run the 'finally' block's code from numerous code paths.
 
1570
     *   The first thing we must do is pop the return address and stash it
 
1571
     *   in a local variable.  (We note an extra push, since the LJSR will
 
1572
     *   have pushed the value before transferring control here.)
 
1573
     */
 
1574
    G_cg->note_push();
 
1575
    CTcSymLocal::s_gen_code_setlcl_stk(jsr_local_id_, FALSE);
 
1576
    
 
1577
    /* generate the code block, if there is one */
 
1578
    if (body_ != 0)
 
1579
        gen_code_substm(body_);
 
1580
 
 
1581
    /* return from the 'finally' subroutine */
 
1582
    G_cg->write_op(OPC_LRET);
 
1583
    G_cs->write2(jsr_local_id_);
 
1584
 
 
1585
    /* restore the enclosing statement */
 
1586
    G_cs->set_enclosing(old_enclosing);
 
1587
}
 
1588
 
 
1589
/*
 
1590
 *   It is not legal to enter a 'finally' block via a 'goto' statement,
 
1591
 *   because there is no valid way to exit the 'finally' block in this
 
1592
 *   case.  
 
1593
 */
 
1594
int CTPNStmFinally::check_enter_by_goto(CTPNStmGoto *goto_stm,
 
1595
                                        CTPNStmLabel *)
 
1596
{
 
1597
    /* this is illegal - log an error */
 
1598
    goto_stm->log_error(TCERR_GOTO_INTO_FINALLY);
 
1599
 
 
1600
    /* indicate that it's not allowed */
 
1601
    return FALSE;
 
1602
}
 
1603
 
 
1604
 
 
1605
/* ------------------------------------------------------------------------ */
 
1606
/*
 
1607
 *   'throw' statement 
 
1608
 */
 
1609
 
 
1610
/*
 
1611
 *   generate code 
 
1612
 */
 
1613
void CTPNStmThrow::gen_code(int, int)
 
1614
{
 
1615
    /* add a line record */
 
1616
    add_debug_line_rec();
 
1617
 
 
1618
    /* 
 
1619
     *   generate our expression - we use the result (discard = false),
 
1620
     *   and we are effectively assigning the result, so we can't use the
 
1621
     *   'for condition' rules 
 
1622
     */
 
1623
    expr_->gen_code(FALSE, FALSE);
 
1624
 
 
1625
    /* generate the 'throw' */
 
1626
    G_cg->write_op(OPC_THROW);
 
1627
 
 
1628
    /* 'throw' pops the expression from the stack */
 
1629
    G_cg->note_pop();
 
1630
}
 
1631
 
 
1632
/* ------------------------------------------------------------------------ */
 
1633
/*
 
1634
 *   'goto' statement 
 
1635
 */
 
1636
 
 
1637
/*
 
1638
 *   generate code 
 
1639
 */
 
1640
void CTPNStmGoto::gen_code(int, int)
 
1641
{
 
1642
    CTcSymbol *sym;
 
1643
    CTPNStmLabel *label_stm;
 
1644
    
 
1645
    /* add a line record */
 
1646
    add_debug_line_rec();
 
1647
 
 
1648
    /* 
 
1649
     *   look up our label symbol in the 'goto' table for the function,
 
1650
     *   and get the label statement node from the label 
 
1651
     */
 
1652
    if (G_cs->get_goto_symtab() == 0
 
1653
        || (sym = G_cs->get_goto_symtab()->find(lbl_, lbl_len_)) == 0
 
1654
        || sym->get_type() != TC_SYM_LABEL
 
1655
        || (label_stm = ((CTcSymLabel *)sym)->get_stm()) == 0)
 
1656
    {
 
1657
        /* log an error */
 
1658
        log_error(TCERR_INVALID_GOTO_LBL, (int)lbl_len_, lbl_);
 
1659
 
 
1660
        /* give up */
 
1661
        return;
 
1662
    }
 
1663
 
 
1664
    /*
 
1665
     *   Tell any enclosing statements to unwind their 'try' blocks for a
 
1666
     *   transfer to the given label.  We only need to go as far as the
 
1667
     *   most deeply nested enclosing statement we have in common with the
 
1668
     *   label, because we'll be transferring control entirely within the
 
1669
     *   confines of that enclosing statement.  
 
1670
     */
 
1671
    if (G_cs->get_enclosing() != 0)
 
1672
    {
 
1673
        /* generate the unwinding code */
 
1674
        G_cs->get_enclosing()->gen_code_unwind_for_goto(this, label_stm);
 
1675
    }
 
1676
    else
 
1677
    {
 
1678
        CTPNStmEnclosing *enc;
 
1679
        
 
1680
        /*
 
1681
         *   The 'goto' isn't enclosed in any statements.  This means that
 
1682
         *   we are entering every block that contains the target label.
 
1683
         *   Some blocks don't allow entering via 'goto', so we must check
 
1684
         *   at this point to see if any of the enclosing blocks are
 
1685
         *   problematic. 
 
1686
         */
 
1687
        for (enc = label_stm ; enc != 0 ; enc = enc->get_enclosing())
 
1688
        {
 
1689
            /* 
 
1690
             *   make sure we're allowed to enter this statement - if not,
 
1691
             *   stop scanning, so that we display only one such error 
 
1692
             */
 
1693
            if (!enc->check_enter_by_goto(this, label_stm))
 
1694
                break;
 
1695
        }
 
1696
    }
 
1697
 
 
1698
    /* generate a jump to the label */
 
1699
    G_cg->write_op(OPC_JMP);
 
1700
    G_cs->write_ofs2(label_stm->get_goto_label(), 0);
 
1701
}
 
1702
 
 
1703
 
 
1704
/* ------------------------------------------------------------------------ */
 
1705
/*
 
1706
 *   Generic enclosing statement node 
 
1707
 */
 
1708
 
 
1709
/*
 
1710
 *   Generate code for a break, given the target code label object and the
 
1711
 *   target label symbol, if any.  This can be used for any of the looping
 
1712
 *   statement types.  
 
1713
 */
 
1714
int CTPNStmEnclosing::gen_code_break_loop(CTcCodeLabel *code_label,
 
1715
                                          const textchar_t *lbl,
 
1716
                                          size_t lbl_len)
 
1717
{
 
1718
    /* 
 
1719
     *   If the statement is labeled, let the enclosing statement handle
 
1720
     *   it -- since it's labeled, we can't assume the statement refers to
 
1721
     *   us without searching for the enclosing label.  
 
1722
     */
 
1723
    if (lbl != 0)
 
1724
    {
 
1725
        /* if there's an enclosing statement, let it handle it */
 
1726
        if (enclosing_ != 0)
 
1727
            return enclosing_->gen_code_break(lbl, lbl_len);
 
1728
 
 
1729
        /* 
 
1730
         *   there's no enclosing statement, and we can't handle this
 
1731
         *   because it has an explicit label attached - indicate that no
 
1732
         *   break has been generated and give up 
 
1733
         */
 
1734
        return FALSE;
 
1735
    }
 
1736
 
 
1737
    /* 
 
1738
     *   It's unlabeled, so we can take it by default as the nearest
 
1739
     *   enclosing statement for which 'break' makes sense -- generate the
 
1740
     *   jump to the given code label.  
 
1741
     */
 
1742
    G_cg->write_op(OPC_JMP);
 
1743
    G_cs->write_ofs2(code_label, 0);
 
1744
 
 
1745
    /* we have generated the break */
 
1746
    return TRUE;
 
1747
}
 
1748
 
 
1749
/*
 
1750
 *   Generate code for a continue, given the target code label object and
 
1751
 *   the target label symbol, if any.  This can be used for any of the
 
1752
 *   looping statement types.  
 
1753
 */
 
1754
int CTPNStmEnclosing::gen_code_continue_loop(CTcCodeLabel *code_label,
 
1755
                                             const textchar_t *lbl,
 
1756
                                             size_t lbl_len)
 
1757
{
 
1758
    /* 
 
1759
     *   If the statement is labeled, let the enclosing statement handle
 
1760
     *   it -- since it's labeled, we can't assume the statement refers to
 
1761
     *   us without searching for the enclosing label.  
 
1762
     */
 
1763
    if (lbl != 0)
 
1764
    {
 
1765
        /* if there's an enclosing statement, let it handle it */
 
1766
        if (enclosing_ != 0)
 
1767
            return enclosing_->gen_code_continue(lbl, lbl_len);
 
1768
 
 
1769
        /* 
 
1770
         *   there's no enclosing statement, and we can't handle this
 
1771
         *   because it has an explicit label attached - indicate that no
 
1772
         *   continue has been generated and give up 
 
1773
         */
 
1774
        return FALSE;
 
1775
    }
 
1776
 
 
1777
    /* 
 
1778
     *   it's unlabeled, so we can take it by default as the nearest
 
1779
     *   enclosing statement for which 'continue' makes sense -- generate
 
1780
     *   the jump to the given code label 
 
1781
     */
 
1782
    G_cg->write_op(OPC_JMP);
 
1783
    G_cs->write_ofs2(code_label, 0);
 
1784
 
 
1785
    /* we have generated the continue */
 
1786
    return TRUE;
 
1787
}
 
1788
 
 
1789
/*
 
1790
 *   Generate the code necessary to unwind the stack for executing a
 
1791
 *   'goto' to the given labeled statement> 
 
1792
 */
 
1793
void CTPNStmEnclosing::gen_code_unwind_for_goto(CTPNStmGoto *goto_stm,
 
1794
                                                CTPNStmLabel *target)
 
1795
{
 
1796
    CTPNStmEnclosing *enc;
 
1797
    
 
1798
    /* 
 
1799
     *   Detmerine if the target statement is enclosed within this
 
1800
     *   statement.  If it is, we do not need to unwind from this
 
1801
     *   statement or any of its enclosing statements, because control
 
1802
     *   will remain within this statement.
 
1803
     *   
 
1804
     *   To make this determination, start at the target label and search
 
1805
     *   up its list of enclosing statements.  If we reach 'this', we know
 
1806
     *   that we enclose the target.  If we reach the outermost enclosing
 
1807
     *   statement, we know that we do not enclose the taret.  
 
1808
     */
 
1809
    for (enc = target ; enc != 0 ; enc = enc->get_enclosing())
 
1810
    {
 
1811
        /* 
 
1812
         *   if we found ourself in the list of enclosing statements
 
1813
         *   around the target label, the label is contained within us,
 
1814
         *   hence we do not need to generate any code to leave, because
 
1815
         *   we're not leaving - simply return immediately without looking
 
1816
         *   any further 
 
1817
         */
 
1818
        if (enc == this)
 
1819
        {
 
1820
            /*
 
1821
             *   'this' is the common ancestor of both the 'goto' and the
 
1822
             *   target label, so we're not transferring control in or out
 
1823
             *   of 'this' or any enclosing statement.  However, we are
 
1824
             *   transfering control IN through all of the statements that
 
1825
             *   enclose the label up to but not including 'this'.
 
1826
             *   
 
1827
             *   Some types of statements do not allow control transfers
 
1828
             *   in to enclosed labels - in particular, we can't use
 
1829
             *   'goto' to transfer control into a 'finally' clause.
 
1830
             *   Check all statements that enclose the label up to but not
 
1831
             *   including 'this', and make sure they will allow a
 
1832
             *   transfer in.
 
1833
             *   
 
1834
             *   Note that we make this check now, only after we've found
 
1835
             *   the common ancestor, because we can't tell if we're
 
1836
             *   actually entering any blocks until we find the common
 
1837
             *   ancestor.  
 
1838
             */
 
1839
            for (enc = target ; enc != 0 && enc != this ;
 
1840
                 enc = enc->get_enclosing())
 
1841
            {
 
1842
                /* make sure we're allowed to enter this statement */
 
1843
                if (!enc->check_enter_by_goto(goto_stm, target))
 
1844
                    break;
 
1845
            }
 
1846
 
 
1847
            /* 
 
1848
             *   we're not transferring out of this statement or any
 
1849
             *   enclosing statement, since the source 'goto' and the
 
1850
             *   target label are both contained within 'this' - we're
 
1851
             *   done unwinding for the transfer 
 
1852
             */
 
1853
            return;
 
1854
        }
 
1855
    }
 
1856
 
 
1857
    /* generate code to transfer out of this statement */
 
1858
    gen_code_for_transfer_out();
 
1859
 
 
1860
    /* check for an enclosing statement */
 
1861
    if (enclosing_ != 0)
 
1862
    {
 
1863
        /* 
 
1864
         *   We are enclosed by another statement or statements.  This
 
1865
         *   means that we haven't found a common ancestor yet, so we
 
1866
         *   might be leaving the enclosing block as well - continue on to
 
1867
         *   our enclosing statement.  
 
1868
         */
 
1869
        enclosing_->gen_code_unwind_for_goto(goto_stm, target);
 
1870
    }
 
1871
    else
 
1872
    {
 
1873
        /*
 
1874
         *   This is the outermost significant enclosing statement, which
 
1875
         *   means that we are transferring control into a completely
 
1876
         *   unrelated block.  As a result, we will enter every statement
 
1877
         *   that encloses the target label.
 
1878
         *   
 
1879
         *   We must check each block we're entering to see if it allows
 
1880
         *   entry by 'goto' statements.  Since we now know there is no
 
1881
         *   common ancestor, and thus that we're entering every block
 
1882
         *   enclosing the target label, we must check every block
 
1883
         *   enclosing the target label to see if they allow transfers in
 
1884
         *   via 'goto' statements.  
 
1885
         */
 
1886
        for (enc = target ; enc != 0 ; enc = enc->get_enclosing())
 
1887
        {
 
1888
            /* make sure we're allowed to enter this statement */
 
1889
            if (!enc->check_enter_by_goto(goto_stm, target))
 
1890
                break;
 
1891
        }
 
1892
    }
 
1893
}
 
1894
 
 
1895
/* ------------------------------------------------------------------------ */
 
1896
/*
 
1897
 *   Object Definition Statement
 
1898
 */
 
1899
 
 
1900
/*
 
1901
 *   given the offset of the start of an object in the compiled object
 
1902
 *   stream, get the offset of the first property 
 
1903
 */
 
1904
ulong CTPNStmObject::get_stream_first_prop_ofs(CTcDataStream *stream,
 
1905
                                               ulong obj_ofs)
 
1906
{
 
1907
    /* 
 
1908
     *   the property table follows the superclass table, which follows
 
1909
     *   the tads-object header; the superclass table contains 4 bytes per
 
1910
     *   superclass (we can obtain the superclass count from the stream
 
1911
     *   data) 
 
1912
     */
 
1913
    return obj_ofs + TCT3_TADSOBJ_SC_OFS
 
1914
        + (get_stream_sc_cnt(stream, obj_ofs) * 4);
 
1915
}
 
1916
 
 
1917
/*
 
1918
 *   given the offset of the start of an object in the compiled object
 
1919
 *   stream, get the offset of the property at the given index
 
1920
 */
 
1921
ulong CTPNStmObject::get_stream_prop_ofs(CTcDataStream *stream,
 
1922
                                         ulong obj_ofs, uint idx)
 
1923
{
 
1924
    /* 
 
1925
     *   calculate the offset to the selected property from the start of
 
1926
     *   the property table 
 
1927
     */
 
1928
    return get_stream_first_prop_ofs(stream, obj_ofs)
 
1929
        + (TCT3_TADSOBJ_PROP_SIZE * idx);
 
1930
}
 
1931
 
 
1932
/*
 
1933
 *   given the offset of the start of an object in the compiled object
 
1934
 *   stream, get the number of properties in the stream data 
 
1935
 */
 
1936
uint CTPNStmObject::get_stream_prop_cnt(CTcDataStream *stream,
 
1937
                                        ulong obj_ofs)
 
1938
{
 
1939
    /* the property count is at offset 2 in the tads-object header */
 
1940
    return stream->readu2_at(obj_ofs + TCT3_TADSOBJ_HEADER_OFS + 2);
 
1941
}
 
1942
 
 
1943
/*
 
1944
 *   given the offset of the start of an object in the compiled object
 
1945
 *   stream, set the number of properties in the stream data, adjusting
 
1946
 *   the data size in the metaclass header to match 
 
1947
 */
 
1948
size_t CTPNStmObject::set_stream_prop_cnt(CTcDataStream *stream,
 
1949
                                          ulong obj_ofs, uint prop_cnt)
 
1950
{
 
1951
    size_t data_size;
 
1952
    
 
1953
    /* the property count is at offset 2 in the tads-object header */
 
1954
    stream->write2_at(obj_ofs + TCT3_TADSOBJ_HEADER_OFS + 2, prop_cnt);
 
1955
 
 
1956
    /* 
 
1957
     *   calculate the new data size to store in the metaclass header --
 
1958
     *   this is the size of the tads-object header, plus the size of the
 
1959
     *   superclass table (4 bytes per superclass), plus the size of the
 
1960
     *   property table 
 
1961
     */
 
1962
    data_size = TCT3_TADSOBJ_HEADER_SIZE
 
1963
                + (get_stream_sc_cnt(stream, obj_ofs) * 4)
 
1964
                + (prop_cnt * TCT3_TADSOBJ_PROP_SIZE);
 
1965
 
 
1966
    /* write the data size to the metaclass header (it's at offset 4) */
 
1967
    stream->write2_at(obj_ofs + TCT3_META_HEADER_OFS + 4, data_size);
 
1968
 
 
1969
    /* return the new data size */
 
1970
    return data_size;
 
1971
}
 
1972
 
 
1973
/*
 
1974
 *   Get the object flags from an object in a compiled stream 
 
1975
 */
 
1976
uint CTPNStmObject::get_stream_obj_flags(CTcDataStream *stream,
 
1977
                                         ulong obj_ofs)
 
1978
{
 
1979
    /* the flags are at offset 4 in the tads-object header */
 
1980
    return stream->read2_at(obj_ofs + TCT3_TADSOBJ_HEADER_OFS + 4);
 
1981
}
 
1982
 
 
1983
/*
 
1984
 *   Set the object flags in an object in a compiled stream 
 
1985
 */
 
1986
void CTPNStmObject::set_stream_obj_flags(CTcDataStream *stream,
 
1987
                                         ulong obj_ofs, uint flags)
 
1988
{
 
1989
    /* 
 
1990
     *   write the new flags - they're at offset 4 in the tads-object
 
1991
     *   header 
 
1992
     */
 
1993
    stream->write2_at(obj_ofs + TCT3_TADSOBJ_HEADER_OFS + 4, flags);
 
1994
}
 
1995
 
 
1996
/*
 
1997
 *   given the offset of the start of an object in the compiled object
 
1998
 *   stream, get the number of superclasses in the stream data 
 
1999
 */
 
2000
uint CTPNStmObject::get_stream_sc_cnt(CTcDataStream *stream,
 
2001
                                      ulong obj_ofs)
 
2002
{
 
2003
    /* the superclass count is at offset 0 in the tads-object header */
 
2004
    return stream->readu2_at(obj_ofs + TCT3_TADSOBJ_HEADER_OFS + 0);
 
2005
}
 
2006
 
 
2007
/*
 
2008
 *   given the stream offset of the start of an object in the compiled
 
2009
 *   object stream, change a superclass object ID 
 
2010
 */
 
2011
void CTPNStmObject::set_stream_sc(CTcDataStream *stream, ulong obj_ofs,
 
2012
                                  uint sc_idx, vm_obj_id_t new_sc)
 
2013
{
 
2014
    /* 
 
2015
     *   set the superclass - it's at offset 6 in the object data, plus
 
2016
     *   four bytes (UINT4) per index slot 
 
2017
     */
 
2018
    stream->write2_at(obj_ofs + TCT3_TADSOBJ_HEADER_OFS + 6 + (sc_idx * 4),
 
2019
                      new_sc);
 
2020
}
 
2021
 
 
2022
/*
 
2023
 *   given the offset of the start of an object in the compiled object
 
2024
 *   stream, get the property ID of the property at a given index in the
 
2025
 *   property table 
 
2026
 */
 
2027
vm_prop_id_t CTPNStmObject::get_stream_prop_id(CTcDataStream *stream,
 
2028
                                               ulong obj_ofs, uint prop_idx)
 
2029
{
 
2030
    ulong prop_ofs;
 
2031
    
 
2032
    /* get the property's data offset */
 
2033
    prop_ofs = get_stream_prop_ofs(stream, obj_ofs, prop_idx);
 
2034
 
 
2035
    /* read the property ID - it's at offset 0 in the property data */
 
2036
    return (vm_prop_id_t)stream->readu2_at(prop_ofs);
 
2037
}
 
2038
 
 
2039
/*
 
2040
 *   given the offset of the start of an object in the compiled object
 
2041
 *   stream, get the datatype of the property at the given index in the
 
2042
 *   property table 
 
2043
 */
 
2044
vm_datatype_t CTPNStmObject::
 
2045
   get_stream_prop_type(CTcDataStream *stream, ulong obj_ofs, uint prop_idx)
 
2046
{
 
2047
    ulong dh_ofs;
 
2048
 
 
2049
    /* get the property's data holder offset */
 
2050
    dh_ofs = get_stream_prop_val_ofs(stream, obj_ofs, prop_idx);
 
2051
    
 
2052
    /* the type is the first byte of the serialized data holder */
 
2053
    return (vm_datatype_t)stream->get_byte_at(dh_ofs);
 
2054
}
 
2055
 
 
2056
/*
 
2057
 *   given the offset of the start of an object in the compiled object
 
2058
 *   stream, get the stream offset of the serialized DATAHOLDER structure
 
2059
 *   for the property at the given index in the property table 
 
2060
 */
 
2061
ulong CTPNStmObject::get_stream_prop_val_ofs(CTcDataStream *stream,
 
2062
                                             ulong obj_ofs, uint prop_idx)
 
2063
{
 
2064
    ulong prop_ofs;
 
2065
 
 
2066
    /* get the property's data offset */
 
2067
    prop_ofs = get_stream_prop_ofs(stream, obj_ofs, prop_idx);
 
2068
 
 
2069
    /* the data holder immediately follows the (UINT2) property ID */
 
2070
    return prop_ofs + 2;
 
2071
}
 
2072
 
 
2073
 
 
2074
/*
 
2075
 *   given the offset of the start of an object in the compiled object
 
2076
 *   stream, get the property ID of the property at a given index in the
 
2077
 *   property table 
 
2078
 */
 
2079
void CTPNStmObject::set_stream_prop_id(CTcDataStream *stream,
 
2080
                                       ulong obj_ofs, uint prop_idx,
 
2081
                                       vm_prop_id_t new_id)
 
2082
{
 
2083
    ulong prop_ofs;
 
2084
 
 
2085
    /* get the property's data offset */
 
2086
    prop_ofs = get_stream_prop_ofs(stream, obj_ofs, prop_idx);
 
2087
 
 
2088
    /* set the data */
 
2089
    stream->write2_at(prop_ofs, (uint)new_id);
 
2090
}
 
2091
 
 
2092
/* ------------------------------------------------------------------------ */
 
2093
/*
 
2094
 *   Object Property list entry - value node
 
2095
 */
 
2096
void CTPNObjProp::gen_code(int, int)
 
2097
{
 
2098
    vm_val_t val;
 
2099
    char buf[VMB_DATAHOLDER];
 
2100
    CTcDataStream *str;
 
2101
 
 
2102
    /* get the correct data stream */
 
2103
    str = obj_stm_->get_obj_sym()->get_stream();
 
2104
 
 
2105
    /* set the current source location for error reporting */
 
2106
    G_tok->set_line_info(file_, linenum_);
 
2107
 
 
2108
    /* generate code for our expression or our code body, as appropriate */
 
2109
    if (expr_ != 0)
 
2110
    {
 
2111
        /* 
 
2112
         *   if my value is constant, write out a dataholder for the
 
2113
         *   constant value to the stream; otherwise, write out our code
 
2114
         *   and store a pointer to the code 
 
2115
         */
 
2116
        if (expr_->is_const())
 
2117
        {
 
2118
            /* write the constant value to the object stream */
 
2119
            G_cg->write_const_as_dh(str, str->get_ofs(),
 
2120
                                    expr_->get_const_val());
 
2121
        }
 
2122
        else if (expr_->is_dstring())
 
2123
        {
 
2124
            CTPNDstr *dstr;
 
2125
            
 
2126
            /* it's a double-quoted string node */
 
2127
            dstr = (CTPNDstr *)expr_;
 
2128
            
 
2129
            /* 
 
2130
             *   Add the string to the constant pool.  Note that the fixup
 
2131
             *   will be one byte from the current object stream offset,
 
2132
             *   since we need to write the type byte first.  
 
2133
             */
 
2134
            G_cg->add_const_str(dstr->get_str(), dstr->get_str_len(),
 
2135
                                str, str->get_ofs() + 1);
 
2136
 
 
2137
            /* 
 
2138
             *   Set up the dstring value.  Use a zero placeholder for
 
2139
             *   now; add_const_str() already added a fixup for us that
 
2140
             *   will supply the correct value at link time.  
 
2141
             */
 
2142
            val.set_dstring(0);
 
2143
            vmb_put_dh(buf, &val);
 
2144
            str->write(buf, VMB_DATAHOLDER);
 
2145
        }
 
2146
        else
 
2147
        {
 
2148
            /* we should never get here */
 
2149
            G_tok->throw_internal_error(TCERR_INVAL_PROP_CODE_GEN);
 
2150
        }
 
2151
    }
 
2152
    else if (code_body_ != 0)
 
2153
    {
 
2154
        char buf[VMB_DATAHOLDER];
 
2155
        vm_val_t val;
 
2156
 
 
2157
        /* if this is a constructor, mark the code body accordingly */
 
2158
        if (prop_sym_->get_prop() == G_prs->get_constructor_prop())
 
2159
            code_body_->set_constructor(TRUE);
 
2160
 
 
2161
        /* if it's static, do some extra work */
 
2162
        if (is_static_)
 
2163
        {
 
2164
            /* mark the code body as static */
 
2165
            code_body_->set_static();
 
2166
 
 
2167
            /* 
 
2168
             *   add the obj.prop to the static ID stream, so the VM knows
 
2169
             *   to invoke this initializer at start-up 
 
2170
             */
 
2171
            G_static_init_id_stream
 
2172
                ->write_obj_id(obj_stm_->get_obj_sym()->get_obj_id());
 
2173
            G_static_init_id_stream
 
2174
                ->write_prop_id(prop_sym_->get_prop());
 
2175
        }
 
2176
        
 
2177
        /* tell our code body to generate the code */
 
2178
        code_body_->gen_code(FALSE, FALSE);
 
2179
        
 
2180
        /* 
 
2181
         *   Set up our code offset value.  Write a code offset of zero
 
2182
         *   for now, since we won't know the correct offset until link
 
2183
         *   time.  
 
2184
         */
 
2185
        val.set_codeofs(0);
 
2186
        
 
2187
        /* 
 
2188
         *   Add a fixup to the code body's fixup list for our dataholder,
 
2189
         *   so that we fix up the property value when we link.  Note that
 
2190
         *   the fixup is one byte into our object stream from the current
 
2191
         *   offset, because the first byte is the type.  
 
2192
         */
 
2193
        CTcAbsFixup::add_abs_fixup(code_body_->get_fixup_list_head(),
 
2194
                                   str, str->get_ofs() + 1);
 
2195
        
 
2196
        /* write out our value in DATAHOLDER format */
 
2197
        vmb_put_dh(buf, &val);
 
2198
        str->write(buf, VMB_DATAHOLDER);
 
2199
    }
 
2200
}
 
2201
 
 
2202
/*
 
2203
 *   Check locals 
 
2204
 */
 
2205
void CTPNObjProp::check_locals()
 
2206
{
 
2207
    /* check locals in our code body */
 
2208
    if (code_body_ != 0)
 
2209
        code_body_->check_locals();
 
2210
}
 
2211
 
 
2212
/* ------------------------------------------------------------------------ */
 
2213
/*
 
2214
 *   Implicit constructor 
 
2215
 */
 
2216
void CTPNStmImplicitCtor::gen_code(int /*discard*/, int /*for_condition*/)
 
2217
{
 
2218
    CTPNSuperclass *sc;
 
2219
 
 
2220
    /* 
 
2221
     *   Generate a call to inherit each superclass constructor.  Pass the
 
2222
     *   same argument list we received by expanding the varargs list
 
2223
     *   parameter in local 0.  
 
2224
     */
 
2225
    for (sc = obj_stm_->get_first_sc() ; sc != 0 ; sc = sc->nxt_)
 
2226
    {
 
2227
        CTcSymObj *sc_sym;
 
2228
 
 
2229
        /* 
 
2230
         *   if this one is valid, generate code to call its constructor -
 
2231
         *   it's valid if it has an object symbol 
 
2232
         */
 
2233
        sc_sym = (CTcSymObj *)sc->get_sym();
 
2234
        if (sc_sym != 0 && sc_sym->get_type() == TC_SYM_OBJ)
 
2235
        {
 
2236
            /* push the argument counter so far (no other arguments) */
 
2237
            G_cg->write_op(OPC_PUSH_0);
 
2238
            G_cg->note_push();
 
2239
 
 
2240
            /* get the varargs list local */
 
2241
            CTcSymLocal::s_gen_code_getlcl(0, FALSE);
 
2242
 
 
2243
            /* convert it to varargs */
 
2244
            G_cg->write_op(OPC_MAKELSTPAR);
 
2245
 
 
2246
            /* note the extra push and pop for the argument count */
 
2247
            G_cg->note_push();
 
2248
            G_cg->note_pop();
 
2249
 
 
2250
            /* it's a varargs call */
 
2251
            G_cg->write_op(OPC_VARARGC);
 
2252
 
 
2253
            /* generate an EXPINHERIT to this superclass */
 
2254
            G_cg->write_op(OPC_EXPINHERIT);
 
2255
            G_cs->write(0);                      /* varargs -> argc ignored */
 
2256
            G_cs->write_prop_id(G_prs->get_constructor_prop());
 
2257
            G_cs->write_obj_id(sc_sym->get_obj_id());
 
2258
 
 
2259
            /* 
 
2260
             *   this removes arguments (the varargs list variable and
 
2261
             *   argument count) 
 
2262
             */
 
2263
            G_cg->note_pop(2);
 
2264
        }
 
2265
    }
 
2266
}
 
2267
 
 
2268
 
 
2269
/* ------------------------------------------------------------------------ */
 
2270
/*
 
2271
 *   Anonymous function 
 
2272
 */
 
2273
 
 
2274
/*
 
2275
 *   generate code 
 
2276
 */
 
2277
void CTPNAnonFunc::gen_code(int discard, int)
 
2278
{
 
2279
    CTcCodeBodyCtx *cur_ctx;
 
2280
    int argc;
 
2281
 
 
2282
    /* if we're discarding the value, don't bother generating the code */
 
2283
    if (discard)
 
2284
        return;
 
2285
 
 
2286
    /* 
 
2287
     *   Push each context object - these are the additional arguments to
 
2288
     *   the anonymous function pointer object's constructor beyond the
 
2289
     *   function pointer itself.  Note that we must push the arguments in
 
2290
     *   reverse order of our list, since arguments are always pushed from
 
2291
     *   last to first.  
 
2292
     */
 
2293
    for (argc = 0, cur_ctx = code_body_->get_ctx_tail() ; cur_ctx != 0 ;
 
2294
         cur_ctx = cur_ctx->prv_, ++argc)
 
2295
    {
 
2296
        int our_varnum;
 
2297
 
 
2298
        /* 
 
2299
         *   find our context matching this context - the caller's
 
2300
         *   contexts are all one level lower than the callee's contexts,
 
2301
         *   because the caller is at the next recursion level out 
 
2302
         */
 
2303
        if (!G_cs->get_code_body()
 
2304
            ->get_ctx_var_for_level(cur_ctx->level_ - 1, &our_varnum))
 
2305
        {
 
2306
            /* this should never happen */
 
2307
            assert(FALSE);
 
2308
        }
 
2309
 
 
2310
        /* 
 
2311
         *   push this context object - to do this, simply retrieve the
 
2312
         *   value of the local variable in our frame that contains this
 
2313
         *   context level 
 
2314
         */
 
2315
        CTcSymLocal::s_gen_code_getlcl(our_varnum, FALSE);
 
2316
    }
 
2317
 
 
2318
    /* 
 
2319
     *   The first argument (and thus last pushed) to the constructor is
 
2320
     *   the constant function pointer that refers to the code of the
 
2321
     *   anonymous function. 
 
2322
     */
 
2323
    G_cg->write_op(OPC_PUSHFNPTR);
 
2324
    code_body_->add_abs_fixup(G_cs);
 
2325
    G_cs->write4(0);
 
2326
    ++argc;
 
2327
 
 
2328
    /* note the push of the function pointer argument */
 
2329
    G_cg->note_push();
 
2330
 
 
2331
    /* create the new function object */
 
2332
    if (argc <= 255)
 
2333
    {
 
2334
        G_cg->write_op(OPC_NEW1);
 
2335
        G_cs->write((char)argc);
 
2336
        G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_ANONFN));
 
2337
    }
 
2338
    else
 
2339
    {
 
2340
        G_cg->write_op(OPC_NEW2);
 
2341
        G_cs->write2(argc);
 
2342
        G_cs->write2(G_cg->get_predef_meta_idx(TCT3_METAID_ANONFN));
 
2343
    }
 
2344
 
 
2345
    /* push the object value */
 
2346
    G_cg->write_op(OPC_GETR0);
 
2347
 
 
2348
    /* the 'new' popped the arguments, then we pushed the result */
 
2349
    G_cg->note_pop(argc - 1);
 
2350
}
 
2351