cfad47cfa3/tads3/vmrun.cpp

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#ifdef RCSID
2
static char RCSid[] =
3
"$Header: d:/cvsroot/tads/tads3/VMRUN.CPP,v 1.4 1999/07/11 00:46:58 MJRoberts Exp $";
4
#endif
5
6
/* 
7
 *   Copyright (c) 1998, 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
  vmrun.cpp - VM Execution
15
Function
16
  
17
Notes
18
  
19
Modified
20
  11/12/98 MJRoberts  - Creation
21
*/
22
23
#include <stdio.h>
24
25
#include "t3std.h"
26
#include "os.h"
27
#include "vmrun.h"
28
#include "vmdbg.h"
29
#include "vmop.h"
30
#include "vmstack.h"
31
#include "vmerr.h"
32
#include "vmerrnum.h"
33
#include "vmobj.h"
34
#include "vmlst.h"
35
#include "vmstr.h"
36
#include "vmtobj.h"
37
#include "vmfunc.h"
38
#include "vmmeta.h"
39
#include "vmbif.h"
40
#include "vmpredef.h"
41
#include "vmfile.h"
42
#include "vmsave.h"
43
#include "vmprof.h"
44
#include "vmhash.h"
45
46
47
/* ------------------------------------------------------------------------ */
48
/*
49
 *   Initialize 
50
 */
51
CVmRun::CVmRun()
52
{
53
    init();
54
}
55
56
void CVmRun::init()
57
{
58
    /* start out with 'nil' in R0 */
59
    r0_.set_nil();
60
61
    /* there's no frame yet */
62
    frame_ptr_ = 0;
63
64
    /* there's no entry pointer yet */
65
    entry_ptr_ = 0;
66
    entry_ptr_native_ = 0;
67
68
    /* function header size is not yet known */
69
    funchdr_size_ = 0;
70
71
    /* we have no 'say' function yet */
72
    say_func_ = 0;
73
74
    /* no default 'say' method */
75
    say_method_ = VM_INVALID_PROP;
76
77
    /* no debugger halt requested yet */
78
    halt_vm_ = FALSE;
79
80
    /* we have no program counter yet */
81
    pc_ptr_ = 0;
82
83
    /*
84
     *   If we're including the profiler in the build, allocate and
85
     *   initialize its memory structures. 
86
     */
87
#ifdef VM_PROFILER
88
89
    /* 
90
     *   Allocate the profiler stack.  This stack will contain one record per
91
     *   activation frame in the regular VM stack.  
92
     */
93
    prof_stack_max_ = 250;
94
    (prof_stack_ = (vm_profiler_rec *)
95
                   t3malloc(prof_stack_max_ * sizeof(prof_stack_[0])));
96
97
    /* we don't have anything on the profiler stack yet */
98
    prof_stack_idx_ = 0;
99
100
    /* create the profiler master hash table */
101
    prof_master_table_ = new CVmHashTable(512, new CVmHashFuncCI(), TRUE);
102
103
    /* we're not running the profiler yet */
104
    profiling_ = FALSE;
105
106
#endif /* VM_PROFILER */
107
}
108
109
/* ------------------------------------------------------------------------ */
110
/*
111
 *   Terminate 
112
 */
113
CVmRun::~CVmRun()
114
{
115
    terminate();
116
}
117
118
void CVmRun::terminate()
119
{
120
    /*
121
     *   If we're including the profiler in the build, delete its memory
122
     *   structures.  
123
     */
124
#ifdef VM_PROFILER
125
126
    /* delete the profiler stack */
127
    t3free(prof_stack_);
128
    prof_stack_ = 0;
129
130
    /* delete the profiler master hash table */
131
    delete prof_master_table_;
132
    prof_master_table_ = 0;
133
134
#endif /* VM_PROFILER */
135
}
136
137
/* ------------------------------------------------------------------------ */
138
/*
139
 *   Set the function header size 
140
 */
141
void CVmRun::set_funchdr_size(size_t siz)
142
{
143
    /* remember the new size */
144
    funchdr_size_ = siz;
145
146
    /* 
147
     *   ensure that the size is at least as large as our required
148
     *   function header block - if it's not, this version of the VM can't
149
     *   run this image file 
150
     */
151
    if (siz < VMFUNC_HDR_MIN_SIZE)
152
        err_throw(VMERR_IMAGE_INCOMPAT_HDR_FMT);
153
}
154
155
/* ------------------------------------------------------------------------ */
156
/*
157
 *   Add two values, leaving the result in *val1 
158
 */
159
void CVmRun::compute_sum(VMG_ vm_val_t *val1, vm_val_t *val2)
160
{    
161
    /* the meaning of "add" depends on the type of the first operand */
162
    switch(val1->typ)
163
    {
164
    case VM_SSTRING:
165
        /* 
166
         *   string constant - add the second value to the string, using
167
         *   the static string add method 
168
         */
169
        CVmObjString::add_to_str(vmg_ val1, VM_INVALID_OBJ,
170
                                 get_const_ptr(vmg_ val1->val.ofs), val2);
171
        break;
172
173
    case VM_LIST:
174
        /* 
175
         *   list constant - add the second value to the list, using the
176
         *   static list add method 
177
         */
178
        CVmObjList::add_to_list(vmg_ val1, VM_INVALID_OBJ,
179
                                get_const_ptr(vmg_ val1->val.ofs), val2);
180
        break;
181
182
    case VM_OBJ:
183
        /*
184
         *   object - add the second value to the object, using the
185
         *   object's virtual metaclass add method 
186
         */
187
        vm_objp(vmg_ val1->val.obj)->add_val(vmg_ val1, val1->val.obj, val2);
188
        break;
189
190
    case VM_INT:
191
        /* make sure the other value is a number as well */
192
        if (!val2->is_numeric())
193
            err_throw(VMERR_NUM_VAL_REQD);
194
195
        /* compute the sum */
196
        val1->val.intval += val2->num_to_int();
197
        break;
198
199
    default:
200
        /* other types cannot be added */
201
        err_throw(VMERR_BAD_TYPE_ADD);
202
        break;
203
    }
204
}
205
206
207
/* ------------------------------------------------------------------------ */
208
/*
209
 *   Compute the difference of two values, leaving the result in *val1 
210
 */
211
void CVmRun::compute_diff(VMG_ vm_val_t *val1, vm_val_t *val2)
212
{
213
    /* the meaning of "subtract" depends on the type of the first operand */
214
    switch(val1->typ)
215
    {
216
    case VM_LIST:
217
        /* 
218
         *   list constant - remove the second value from the list, using
219
         *   the static list subtraction method 
220
         */
221
        CVmObjList::sub_from_list(vmg_ val1, val1,
222
                                  get_const_ptr(vmg_ val1->val.ofs), val2);
223
        break;
224
225
    case VM_OBJ:
226
        /* object - use the object's virtual subtraction method */
227
        vm_objp(vmg_ val1->val.obj)->sub_val(vmg_ val1, val1->val.obj, val2);
228
        break;
229
230
    case VM_INT:
231
        /* make sure the other value is a number as well */
232
        if (!val2->is_numeric())
233
            err_throw(VMERR_NUM_VAL_REQD);
234
235
        /* compute the difference */
236
        val1->val.intval -= val2->num_to_int();
237
        break;
238
239
    default:
240
        /* other types cannot be subtracted */
241
        err_throw(VMERR_BAD_TYPE_SUB);
242
    }
243
244
}
245
246
/* ------------------------------------------------------------------------ */
247
/*
248
 *   Compute the product val1 * val2, leaving the result in val1 
249
 */
250
void CVmRun::compute_product(VMG_ vm_val_t *val1, vm_val_t *val2)
251
{
252
    switch(val1->typ)
253
    {
254
    case VM_OBJ:
255
        /* use the object's virtual multiplication method */
256
        vm_objp(vmg_ val1->val.obj)->mul_val(vmg_ val1, val1->val.obj, val2);
257
        break;
258
259
    case VM_INT:
260
        /* make sure the other value is a number as well */
261
        if (!val2->is_numeric())
262
            err_throw(VMERR_NUM_VAL_REQD);
263
264
        /* compute the product */
265
        val1->val.intval *= val2->num_to_int();
266
        break;
267
268
    default:
269
        /* other types are invalid */
270
        err_throw(VMERR_BAD_TYPE_MUL);
271
    }
272
}
273
274
/* ------------------------------------------------------------------------ */
275
/*
276
 *   Compute the quotient val1/val2, leaving the result in val1.  
277
 */
278
void CVmRun::compute_quotient(VMG_ vm_val_t *val1, vm_val_t *val2)
279
{
280
    switch(val1->typ)
281
    {
282
    case VM_OBJ:
283
        /* use the object's virtual division method */
284
        vm_objp(vmg_ val1->val.obj)->div_val(vmg_ val1, val1->val.obj, val2);
285
        break;
286
287
    case VM_INT:
288
        /* make sure the other value is a number as well */
289
        if (!val2->is_numeric())
290
            err_throw(VMERR_NUM_VAL_REQD);
291
292
        /* check for divide by zero */
293
        if (val2->num_to_int() == 0)
294
            err_throw(VMERR_DIVIDE_BY_ZERO);
295
296
        /* compute the product */
297
        val1->val.intval = os_divide_long(val1->val.intval,
298
                                          val2->num_to_int());
299
        break;
300
301
    default:
302
        /* other types are invalid */
303
        err_throw(VMERR_BAD_TYPE_DIV);
304
    }
305
}
306
307
/* ------------------------------------------------------------------------ */
308
/*
309
 *   XOR two values and push the result.  The values can be numeric or
310
 *   logical.  If either value is logical, the result will be logical;
311
 *   otherwise, the result will be a bitwise XOR of the integers.  
312
 */
313
void CVmRun::xor_and_push(VMG_ vm_val_t *val1, vm_val_t *val2)
314
{
315
    /* if either value is logical, compute the logical XOR */
316
    if (val1->is_logical() && val2->is_logical())
317
    {
318
        /* both values are logical - compute the logical XOR */
319
        val1->set_logical(val1->get_logical() ^ val2->get_logical());
320
    }
321
    else if (val1->is_logical() || val2->is_logical())
322
    {
323
        /* 
324
         *   one value is logical, but not both - convert the other value
325
         *   from a number to a logical and compute the result as a
326
         *   logical value 
327
         */
328
        if (!val1->is_logical())
329
            val1->num_to_logical();
330
        else if (!val2->is_logical())
331
            val2->num_to_logical();
332
333
        /* compute the logical xor */
334
        val1->set_logical(val1->get_logical() ^ val2->get_logical());
335
    }
336
    else if (val1->typ == VM_INT && val2->typ == VM_INT)
337
    {
338
        /* compute and store the bitwise XOR */
339
        val1->val.intval = val1->val.intval ^ val2->val.intval;
340
    }
341
    else
342
    {
343
        /* no logical conversion */
344
        err_throw(VMERR_NO_LOG_CONV);
345
    }
346
347
    /* push the result */
348
    pushval(vmg_ val1);
349
}
350
351
352
/* ------------------------------------------------------------------------ */
353
/*
354
 *   Index a value and push the result.
355
 */
356
void CVmRun::apply_index(VMG_ vm_val_t *result,
357
                         const vm_val_t *container_val,
358
                         const vm_val_t *index_val)
359
{
360
    /* check the type of the value we're indexing */
361
    switch(container_val->typ)
362
    {
363
    case VM_LIST:
364
        /* list constant - use the static list indexing method */
365
        CVmObjList::index_list(vmg_ result,
366
                               get_const_ptr(vmg_ container_val->val.ofs),
367
                               index_val);
368
        break;
369
370
    case VM_OBJ:
371
        /* object - use the object's virtual indexing method */
372
        vm_objp(vmg_ container_val->val.obj)
373
            ->index_val(vmg_ result, container_val->val.obj, index_val);
374
        break;
375
376
    default:
377
        /* other values cannot be indexed */
378
        err_throw(VMERR_CANNOT_INDEX_TYPE);
379
    }
380
}
381
382
/* ------------------------------------------------------------------------ */
383
/*
384
 *   Set an indexed value.  Updates *container_val with the modified
385
 *   container, if the operation requires this.  (For example, setting an
386
 *   indexed element of a list will create a new list, and return the new
387
 *   list in *container_val.  Setting an element of a vector simply modifies
388
 *   the vector in place, hence the container reference is unchanged.)  
389
 */
390
void CVmRun::set_index(VMG_ vm_val_t *container_val,
391
                       const vm_val_t *index_val,
392
                       const vm_val_t *new_val)
393
{
394
    switch(container_val->typ)
395
    {
396
    case VM_LIST:
397
        /* list constant - use the static list set-index method */
398
        CVmObjList::set_index_list(vmg_ container_val,
399
                                   get_const_ptr(vmg_ container_val->val.ofs),
400
                                   index_val, new_val);
401
        break;
402
403
    case VM_OBJ:
404
        /* object - use the object's virtual set-index method */
405
        vm_objp(vmg_ container_val->val.obj)
406
            ->set_index_val(vmg_ container_val,
407
                            container_val->val.obj, index_val, new_val);
408
        break;
409
410
    default:
411
        /* other values cannot be indexed */
412
        err_throw(VMERR_CANNOT_INDEX_TYPE);
413
    }
414
}
415
416
/* ------------------------------------------------------------------------ */
417
/*
418
 *   Create a new object and store it in R0
419
 */
420
const uchar *CVmRun::new_and_store_r0(VMG_ const uchar *pc,
421
                                      uint metaclass_idx, uint argc,
422
                                      int is_transient)
423
{
424
    vm_obj_id_t obj;
425
    
426
    /* create the object */
427
    obj = G_meta_table->create_from_stack(vmg_ &pc, metaclass_idx, argc);
428
429
    /* if we got a valid object, store a reference to it in R0 */
430
    if (obj != VM_INVALID_OBJ)
431
    {
432
        /* set the object return value */
433
        r0_.set_obj(obj);
434
435
        /* make the object transient if desired */
436
        if (is_transient)
437
            G_obj_table->set_obj_transient(obj);
438
    }
439
    else
440
    {
441
        /* failed - return nil */
442
        r0_.set_nil();
443
    }
444
445
    /* return the new instruction pointer */
446
    return pc;
447
}
448
449
/* ------------------------------------------------------------------------ */
450
/*
451
 *   Execute byte code 
452
 */
453
void CVmRun::run(VMG_ const uchar *start_pc)
454
{
455
    /* 
456
     *   If you're concerned about a compiler warning on the following
457
     *   'register' declaration, refer to the footnote at the bottom of this
458
     *   file (search for [REGISTER_P_FOOTNOTE]).  Executive summary: you can
459
     *   safely ignore the warning, and I'm keeping the code as it is.  
460
     */
461
    register const uchar *p = start_pc;
462
    const uchar *last_pc;
463
    const uchar **old_pc_ptr;
464
    vm_val_t *valp;
465
    vm_val_t *valp2;
466
    vm_val_t val;
467
    vm_val_t val2;
468
    vm_val_t val3;
469
    int done;
470
    vm_obj_id_t obj;
471
    vm_prop_id_t prop;
472
    uint argc;
473
    uint idx;
474
    uint set_idx;
475
    pool_ofs_t ofs;
476
    uint cnt;
477
    vm_obj_id_t unhandled_exc;
478
    int level;
479
    int trans;
480
481
    /* save the enclosing program counter pointer, and remember the new one */
482
    old_pc_ptr = pc_ptr_;
483
    pc_ptr_ = &last_pc;
484
485
    /* we're not done yet */
486
    done = FALSE;
487
488
    /* no unhandled exception yet */
489
    unhandled_exc = VM_INVALID_OBJ;
490
491
    /*
492
     *   Come back here whenever we catch a run-time exception and find a
493
     *   byte-code error handler to process it in the stack.  We'll
494
     *   re-enter our exception handler and resume byte-code execution at
495
     *   the handler.  
496
     */
497
resume_execution:
498
499
    /*
500
     *   Execute all code within an exception frame.  If any routine we
501
     *   call throws an exception, we'll catch the exception and process
502
     *   it as a run-time error.  
503
     */
504
    err_try
505
    {
506
        /* execute code until something makes us stop */
507
        for (;;)
508
        {
509
            VM_IF_DEBUGGER(static int brkchk = 0);
510
511
            /* 
512
             *   check for user-requested break, and step into the debugger
513
             *   if we find it 
514
             */
515
            VM_IF_DEBUGGER(
516
                /* check for break every so often */
517
                if (++brkchk > 10000)
518
                {
519
                    /* reset the break counter */
520
                    brkchk = 0;
521
522
                    /* check for break, and step into debugger if found */
523
                    if (os_break())
524
                        G_debugger->set_break_stop();
525
                }
526
            );
527
528
            /* if we're single-stepping, break into the debugger */
529
            VM_IF_DEBUGGER(if (G_debugger->is_single_step())
530
                G_debugger->step(vmg_ &p, entry_ptr_, FALSE, 0));
531
532
            /* check for a halt request from the debugger */
533
            VM_IF_DEBUGGER(if (halt_vm_) { done = TRUE; goto exit_loop; });
534
535
        exec_instruction:
536
            /* 
537
             *   Remember the location of this instruction in a non-register
538
             *   variable, in case there's an exception.  (We know that
539
             *   last_pc is guaranteed to be a non-register variable because
540
             *   we take its address and store it in our pc_ptr_ member.)
541
             *   
542
             *   We need to know the location of the last instruction when
543
             *   an exception occurs so that we can find the exception
544
             *   handler.  We want to encourage the compiler to enregister
545
             *   'p', since we access it so frequently in this routine; but
546
             *   if it's in a register, there's a risk we'd get the
547
             *   setjmp-time value in our exception handler.  To handle both
548
             *   needs, simply copy the value to our non-register variable
549
             *   last_pc; this will still let the vast majority of our
550
             *   access to 'p' use fast register operations if the compiler
551
             *   allows this, while ensuring we have a safe copy around in
552
             *   case of exceptions.  
553
             */
554
            last_pc = p;
555
556
            /* execute the current instruction */
557
            switch(*p++)
558
            {
559
            case OPC_PUSH_0:
560
                /* push the constant value 0 */
561
                push_int(vmg_ 0);
562
                break;
563
564
            case OPC_PUSH_1:
565
                /* push the constant value 1 */
566
                push_int(vmg_ 1);
567
                break;
568
                
569
            case OPC_PUSHINT8:
570
                /* push an SBYTE operand value */
571
                push_int(vmg_ get_op_int8(&p));
572
                break;
573
574
            case OPC_PUSHINT:
575
                /* push a UINT4 operand value */
576
                push_int(vmg_ get_op_int32(&p));
577
                break;
578
579
            case OPC_PUSHENUM:
580
                /* push a UINT4 operand value */
581
                push_enum(vmg_ get_op_uint32(&p));
582
                break;
583
584
            case OPC_PUSHSTR:
585
                /* push UINT4 offset operand as a string */
586
                G_stk->push()->set_sstring(get_op_uint32(&p));
587
                break;
588
589
            case OPC_PUSHSTRI:
590
                /* inline string - get the length prefix */
591
                cnt = get_op_uint16(&p);
592
593
                /* create the new string from the inline data */
594
                obj = CVmObjString::create(vmg_ FALSE, (const char *)p, cnt);
595
596
                /* skip past the string's bytes */
597
                p += cnt;
598
599
                /* push the new string */
600
                push_obj(vmg_ obj);
601
                break;
602
603
            case OPC_PUSHLST:
604
                /* push UINT4 offset operand as a list */
605
                G_stk->push()->set_list(get_op_uint32(&p));
606
                break;
607
608
            case OPC_PUSHOBJ:
609
                /* push UINT4 object ID operand */
610
                G_stk->push()->set_obj(get_op_uint32(&p));
611
                break;
612
613
            case OPC_PUSHNIL:
614
                /* push nil */
615
                push_nil(vmg0_);
616
                break;
617
618
            case OPC_PUSHTRUE:
619
                /* push true */
620
                G_stk->push()->set_true();
621
                break;
622
623
            case OPC_PUSHPROPID:
624
                /* push UINT2 property ID operand */
625
                G_stk->push()->set_propid(get_op_uint16(&p));
626
                break;
627
628
            case OPC_PUSHFNPTR:
629
                /* push a function pointer operand */
630
                G_stk->push()->set_fnptr(get_op_uint32(&p));
631
                break;
632
633
            case OPC_PUSHPARLST:
634
                /* get the number of fixed parameters */
635
                cnt = *p++;
636
637
                /* allocate the list from the parameters */
638
                obj = CVmObjList::create_from_params(
639
                    vmg_ cnt, get_cur_argc(vmg0_) - cnt);
640
641
                /* push the new list */
642
                push_obj(vmg_ obj);
643
                break;
644
645
            case OPC_MAKELSTPAR:
646
                {
647
                    const char *lstp;
648
                    uint i;
649
                    uint hdr_depth;
650
                    CVmFuncPtr hdr_ptr;
651
652
                    /* pop the value */
653
                    popval(vmg_ &val);
654
655
                    /* pop the argument counter so far */
656
                    pop_int(vmg_ &val2);
657
658
                    /* if it's not a list, just push it again unchanged */
659
                    if ((lstp = val.get_as_list(vmg0_)) == 0)
660
                    {
661
                        /* put it back on the stack */
662
                        pushval(vmg_ &val);
663
664
                        /* increment the argument count and push it */
665
                        ++val2.val.intval;
666
                        pushval(vmg_ &val2);
667
668
                        /* our work here is done */
669
                        break;
670
                    }
671
672
                    /* set up a pointer to the current function header */
673
                    hdr_ptr.set(entry_ptr_native_);
674
675
                    /* get the depth required for the header */
676
                    hdr_depth = hdr_ptr.get_stack_depth();
677
678
                    /* 
679
                     *   deduct the amount stack space we've already used
680
                     *   from the amount noted in the header, because
681
                     *   that's the amount more that we could need for the
682
                     *   fixed stuff
683
                     */
684
                    hdr_depth -= (G_stk->get_depth_rel(frame_ptr_) - 1);
685
686
                    /* get the number of elements in the list */
687
                    cnt = vmb_get_len(lstp);
688
                      
689
                    /* make sure we have enough stack space available */
690
                    if (!G_stk->check_space(cnt + hdr_depth))
691
                        err_throw(VMERR_STACK_OVERFLOW);
692
693
                    /* push the elements of the list from last to first */
694
                    for (i = cnt ; i != 0 ; --i)
695
                    {
696
                        /* get this element's value */
697
                        CVmObjList::index_and_push(vmg_ lstp, i);
698
                    }
699
700
                    /* increment and push the argument count */
701
                    val2.val.intval += cnt;
702
                    pushval(vmg_ &val2);
703
                }
704
                break;
705
706
            case OPC_NEG:
707
                /* check for an object */
708
                if ((valp = G_stk->get(0))->typ == VM_OBJ)
709
                {
710
                    /* call the object's negate method */
711
                    vm_objp(vmg_ valp->val.obj)
712
                        ->neg_val(vmg_ &val2, valp->val.obj);
713
714
                    /* replace TOS with the result */
715
                    *valp = val2;
716
                }
717
                else
718
                {
719
                    /* make sure it's a number */
720
                    if (!valp->is_numeric())
721
                        err_throw(VMERR_NUM_VAL_REQD);
722
723
                    /* negate number in place */
724
                    valp->val.intval = -valp->val.intval;
725
                }
726
                break;
727
728
            case OPC_BNOT:
729
                /* ensure we have an integer */
730
                if ((valp = G_stk->get(0))->typ != VM_INT)
731
                    err_throw(VMERR_INT_VAL_REQD);
732
733
                /* bitwise NOT the integer on top of stack */
734
                valp->val.intval = ~valp->val.intval;
735
                break;
736
737
            case OPC_ADD:
738
                /* if they're both integers, add them the quick way */
739
                valp = G_stk->get(0);
740
                valp2 = G_stk->get(1);
741
                if (valp->typ == VM_INT && valp2->typ == VM_INT)
742
                {
743
                    /* add the two values */
744
                    valp2->val.intval += valp->val.intval;
745
746
                    /* discard the second value */
747
                    G_stk->discard();
748
                }
749
                else
750
                {
751
                    /* 
752
                     *   compute the sum of (TOS-1) + (TOS), leaving the
753
                     *   result in (TOS-1) 
754
                     */
755
                    compute_sum(vmg_ valp2, valp);
756
757
                    /* discard TOS */
758
                    G_stk->discard();
759
                }
760
                break;
761
762
            case OPC_INC:
763
                /* 
764
                 *   Increment the value at top of stack.  We must perform
765
                 *   the same type conversions as the ADD instruction
766
                 *   does.  As an optimization, check to see if we have an
767
                 *   integer on top of the stack, and if so simply
768
                 *   increment its value without popping and repushing.  
769
                 */
770
                if ((valp = G_stk->get(0))->typ == VM_INT)
771
                {
772
                    /* it's an integer - increment it, and we're done */
773
                    ++(valp->val.intval);
774
                }
775
                else
776
                {
777
                    /* add 1 to the value at TOS, leaving it on the stack */
778
                    val2.set_int(1);
779
                    compute_sum(vmg_ valp, &val2);
780
                }
781
                break;
782
783
            case OPC_DEC:
784
                /* 
785
                 *   Decrement the value at top of stack.  We must perform
786
                 *   the same type conversions as the SUB instruction
787
                 *   does.  As an optimization, check to see if we have an
788
                 *   integer on top of the stack, and if so simply
789
                 *   decrement its value without popping and repushing.  
790
                 */
791
                if ((valp = G_stk->get(0))->typ == VM_INT)
792
                {
793
                    /* it's an integer - decrement it, and we're done */
794
                    --(valp->val.intval);
795
                }
796
                else
797
                {
798
                    /* compute TOS - 1, leaving the result in TOS */
799
                    val2.set_int(1);
800
                    compute_diff(vmg_ valp, &val2);
801
                }
802
                break;
803
804
            case OPC_SUB:
805
                /* if they're both integers, subtract them the quick way */
806
                valp = G_stk->get(0);
807
                valp2 = G_stk->get(1);
808
                if (valp->typ == VM_INT && valp2->typ == VM_INT)
809
                {
810
                    /* compute the difference */
811
                    valp2->val.intval -= valp->val.intval;
812
813
                    /* discard the second value */
814
                    G_stk->discard();
815
                }
816
                else
817
                {
818
                    /* 
819
                     *   compute the difference (TOS-1) - (TOS), leaving the
820
                     *   result in (TOS-1) 
821
                     */
822
                    compute_diff(vmg_ valp2, valp);
823
824
                    /* discard TOS */
825
                    G_stk->discard();
826
                }
827
                break;
828
829
            case OPC_MUL:
830
                /* if they're both integers, this is easy */
831
                valp = G_stk->get(0);
832
                valp2 = G_stk->get(1);
833
                if (valp->typ == VM_INT && valp2->typ == VM_INT)
834
                {
835
                    /* compute the difference */
836
                    valp2->val.intval *= valp->val.intval;
837
838
                    /* discard the second value */
839
                    G_stk->discard();
840
                }
841
                else
842
                {
843
                    /* 
844
                     *   compute the product (TOS-1) * (TOS), leaving the
845
                     *   result in (TOS-1) 
846
                     */
847
                    compute_product(vmg_ valp2, valp);
848
849
                    /* discard TOS */
850
                    G_stk->discard();
851
                }
852
                break;
853
854
            case OPC_DIV:
855
                /* if they're both integers, divide them the quick way */
856
                valp = G_stk->get(0);
857
                valp2 = G_stk->get(1);
858
                if (valp->typ == VM_INT && valp2->typ == VM_INT)
859
                {
860
                    /* check for division by zero */
861
                    if (valp->val.intval == 0)
862
                        err_throw(VMERR_DIVIDE_BY_ZERO);
863
864
                    /* compute the result of the division */
865
                    valp2->val.intval = os_divide_long(
866
                        valp2->val.intval, valp->val.intval);
867
868
                    /* discard the second value */
869
                    G_stk->discard();
870
                }
871
                else
872
                {
873
                    /* 
874
                     *   compute (TOS-1) / (TOS), leaving the result in
875
                     *   (TOS-1) 
876
                     */
877
                    compute_quotient(vmg_ valp2, valp);
878
879
                    /* discard TOS */
880
                    G_stk->discard();
881
                }
882
                break;
883
884
            case OPC_MOD:
885
                /* remainder number at (TOS-1) by number at top of stack */
886
                valp = G_stk->get(0);
887
                valp2 = G_stk->get(1);
888
889
                /* make sure the values are integers */
890
                if (valp->typ != VM_INT || valp2->typ != VM_INT)
891
                    err_throw(VMERR_INT_VAL_REQD);
892
893
                /* 
894
                 *   compute the remainger (TOS-1) % (TOS), leaving the
895
                 *   result at (TOS-1), and discard the second operand 
896
                 */
897
                valp2->val.intval = os_remainder_long(
898
                    valp2->val.intval, valp->val.intval);
899
                G_stk->discard();
900
                break;
901
902
            case OPC_BAND:
903
                /* bitwise AND two integers on top of stack */
904
                valp = G_stk->get(0);
905
                valp2 = G_stk->get(1);
906
907
                /* ensure we have two integers */
908
                if (valp->typ != VM_INT || valp2->typ != VM_INT)
909
                    err_throw(VMERR_INT_VAL_REQD);
910
911
                /* compute the result and discard the second operand */
912
                valp2->val.intval &= valp->val.intval;
913
                G_stk->discard();
914
                break;
915
916
            case OPC_BOR:
917
                /* bitwise OR two integers on top of stack */
918
                valp = G_stk->get(0);
919
                valp2 = G_stk->get(1);
920
921
                /* ensure we have two integers */
922
                if (valp->typ != VM_INT || valp2->typ != VM_INT)
923
                    err_throw(VMERR_INT_VAL_REQD);
924
925
                /* compute the result and discard the second operand */
926
                valp2->val.intval |= valp->val.intval;
927
                G_stk->discard();
928
                break;
929
930
            case OPC_SHL:
931
                /* 
932
                 *   bit-shift left integer at (TOS-1) by integer at top
933
                 *   of stack 
934
                 */
935
                valp = G_stk->get(0);
936
                valp2 = G_stk->get(1);
937
938
                /* ensure we have two integers */
939
                if (valp->typ != VM_INT || valp2->typ != VM_INT)
940
                    err_throw(VMERR_INT_VAL_REQD);
941
942
                /* compute the result and discard the second operand */
943
                valp2->val.intval <<= valp->val.intval;
944
                G_stk->discard();
945
                break;
946
947
            case OPC_SHR:
948
                /* 
949
                 *   bit-shift right integer at (TOS-1) by integer at top
950
                 *   of stack 
951
                 */
952
                valp = G_stk->get(0);
953
                valp2 = G_stk->get(1);
954
955
                /* ensure we have two integers */
956
                if (valp->typ != VM_INT || valp2->typ != VM_INT)
957
                    err_throw(VMERR_INT_VAL_REQD);
958
959
                /* compute the result and discard the second operand */
960
                valp2->val.intval >>= valp->val.intval;
961
                G_stk->discard();
962
                break;
963
964
            case OPC_XOR:
965
                /* XOR two values at top of stack */
966
                popval_2(vmg_ &val, &val2);
967
                xor_and_push(vmg_ &val, &val2);
968
                break;
969
970
            case OPC_NOT:
971
                /* 
972
                 *   invert the logic value; if the value is a number,
973
                 *   treat 0 as nil and non-zero as true 
974
                 */
975
                valp = G_stk->get(0);
976
                switch(valp->typ)
977
                {
978
                case VM_NIL:
979
                    /* !nil -> true */
980
                    valp->set_true();
981
                    break;
982
983
                case VM_OBJ:
984
                    /* !obj -> true if obj is nil, nil otherwise */
985
                    valp->set_logical(valp->val.obj == VM_INVALID_OBJ);
986
                    break;
987
988
                case VM_TRUE:
989
                case VM_PROP:
990
                case VM_SSTRING:
991
                case VM_LIST:
992
                case VM_CODEOFS:
993
                case VM_FUNCPTR:
994
                case VM_ENUM:
995
                    /* these are all considered true, so !them -> nil */
996
                    valp->set_nil();
997
                    break;
998
999
                case VM_INT:
1000
                    /* !int -> true if int is 0, nil otherwise */
1001
                    valp->set_logical(valp->val.intval == 0);
1002
                    break;
1003
1004
                default:
1005
                    err_throw(VMERR_NO_LOG_CONV);
1006
                }
1007
                break;
1008
1009
            case OPC_BOOLIZE:
1010
                /* set to a boolean value */
1011
                valp = G_stk->get(0);
1012
                switch(valp->typ)
1013
                {
1014
                case VM_NIL:
1015
                case VM_TRUE:
1016
                    /* it's already a logical value - leave it alone */
1017
                    break;
1018
1019
                case VM_INT:
1020
                    /* integer: 0 -> nil, non-zero -> true */
1021
                    valp->set_logical(valp->val.intval);
1022
                    break;
1023
1024
                case VM_ENUM:
1025
                    /* an enum is always non-nil */
1026
                    valp->set_true();
1027
                    break;
1028
1029
                default:
1030
                    err_throw(VMERR_NO_LOG_CONV);
1031
                }
1032
                break;
1033
1034
            case OPC_EQ:
1035
                /* compare two values at top of stack for equality */
1036
                push_bool(vmg_ pop2_equal(vmg0_));
1037
                break;
1038
1039
            case OPC_NE:
1040
                /* compare two values at top of stack for inequality */
1041
                push_bool(vmg_ !pop2_equal(vmg0_));
1042
                break;
1043
1044
            case OPC_LT:
1045
                /* compare values at top of stack - true if (TOS-1) < TOS */
1046
                push_bool(vmg_ pop2_compare_lt(vmg0_));
1047
                break;
1048
1049
            case OPC_LE:
1050
                /* compare values at top of stack - true if (TOS-1) <= TOS */
1051
                push_bool(vmg_ pop2_compare_le(vmg0_));
1052
                break;
1053
1054
            case OPC_GT:
1055
                /* compare values at top of stack - true if (TOS-1) > TOS */
1056
                push_bool(vmg_ pop2_compare_gt(vmg0_));
1057
                break;
1058
1059
            case OPC_GE:
1060
                /* compare values at top of stack - true if (TOS-1) >= TOS */
1061
                push_bool(vmg_ pop2_compare_ge(vmg0_));
1062
                break;
1063
1064
            case OPC_VARARGC:
1065
                {
1066
                    uchar opc;
1067
1068
                    /* get the modified opcode */
1069
                    opc = *p++;
1070
1071
                    /* 
1072
                     *   skip the immediate data argument count - this is
1073
                     *   superseded by our dynamic argument counter 
1074
                     */
1075
                    ++p;
1076
                    
1077
                    /* pop the argument counter */
1078
                    pop_int(vmg_ &val);
1079
                    argc = val.val.intval;
1080
1081
                    /* execute the appropriate next opcode */
1082
                    switch(opc)
1083
                    {
1084
                    case OPC_CALL:
1085
                        goto do_opc_call;
1086
1087
                    case OPC_PTRCALL:
1088
                        goto do_opc_ptrcall;
1089
1090
                    case OPC_CALLPROP:
1091
                        goto do_opc_callprop;
1092
1093
                    case OPC_PTRCALLPROP:
1094
                        goto do_opc_ptrcallprop;
1095
1096
                    case OPC_CALLPROPSELF:
1097
                        goto do_opc_callpropself;
1098
1099
                    case OPC_PTRCALLPROPSELF:
1100
                        goto do_opc_ptrcallpropself;
1101
1102
                    case OPC_OBJCALLPROP:
1103
                        goto do_opc_objcallprop;
1104
1105
                    case OPC_CALLPROPLCL1:
1106
                        goto do_opc_callproplcl1;
1107
1108
                    case OPC_CALLPROPR0:
1109
                        goto do_opc_callpropr0;
1110
1111
                    case OPC_INHERIT:
1112
                        goto do_opc_inherit;
1113
1114
                    case OPC_PTRINHERIT:
1115
                        goto do_opc_ptrinherit;
1116
1117
                    case OPC_EXPINHERIT:
1118
                        goto do_opc_expinherit;
1119
1120
                    case OPC_PTREXPINHERIT:
1121
                        goto do_opc_ptrexpinherit;
1122
1123
                    case OPC_DELEGATE:
1124
                        goto do_opc_delegate;
1125
1126
                    case OPC_PTRDELEGATE:
1127
                        goto do_opc_ptrdelegate;
1128
1129
                    case OPC_BUILTIN_A:
1130
                        goto do_opc_builtin_a;
1131
1132
                    case OPC_BUILTIN_B:
1133
                        goto do_opc_builtin_b;
1134
1135
                    case OPC_BUILTIN_C:
1136
                        goto do_opc_builtin_c;
1137
1138
                    case OPC_BUILTIN_D:
1139
                        goto do_opc_builtin_d;
1140
1141
                    case OPC_BUILTIN1:
1142
                        goto do_opc_builtin1;
1143
1144
                    case OPC_BUILTIN2:
1145
                        goto do_opc_builtin2;
1146
1147
                    case OPC_NEW1:
1148
                        trans = FALSE;
1149
                        goto do_opc_new1_argc;
1150
1151
                    case OPC_TRNEW1:
1152
                        trans = TRUE;
1153
                        goto do_opc_new1_argc;
1154
1155
                    case OPC_NEW2:
1156
                        trans = FALSE;
1157
                        goto do_opc_new2_argc;
1158
1159
                    case OPC_TRNEW2:
1160
                        trans = TRUE;
1161
                        goto do_opc_new2_argc;
1162
1163
                    default:
1164
                        err_throw(VMERR_INVALID_OPCODE_MOD);
1165
                        break;
1166
                    }
1167
                }
1168
                break;
1169
1170
            case OPC_CALL:
1171
                /* get the argument count */
1172
                argc = get_op_uint8(&p);
1173
1174
            do_opc_call:
1175
                /* get the code offset to invoke */
1176
                ofs = get_op_int32(&p);
1177
1178
                /* call it */
1179
                p = do_call_func_nr(vmg_ p - entry_ptr_native_, ofs, argc);
1180
                break;
1181
1182
            case OPC_PTRCALL:
1183
                /* get the argument count */
1184
                argc = get_op_uint8(&p);
1185
1186
            do_opc_ptrcall:
1187
                /* retrieve the target of the call */
1188
                popval(vmg_ &val);
1189
1190
                /* 
1191
                 *   if it's a prop ID, and there's a valid "self" object,
1192
                 *   treat it as a PTRCALLPROPSELF 
1193
                 */
1194
                if (val.typ == VM_PROP && get_self(vmg0_) != VM_INVALID_OBJ)
1195
                    goto do_opc_ptrcallpropself_val;
1196
                
1197
                /* call the function */
1198
                p = call_func_ptr(vmg_ &val, argc, 0, p - entry_ptr_native_);
1199
                break;
1200
1201
            case OPC_RETVAL:
1202
                /* pop the return value into R0 */
1203
                popval(vmg_ &r0_);
1204
1205
                /* return */
1206
                if ((p = do_return(vmg0_)) == 0)
1207
                    goto exit_loop;
1208
                break;
1209
1210
            case OPC_RET:
1211
                /* return, leaving R0 unchanged */
1212
                if ((p = do_return(vmg0_)) == 0)
1213
                    goto exit_loop;
1214
                break;
1215
1216
            case OPC_RETNIL:
1217
                /* store nil in R0 */
1218
                r0_.set_nil();
1219
1220
                /* return */
1221
                if ((p = do_return(vmg0_)) == 0)
1222
                    goto exit_loop;
1223
                break;
1224
1225
            case OPC_RETTRUE:
1226
                /* store true in R0 */
1227
                r0_.set_true();
1228
1229
                /* return */
1230
                if ((p = do_return(vmg0_)) == 0)
1231
                    goto exit_loop;
1232
                break;
1233
1234
            case OPC_GETPROP:
1235
                /* get the object whose property we're fetching */
1236
                G_stk->pop(&val);
1237
1238
                /* evaluate the property given by the immediate data */
1239
                prop = get_op_uint16(&p);
1240
                p = get_prop(vmg_ p - entry_ptr_native_, &val, prop, &val, 0);
1241
                break;
1242
1243
            case OPC_GETPROPLCL1:
1244
                /* get the local whose property we're evaluating */
1245
                valp = get_local(vmg_ get_op_uint8(&p));
1246
1247
                /* evaluate the property of the local variable */
1248
                prop = get_op_uint16(&p);
1249
                p = get_prop(vmg_ p - entry_ptr_native_, valp, prop, valp, 0);
1250
                break;
1251
1252
            case OPC_GETPROPR0:
1253
                /* evaluate the property of R0 */
1254
                val = r0_;
1255
                valp = &val;
1256
                prop = get_op_uint16(&p);
1257
                p = get_prop(vmg_ p - entry_ptr_native_, valp, prop, valp, 0);
1258
                break;
1259
1260
            case OPC_CALLPROP:
1261
                /* get the argument count */
1262
                argc = get_op_uint8(&p);
1263
1264
            do_opc_callprop:
1265
                /* pop the object whose property we're fetching */
1266
                G_stk->pop(&val);
1267
1268
                /* evaluate the property given by the immediate data */
1269
                prop = get_op_uint16(&p);
1270
                p = get_prop(vmg_ p - entry_ptr_native_, &val,
1271
                             prop, &val, argc);
1272
                break;
1273
1274
            case OPC_CALLPROPLCL1:
1275
                /* get the argument count */
1276
                argc = get_op_uint8(&p);
1277
1278
            do_opc_callproplcl1:
1279
                /* get the local whose property we're calling */
1280
                valp = get_local(vmg_ get_op_uint8(&p));
1281
1282
                /* call the property of the local */
1283
                prop = get_op_uint16(&p);
1284
                p = get_prop(vmg_ p - entry_ptr_native_, valp,
1285
                             prop, valp, argc);
1286
                break;
1287
1288
            case OPC_CALLPROPR0:
1289
                /* get the argument count */
1290
                argc = get_op_uint8(&p);
1291
1292
            do_opc_callpropr0:
1293
                /* call the property of R0 */
1294
                val = r0_;
1295
                prop = get_op_uint16(&p);
1296
                p = get_prop(vmg_ p - entry_ptr_native_, &val,
1297
                             prop, &val, argc);
1298
                break;
1299
1300
            case OPC_PTRCALLPROP:
1301
                /* get the argument count */
1302
                argc = get_op_uint8(&p);
1303
1304
            do_opc_ptrcallprop:
1305
                /* 
1306
                 *   pop the property to be evaluated, and the object
1307
                 *   whose property we're evaluating 
1308
                 */
1309
                pop_prop(vmg_ &val);
1310
                G_stk->pop(&val2);
1311
                
1312
                /* evaluate the property */
1313
                p = get_prop(vmg_ p - entry_ptr_native_, &val2,
1314
                             val.val.prop, &val2, argc);
1315
                break;
1316
1317
            case OPC_GETPROPSELF:
1318
                /* evaluate the property of 'self' */
1319
                val.set_obj(get_self(vmg0_));
1320
                prop = get_op_uint16(&p);
1321
                p = get_prop(vmg_ p - entry_ptr_native_, &val, prop, &val, 0);
1322
                break;
1323
1324
            case OPC_CALLPROPSELF:
1325
                /* get the argument count */
1326
                argc = get_op_uint8(&p);
1327
1328
            do_opc_callpropself:
1329
                /* evaluate the property of 'self' */
1330
                val.set_obj(get_self(vmg0_));
1331
                prop = get_op_uint16(&p);
1332
                p = get_prop(vmg_ p - entry_ptr_native_, &val,
1333
                             prop, &val, argc);
1334
                break;
1335
1336
            case OPC_PTRCALLPROPSELF:
1337
                /* get the argument count */
1338
                argc = get_op_uint8(&p);
1339
1340
            do_opc_ptrcallpropself:
1341
                /* get the property to be evaluated */
1342
                pop_prop(vmg_ &val);
1343
1344
            do_opc_ptrcallpropself_val:
1345
                /* evaluate the property of 'self' */
1346
                val2.set_obj(get_self(vmg0_));
1347
                p = get_prop(vmg_ p - entry_ptr_native_,
1348
                             &val2, val.val.prop, &val2, argc);
1349
                break;
1350
1351
            case OPC_OBJGETPROP:
1352
                /* get the object */
1353
                val.set_obj((vm_obj_id_t)get_op_uint32(&p));
1354
                
1355
                /* evaluate the property */
1356
                prop = get_op_uint16(&p);
1357
                p = get_prop(vmg_ p - entry_ptr_native_, &val, prop, &val, 0);
1358
                break;
1359
1360
            case OPC_OBJCALLPROP:
1361
                /* get the argument count */
1362
                argc = get_op_uint8(&p);
1363
1364
            do_opc_objcallprop:
1365
                /* get the object */
1366
                val.set_obj((vm_obj_id_t)get_op_uint32(&p));
1367
                
1368
                /* evaluate the property */
1369
                prop = get_op_uint16(&p);
1370
                p = get_prop(vmg_ p - entry_ptr_native_,
1371
                             &val, prop, &val, argc);
1372
                break;
1373
1374
            case OPC_GETPROPDATA:
1375
                /* get the object whose property we're fetching */
1376
                G_stk->pop(&val);
1377
1378
                /* 
1379
                 *   if the object is not an object, it's one of the
1380
                 *   native types, in which case we'll definitely run
1381
                 *   native code to evaluate the property, in which case
1382
                 *   it's not valid for speculative evaluation 
1383
                 */
1384
                if (val.typ != VM_OBJ)
1385
                    err_throw(VMERR_BAD_SPEC_EVAL);
1386
1387
                /* get the property */
1388
                prop = (vm_prop_id_t)get_op_uint16(&p);
1389
1390
                /* check validity for speculative evaluation */
1391
                check_prop_spec_eval(vmg_ val.val.obj, prop);
1392
1393
                /* evaluate the property given by the immediate data */
1394
                p = get_prop(vmg_ p - entry_ptr_native_, &val, prop, &val, 0);
1395
                break;
1396
1397
            case OPC_PTRGETPROPDATA:
1398
                /* get the property and object to evaluate */
1399
                pop_prop(vmg_ &val);
1400
                G_stk->pop(&val2);
1401
1402
                /* 
1403
                 *   if the object is not an object, it's one of the
1404
                 *   native types, in which case we'll definitely run
1405
                 *   native code to evaluate the property, in which case
1406
                 *   it's not valid for speculative evaluation 
1407
                 */
1408
                if (val2.typ != VM_OBJ)
1409
                    err_throw(VMERR_BAD_SPEC_EVAL);
1410
1411
                /* check validity for speculative evaluation */
1412
                check_prop_spec_eval(vmg_ val2.val.obj, val.val.prop);
1413
1414
                /* evaluate it */
1415
                p = get_prop(vmg_ p - entry_ptr_native_,
1416
                             &val2, val.val.prop, &val2, 0);
1417
                break;
1418
1419
            case OPC_GETLCL1:
1420
                /* push the local */
1421
                pushval(vmg_ get_local(vmg_ get_op_uint8(&p)));
1422
                break;
1423
1424
            case OPC_GETLCL2:
1425
                /* push the local */
1426
                pushval(vmg_ get_local(vmg_ get_op_uint16(&p)));
1427
                break;
1428
1429
            case OPC_GETARG1:
1430
                /* push the argument */
1431
                pushval(vmg_ get_param(vmg_ get_op_uint8(&p)));
1432
                break;
1433
1434
            case OPC_GETARG2:
1435
                /* push the argument */
1436
                pushval(vmg_ get_param(vmg_ get_op_uint16(&p)));
1437
                break;
1438
1439
            case OPC_PUSHSELF:
1440
                /* push 'self' */
1441
                pushval(vmg_ get_self_val(vmg0_));
1442
                break;
1443
1444
            case OPC_SETSELF:
1445
                /* retrieve the 'self' object */
1446
                G_stk->pop(&val);
1447
1448
                /* set 'self' */
1449
                set_self(vmg_ &val);
1450
                break;
1451
1452
            case OPC_STORECTX:
1453
                {
1454
                    char buf[VMB_LEN + 4*VMB_DATAHOLDER];
1455
1456
                    /* our list has four elements */
1457
                    vmb_put_len(buf, 4);
1458
1459
                    /* 
1460
                     *   put the list elements: 'self', targetprop, original
1461
                     *   target object, and defining object 
1462
                     */
1463
                    vmb_put_dh_obj(buf + VMB_LEN, get_self(vmg0_));
1464
                    vmb_put_dh_prop(buf + VMB_LEN + VMB_DATAHOLDER,
1465
                                    get_target_prop(vmg0_));
1466
                    vmb_put_dh_obj(buf + VMB_LEN + 2*VMB_DATAHOLDER,
1467
                                   get_orig_target_obj(vmg0_));
1468
                    vmb_put_dh_obj(buf + VMB_LEN + 3*VMB_DATAHOLDER,
1469
                                   get_defining_obj(vmg0_));
1470
1471
                    /* push a new list copied from our prepared buffer */
1472
                    push_obj(vmg_ CVmObjList::create(vmg_ FALSE, buf));
1473
                }
1474
                break;
1475
1476
            case OPC_LOADCTX:
1477
                {
1478
                    const char *lstp;
1479
1480
                    /* 
1481
                     *   convert the context object (at top of stack) to a
1482
                     *   list pointer 
1483
                     */
1484
                    lstp = G_stk->get(0)->get_as_list(vmg0_);
1485
1486
                    /* throw an error if it's not what we're expecting */
1487
                    if (lstp == 0 || vmb_get_len(lstp) < 4)
1488
                        err_throw(VMERR_LIST_VAL_REQD);
1489
1490
                    /* retrieve and store the context elements */
1491
                    set_method_ctx(
1492
                        vmg_ vmb_get_dh_obj(lstp + VMB_LEN),
1493
                        vmb_get_dh_prop(lstp + VMB_LEN
1494
                                        + VMB_DATAHOLDER),
1495
                        vmb_get_dh_obj(lstp + VMB_LEN
1496
                                       + 2*VMB_DATAHOLDER),
1497
                        vmb_get_dh_obj(lstp + VMB_LEN
1498
                                       + 3*VMB_DATAHOLDER));
1499
1500
                    /* discard the context object at top of stack */
1501
                    G_stk->discard();
1502
                }
1503
                break;
1504
1505
            case OPC_PUSHCTXELE:
1506
                /* check our context element type */
1507
                switch(*p++)
1508
                {
1509
                case PUSHCTXELE_TARGPROP:
1510
                    /* push the target property ID */
1511
                    push_prop(vmg_ get_target_prop(vmg0_));
1512
                    break;
1513
1514
                case PUSHCTXELE_TARGOBJ:
1515
                    /* push the original target object ID */
1516
                    push_obj(vmg_ get_orig_target_obj(vmg0_));
1517
                    break;
1518
1519
                case PUSHCTXELE_DEFOBJ:
1520
                    /* push the defining object */
1521
                    push_obj(vmg_ get_defining_obj(vmg0_));
1522
                    break;
1523
1524
                default:
1525
                    /* the opcode is not valid in this VM version */
1526
                    err_throw(VMERR_INVALID_OPCODE);
1527
                }
1528
                break;
1529
1530
            case OPC_GETARGC:
1531
                /* push the argument counter */
1532
                push_int(vmg_ get_cur_argc(vmg0_));
1533
                break;
1534
1535
            case OPC_DUP:
1536
                /* re-push the item at top of stack */
1537
                pushval(vmg_ G_stk->get(0));
1538
                break;
1539
1540
            case OPC_SWAP:
1541
                /* swap the top two elements on the stack */
1542
                valp = G_stk->get(0);
1543
                valp2 = G_stk->get(1);
1544
1545
                /* make a working copy of TOS */
1546
                val = *valp;
1547
1548
                /* copy TOS-1 over TOS */
1549
                *valp = *valp2;
1550
1551
                /* copy the working copy of TOS over TOS-1 */
1552
                *valp2 = val;
1553
                break;
1554
1555
            case OPC_DISC:
1556
                /* discard the item at the top of the stack */
1557
                G_stk->discard();
1558
                break;
1559
1560
            case OPC_DISC1:
1561
                /* discard n items */
1562
                G_stk->discard(get_op_uint8(&p));
1563
                break;
1564
1565
            case OPC_GETR0:
1566
                /* push the contents of R0 */
1567
                pushval(vmg_ &r0_);
1568
                break;
1569
1570
            case OPC_GETDBARGC:
1571
                /* push the argument count from the selected frame */
1572
                push_int(vmg_ get_argc_at_level(vmg_ get_op_uint16(&p) + 1));
1573
                break;
1574
1575
            case OPC_GETDBLCL:
1576
                /* get the local variable number and stack level */
1577
                idx = get_op_uint16(&p);
1578
                level = get_op_uint16(&p);
1579
1580
                /* push the value */
1581
                pushval(vmg_ get_local_at_level(vmg_ idx, level + 1));
1582
                break;
1583
                
1584
            case OPC_GETDBARG:
1585
                /* get the parameter variable number and stack level */
1586
                idx = get_op_uint16(&p);
1587
                level = get_op_uint16(&p);
1588
1589
                /* push the value */
1590
                pushval(vmg_ get_param_at_level(vmg_ idx, level + 1));
1591
                break;
1592
1593
            case OPC_SETDBLCL:
1594
                /* get the local variable number and stack level */
1595
                idx = get_op_uint16(&p);
1596
                level = get_op_uint16(&p);
1597
1598
                /* get the local pointer */
1599
                valp = get_local_at_level(vmg_ idx, level + 1);
1600
1601
                /* pop the value into the local */
1602
                popval(vmg_ valp);
1603
                break;
1604
1605
            case OPC_SETDBARG:
1606
                /* get the parameter variable number and stack level */
1607
                idx = get_op_uint16(&p);
1608
                level = get_op_uint16(&p);
1609
1610
                /* get the parameter pointer */
1611
                valp = get_param_at_level(vmg_ idx, level + 1);
1612
1613
                /* pop the value into the local */
1614
                popval(vmg_ valp);
1615
                break;
1616
1617
            case OPC_SWITCH:
1618
                /* get the control value */
1619
                valp = G_stk->get(0);
1620
1621
                /* get the case count */
1622
                cnt = get_op_uint16(&p);
1623
1624
                /* iterate through the case table */
1625
                for ( ; cnt != 0 ; p += 7, --cnt)
1626
                {
1627
                    /* get this value */
1628
                    vmb_get_dh((const char *)p, &val2);
1629
1630
                    /* check if the values match */
1631
                    if (valp->equals(vmg_ &val2))
1632
                    {
1633
                        /* it matches - jump to this offset */
1634
                        p += VMB_DATAHOLDER;
1635
                        p += osrp2s(p);
1636
1637
                        /* no need to look any further */
1638
                        break;
1639
                    }
1640
                }
1641
1642
                /* discard the control value */
1643
                G_stk->discard();
1644
1645
                /* if we didn't find it, jump to the default case */
1646
                if (cnt == 0)
1647
                    p += osrp2s(p);
1648
                break;
1649
1650
            case OPC_JMP:
1651
                /* unconditionally jump to the given offset */
1652
                p += osrp2s(p);
1653
                break;
1654
1655
            case OPC_JT:
1656
                /* get the value */
1657
                valp = G_stk->get(0);
1658
1659
                /* 
1660
                 *   if it's true, or a non-zero numeric value, or any
1661
                 *   non-numeric and non-boolean value, jump 
1662
                 */
1663
                if (valp->typ == VM_NIL
1664
                    || (valp->typ == VM_INT && valp->val.intval == 0))
1665
                {
1666
                    /* it's zero or nil - do not jump */
1667
                    p += 2;
1668
                }
1669
                else
1670
                {
1671
                    /* it's non-zero and non-nil - jump */
1672
                    p += osrp2s(p);
1673
                }
1674
1675
                /* discard the value */
1676
                G_stk->discard();
1677
                break;
1678
1679
            case OPC_JR0T:
1680
                /* 
1681
                 *   if R0 is true, or it's a non-zero numeric value, or any
1682
                 *   non-numeric and non-boolean value, jump 
1683
                 */
1684
                if (r0_.typ == VM_NIL
1685
                    || (r0_.typ == VM_INT && r0_.val.intval == 0))
1686
                {
1687
                    /* it's zero or nil - do not jump */
1688
                    p += 2;
1689
                }
1690
                else
1691
                {
1692
                    /* it's non-zero and non-nil - jump */
1693
                    p += osrp2s(p);
1694
                }
1695
                break;
1696
1697
            case OPC_JF:
1698
                /* get the value */
1699
                valp = G_stk->get(0);
1700
1701
                /* 
1702
                 *   if it's true, or a non-zero numeric value, or any
1703
                 *   non-numeric and non-boolean value, do not jump;
1704
                 *   otherwise, jump 
1705
                 */
1706
                if (valp->typ == VM_NIL
1707
                    || (valp->typ == VM_INT && valp->val.intval == 0))
1708
                {
1709
                    /* it's zero or nil - jump */
1710
                    p += osrp2s(p);
1711
                }
1712
                else
1713
                {
1714
                    /* it's non-zero and non-nil - do not jump */
1715
                    p += 2;
1716
                }
1717
1718
                /* discard the value */
1719
                G_stk->discard();
1720
                break;
1721
1722
            case OPC_JR0F:
1723
                /* 
1724
                 *   if R0 is true, or it's a non-zero numeric value, or any
1725
                 *   non-numeric and non-boolean value, stay put; otherwise,
1726
                 *   jump 
1727
                 */
1728
                if (r0_.typ == VM_NIL
1729
                    || (r0_.typ == VM_INT && r0_.val.intval == 0))
1730
                {
1731
                    /* it's zero or nil - jump */
1732
                    p += osrp2s(p);
1733
                }
1734
                else
1735
                {
1736
                    /* it's non-zero and non-nil - do not jump */
1737
                    p += 2;
1738
                }
1739
                break;
1740
1741
            case OPC_JE:
1742
                /* jump if the two values at top of stack are equal */
1743
                p += (pop2_equal(vmg0_) ? osrp2s(p) : 2);
1744
                break;
1745
1746
            case OPC_JNE:
1747
                /* jump if the two values at top of stack are not equal */
1748
                p += (!pop2_equal(vmg0_) ? osrp2s(p) : 2);
1749
                break;
1750
1751
            case OPC_JGT:
1752
                /* jump if greater */
1753
                p += (pop2_compare_gt(vmg0_) ? osrp2s(p) : 2);
1754
                break;
1755
1756
            case OPC_JGE:
1757
                /* jump if greater or equal */
1758
                p += (pop2_compare_ge(vmg0_) ? osrp2s(p) : 2);
1759
                break;
1760
1761
            case OPC_JLT:
1762
                /* jump if less */
1763
                p += (pop2_compare_lt(vmg0_) ? osrp2s(p) : 2);
1764
                break;
1765
1766
            case OPC_JLE:
1767
                /* jump if less or equal */
1768
                p += (pop2_compare_le(vmg0_) ? osrp2s(p) : 2);
1769
                break;
1770
1771
            case OPC_JST:
1772
                /* get (do not remove) the element at top of stack */
1773
                valp = G_stk->get(0);
1774
1775
                /* 
1776
                 *   if it's true or a non-zero number, jump, saving the
1777
                 *   value; otherwise, require that it be a logical value,
1778
                 *   pop it, and proceed 
1779
                 */
1780
                if (valp->typ == VM_TRUE
1781
                    || valp->typ == VM_ENUM
1782
                    || valp->typ == VM_INT && !valp->num_is_zero())
1783
                {
1784
                    /* it's true - save it and jump */
1785
                    p += osrp2s(p);
1786
                }
1787
                else
1788
                {
1789
                    /* 
1790
                     *   it's not true - discard the value, but require
1791
                     *   that it be a valid logical value 
1792
                     */
1793
                    if (valp->typ != VM_NIL && valp->typ != VM_INT)
1794
                        err_throw(VMERR_LOG_VAL_REQD);
1795
                    G_stk->discard();
1796
1797
                    /* skip to the next instruction */
1798
                    p += 2;
1799
                }
1800
                break;
1801
1802
            case OPC_JSF:
1803
                /* get (do not remove) the element at top of stack */
1804
                valp = G_stk->get(0);
1805
1806
                /* 
1807
                 *   if it's nil or zero, jump, saving the value;
1808
                 *   otherwise, discard the value and proceed 
1809
                 */
1810
                if (valp->typ == VM_NIL
1811
                    || valp->typ == VM_INT && valp->num_is_zero())
1812
                {
1813
                    /* it's nil or zero - save it and jump */
1814
                    p += osrp2s(p);
1815
                }
1816
                else
1817
                {
1818
                    /* it's something non-false - discard it */
1819
                    G_stk->discard();
1820
1821
                    /* skip to the next instruction */
1822
                    p += 2;
1823
                }
1824
                break;
1825
1826
            case OPC_LJSR:
1827
                /* 
1828
                 *   compute and push the offset of the next instruction
1829
                 *   (at +2 because of the branch offset operand) from our
1830
                 *   method header - this will be the return address,
1831
                 *   which in this offset format will survive any code
1832
                 *   swapping that might occur in subsequent execution 
1833
                 */
1834
                push_int(vmg_ pc_to_method_ofs(p + 2));
1835
1836
                /* jump to the target address */
1837
                p += osrp2s(p);
1838
                break;
1839
1840
            case OPC_LRET:
1841
                /* get the indicated local variable */
1842
                valp = get_local(vmg_ get_op_uint16(&p));
1843
                
1844
                /* the value must be an integer */
1845
                if (valp->typ != VM_INT)
1846
                    err_throw(VMERR_INT_VAL_REQD);
1847
                
1848
                /* 
1849
                 *   jump to the code address obtained from adding the
1850
                 *   integer value in the given local variable to the
1851
                 *   current method header pointer 
1852
                 */
1853
                p = entry_ptr_native_ + valp->val.intval;
1854
                break;
1855
1856
            case OPC_JNIL:
1857
                /* jump if top of stack is nil */
1858
                valp = G_stk->get(0);
1859
                p += (valp->typ == VM_NIL ? osrp2s(p) : 2);
1860
1861
                /* discard the top value, regardless of what happened */
1862
                G_stk->discard();
1863
                break;
1864
1865
            case OPC_JNOTNIL:
1866
                /* jump if top of stack is not nil */
1867
                valp = G_stk->get(0);
1868
                p += (valp->typ != VM_NIL ? osrp2s(p) : 2);
1869
1870
                /* discard the top value, regardless of what happened */
1871
                G_stk->discard();
1872
                break;
1873
1874
            case OPC_SAY:
1875
                /* get the string offset */
1876
                ofs = get_op_int32(&p);
1877
1878
                /* display it */
1879
                p = disp_dstring(vmg_ ofs, p - entry_ptr_native_,
1880
                                 get_self_check(vmg0_));
1881
                break;
1882
1883
            case OPC_SAYVAL:
1884
                /* invoke the default string display function */
1885
                p = disp_string_val(vmg_ p - entry_ptr_native_,
1886
                                    get_self_check(vmg0_));
1887
                break;
1888
1889
            case OPC_THROW:
1890
                /* pop the exception object */
1891
                pop_obj(vmg_ &val);
1892
1893
                /* 
1894
                 *   Throw it.  Note that we pass the start of the current
1895
                 *   instruction as the program counter, since we want to
1896
                 *   find the exception handler (if any) for the current
1897
                 *   instruction, not for the next instruction. 
1898
                 */
1899
                if ((p = do_throw(vmg_ p - 1, val.val.obj)) == 0)
1900
                {
1901
                    /* remember the unhandled exception for re-throwing */
1902
                    unhandled_exc = val.val.obj;
1903
1904
                    /* terminate execution */
1905
                    goto exit_loop;
1906
                }
1907
                break;
1908
1909
            case OPC_INHERIT:
1910
                /* get the argument count */
1911
                argc = get_op_uint8(&p);
1912
1913
            do_opc_inherit:
1914
                /* inherit the property */
1915
                prop = (vm_prop_id_t)get_op_uint16(&p);
1916
                p = inh_prop(vmg_ p - entry_ptr_native_, prop, argc);
1917
                break;
1918
1919
            case OPC_PTRINHERIT:
1920
                /* get the argument count */
1921
                argc = get_op_uint8(&p);
1922
1923
            do_opc_ptrinherit:
1924
                /* pop the property to be inherited */
1925
                pop_prop(vmg_ &val);
1926
1927
                /* inherit it */
1928
                p = inh_prop(vmg_ p - entry_ptr_native_, val.val.prop, argc);
1929
                break;
1930
1931
            case OPC_EXPINHERIT:
1932
                /* get the argument count */
1933
                argc = get_op_uint8(&p);
1934
1935
            do_opc_expinherit:
1936
                /* get the property to inherit */
1937
                prop = (vm_prop_id_t)get_op_uint16(&p);
1938
1939
                /* get the superclass to inherit it from */
1940
                val.set_obj((vm_obj_id_t)get_op_uint32(&p));
1941
1942
                /* 
1943
                 *   inherit it -- process this essentially the same way
1944
                 *   as a normal CALLPROP, since we're going to evaluate
1945
                 *   the given property of the given object, but retain
1946
                 *   the current 'self' object 
1947
                 */
1948
                val2.set_obj(get_self(vmg0_));
1949
                p = get_prop(vmg_ p - entry_ptr_native_,
1950
                             &val, prop, &val2, argc);
1951
                break;
1952
1953
            case OPC_PTREXPINHERIT:
1954
                /* get the argument count */
1955
                argc = get_op_uint8(&p);
1956
1957
            do_opc_ptrexpinherit:
1958
                /* pop the property to inherit */
1959
                pop_prop(vmg_ &val);
1960
1961
                /* get the superclass to inherit it from */
1962
                val3.set_obj((vm_obj_id_t)get_op_uint32(&p));
1963
1964
                /* inherit it */
1965
                val2.set_obj(get_self(vmg0_));
1966
                p = get_prop(vmg_ p - entry_ptr_native_,
1967
                             &val3, val.val.prop, &val2, argc);
1968
                break;
1969
1970
            case OPC_DELEGATE:
1971
                /* get the argument count */
1972
                argc = get_op_uint8(&p);
1973
1974
            do_opc_delegate:
1975
                /* get the property to inherit */
1976
                prop = (vm_prop_id_t)get_op_uint16(&p);
1977
1978
                /* get the object to delegate to */
1979
                G_stk->pop(&val);
1980
1981
                /* delegate it */
1982
                val2.set_obj(get_self(vmg0_));
1983
                p = get_prop(vmg_ p - entry_ptr_native_,
1984
                             &val, prop, &val2, argc);
1985
                break;
1986
1987
            case OPC_PTRDELEGATE:
1988
                /* get the argument count */
1989
                argc = get_op_uint8(&p);
1990
1991
            do_opc_ptrdelegate:
1992
                /* pop the property to delegate to */
1993
                pop_prop(vmg_ &val);
1994
1995
                /* pop the object to delegate to */
1996
                G_stk->pop(&val2);
1997
1998
                /* delegate it */
1999
                val3.set_obj(get_self(vmg0_));
2000
                p = get_prop(vmg_ p - entry_ptr_native_,
2001
                             &val2, val.val.prop, &val3, argc);
2002
                break;
2003
2004
            case OPC_BUILTIN_A:
2005
                /* get the function index and argument count */
2006
                argc = get_op_uint8(&p);
2007
2008
            do_opc_builtin_a:
2009
                idx = get_op_uint8(&p);
2010
2011
                /* call the function in set #0 */
2012
                call_bif(vmg_ 0, idx, argc);
2013
                break;
2014
2015
            case OPC_BUILTIN_B:
2016
                /* get the function index and argument count */
2017
                argc = get_op_uint8(&p);
2018
2019
            do_opc_builtin_b:
2020
                idx = get_op_uint8(&p);
2021
2022
                /* call the function in set #1 */
2023
                call_bif(vmg_ 1, idx, argc);
2024
                break;
2025
2026
            case OPC_BUILTIN_C:
2027
                /* get the function index and argument count */
2028
                argc = get_op_uint8(&p);
2029
2030
            do_opc_builtin_c:
2031
                idx = get_op_uint8(&p);
2032
2033
                /* call the function in set #2 */
2034
                call_bif(vmg_ 2, idx, argc);
2035
                break;
2036
2037
            case OPC_BUILTIN_D:
2038
                /* get the function index and argument count */
2039
                argc = get_op_uint8(&p);
2040
2041
            do_opc_builtin_d:
2042
                idx = get_op_uint8(&p);
2043
2044
                /* call the function in set #3 */
2045
                call_bif(vmg_ 3, idx, argc);
2046
                break;
2047
2048
            case OPC_BUILTIN1:
2049
                /* get the function index and argument count */
2050
                argc = get_op_uint8(&p);
2051
2052
            do_opc_builtin1:
2053
                idx = get_op_uint8(&p);
2054
2055
                /* get the function set ID */
2056
                set_idx = get_op_uint8(&p);
2057
2058
                /* call the function in set #0 */
2059
                call_bif(vmg_ set_idx, idx, argc);
2060
                break;
2061
2062
            case OPC_BUILTIN2:
2063
                /* get the function index and argument count */
2064
                argc = get_op_uint8(&p);
2065
2066
            do_opc_builtin2:
2067
                idx = get_op_uint16(&p);
2068
2069
                /* get the function set ID */
2070
                set_idx = get_op_uint8(&p);
2071
2072
                /* call the function in set #0 */
2073
                call_bif(vmg_ set_idx, idx, argc);
2074
                break;
2075
2076
            case OPC_CALLEXT:
2077
                //$$$
2078
                err_throw(VMERR_CALLEXT_NOT_IMPL);
2079
                break;
2080
2081
            case OPC_INDEX:
2082
                /* 
2083
                 *   make a safe copy of the object to index, as we're going
2084
                 *   to store the result directly over that stack slot 
2085
                 */
2086
                val = *(valp = G_stk->get(1));
2087
2088
                /* index val by TOS, storing the result at TOS-1 */
2089
                apply_index(vmg_ valp, &val, G_stk->get(0));
2090
2091
                /* discard the index value */
2092
                G_stk->discard();
2093
                break;
2094
2095
            case OPC_IDXLCL1INT8:
2096
                /* get the local */
2097
                valp = get_local(vmg_ get_op_uint8(&p));
2098
                
2099
                /* get the index value */
2100
                val2.set_int(get_op_uint8(&p));
2101
2102
                /* 
2103
                 *   look up the indexed value of the local, storing the
2104
                 *   result in a newly-pushed stack element 
2105
                 */
2106
                apply_index(vmg_ G_stk->push(), valp, &val2);
2107
                break;
2108
2109
            case OPC_IDXINT8:
2110
                /* 
2111
                 *   make a copy of the value to index, so we can overwrite
2112
                 *   the stack slot with the result 
2113
                 */
2114
                val = *(valp = G_stk->get(0));
2115
2116
                /* set up the index value */
2117
                val2.set_int(get_op_uint8(&p));
2118
2119
                /* apply the index, storing the result at TOS */
2120
                apply_index(vmg_ valp, &val, &val2);
2121
                break;
2122
2123
            case OPC_BP:
2124
                /* step back to the breakpoint location itself */
2125
                VM_IF_DEBUGGER(--p);
2126
2127
                /* let the debugger take control */
2128
                VM_IF_DEBUGGER(G_debugger
2129
                               ->step(vmg_ &p, entry_ptr_, TRUE, 0));
2130
2131
                /* if there's no debugger, it's an error */
2132
                VM_IF_NOT_DEBUGGER(err_throw(VMERR_BREAKPOINT));
2133
2134
                /* 
2135
                 *   go back and execute the current instruction - bypass
2136
                 *   single-step tracing into the debugger in this case,
2137
                 *   since the debugger expects when it returns that one
2138
                 *   instruction will always be traced before the debugger
2139
                 *   is re-entered 
2140
                 */
2141
                goto exec_instruction;
2142
2143
            case OPC_NOP:
2144
                /* NO OP - no effect */
2145
                break;
2146
2147
            case OPC_TRNEW1:
2148
                trans = TRUE;
2149
                goto do_opc_new1;
2150
2151
            case OPC_NEW1:
2152
                trans = FALSE;
2153
                /* fall through to do_opc_new1 */
2154
2155
            do_opc_new1:
2156
                /* get the argument count */
2157
                argc = get_op_uint8(&p);
2158
2159
                /* fall through to do_opc_new1_argc */
2160
2161
            do_opc_new1_argc:
2162
                /* get the metaclass ID */
2163
                idx = get_op_uint8(&p);
2164
                
2165
                /* create the new object */
2166
                p = new_and_store_r0(vmg_ p, idx, argc, trans);
2167
                break;
2168
2169
            case OPC_TRNEW2:
2170
                trans = TRUE;
2171
                goto do_opc_new2;
2172
2173
            case OPC_NEW2:
2174
                trans = FALSE;
2175
                /* fall through to do_opc_new2 */
2176
2177
            do_opc_new2:
2178
                /* get the argument count */
2179
                argc = get_op_uint16(&p);
2180
2181
                /* fall through to do_opc_new2_argc */
2182
2183
            do_opc_new2_argc:
2184
                /* get the metaclass ID */
2185
                idx = get_op_uint16(&p);
2186
2187
                /* create the new object */
2188
                p = new_and_store_r0(vmg_ p, idx, argc, trans);
2189
                break;
2190
2191
            case OPC_INCLCL:
2192
                /* get the local */
2193
                valp = get_local(vmg_ get_op_uint16(&p));
2194
                
2195
                /* check if it's a number */
2196
                if (valp->is_numeric())
2197
                {
2198
                    /* it's a number - just increment the value */
2199
                    ++(valp->val.intval);
2200
                }
2201
                else
2202
                {
2203
                    /* it's a non-numeric value - do the full addition */
2204
                    val2.set_int(1);
2205
                    compute_sum(vmg_ valp, &val2);
2206
                }
2207
                break;
2208
2209
            case OPC_DECLCL:
2210
                /* get the local */
2211
                valp = get_local(vmg_ get_op_uint16(&p));
2212
2213
                /* check for a number */
2214
                if (valp->is_numeric())
2215
                {
2216
                    /* it's a number - just decrement the value */
2217
                    --(valp->val.intval);
2218
                }
2219
                else
2220
                {
2221
                    /* non-numeric - we must do the full subtraction work */
2222
                    val2.set_int(1);
2223
                    compute_diff(vmg_ valp, &val2);
2224
                }
2225
                break;
2226
2227
            case OPC_ADDILCL1:
2228
                /* get the local */
2229
                valp = get_local(vmg_ get_op_uint8(&p));
2230
2231
                /* if it's numeric, handle it in-line */
2232
                if (valp->is_numeric())
2233
                {
2234
                    /* it's a number - just add the value */
2235
                    valp->val.intval += get_op_int8(&p);
2236
                }
2237
                else
2238
                {
2239
                    /* get the number to add */
2240
                    val2.set_int(get_op_int8(&p));
2241
2242
                    /* compute the sum, leaving the result in the local */
2243
                    compute_sum(vmg_ valp, &val2);
2244
                }
2245
                break;
2246
2247
            case OPC_ADDILCL4:
2248
                /* get the local */
2249
                valp = get_local(vmg_ get_op_uint16(&p));
2250
2251
                /* if it's a number, handle it in-line */
2252
                if (valp->is_numeric())
2253
                {
2254
                    /* it's a number - just add the value */
2255
                    valp->val.intval += get_op_int32(&p);
2256
                }
2257
                else
2258
                {
2259
                    /* get the number to add */
2260
                    val2.set_int(get_op_int32(&p));
2261
2262
                    /* compute the sum, leaving the result in the local */
2263
                    compute_sum(vmg_ valp, &val2);
2264
                }
2265
                break;
2266
2267
            case OPC_ADDTOLCL:
2268
                /* get the local */
2269
                valp = get_local(vmg_ get_op_uint16(&p));
2270
2271
                /* get the value to add */
2272
                valp2 = G_stk->get(0);
2273
2274
                /* if they're both numeric, handle in-line */
2275
                if (valp->is_numeric() && valp2->is_numeric())
2276
                {
2277
                    /* add the value to the local */
2278
                    valp->val.intval += valp2->val.intval;
2279
                }
2280
                else
2281
                {
2282
                    /* compute the sum, leaving the result in the local */
2283
                    compute_sum(vmg_ valp, valp2);
2284
                }
2285
2286
                /* discard the addend */
2287
                G_stk->discard();
2288
                break;
2289
2290
            case OPC_SUBFROMLCL:
2291
                /* get the local */
2292
                valp = get_local(vmg_ get_op_uint16(&p));
2293
2294
                /* get the value to add */
2295
                valp2 = G_stk->get(0);
2296
2297
                /* if they're both numeric, handle in-line */
2298
                if (valp->is_numeric() && valp2->is_numeric())
2299
                {
2300
                    /* subtract the value from the local */
2301
                    valp->val.intval -= valp2->val.intval;
2302
                }
2303
                else
2304
                {
2305
                    /* subtract the values, leaving the result in the local */
2306
                    compute_diff(vmg_ valp, valp2);
2307
                }
2308
2309
                /* discard the value subtracted */
2310
                G_stk->discard();
2311
                break;
2312
2313
            case OPC_ZEROLCL1:
2314
                /* get the local and set it to zero */
2315
                get_local(vmg_ get_op_uint8(&p))->set_int(0);
2316
                break;
2317
2318
            case OPC_ZEROLCL2:
2319
                /* get the local and set it to zero */
2320
                get_local(vmg_ get_op_uint16(&p))->set_int(0);
2321
                break;
2322
2323
            case OPC_NILLCL1:
2324
                /* get the local and set it to zero */
2325
                get_local(vmg_ get_op_uint8(&p))->set_nil();
2326
                break;
2327
2328
            case OPC_NILLCL2:
2329
                /* get the local and set it to zero */
2330
                get_local(vmg_ get_op_uint16(&p))->set_nil();
2331
                break;
2332
2333
            case OPC_ONELCL1:
2334
                /* get the local and set it to zero */
2335
                get_local(vmg_ get_op_uint8(&p))->set_int(1);
2336
                break;
2337
2338
            case OPC_ONELCL2:
2339
                /* get the local and set it to zero */
2340
                get_local(vmg_ get_op_uint16(&p))->set_int(1);
2341
                break;
2342
2343
            case OPC_SETLCL1:
2344
                /* get a pointer to the local */
2345
                valp = get_local(vmg_ get_op_uint8(&p));
2346
                
2347
                /* pop the value into the local */
2348
                popval(vmg_ valp);
2349
                break;
2350
2351
            case OPC_SETLCL2:
2352
                /* get a pointer to the local */
2353
                valp = get_local(vmg_ get_op_uint16(&p));
2354
2355
                /* pop the value into the local */
2356
                popval(vmg_ valp);
2357
                break;
2358
2359
            case OPC_SETLCL1R0:
2360
                /* store R0 in the specific local */
2361
                *get_local(vmg_ get_op_uint8(&p)) = r0_;
2362
                break;
2363
2364
            case OPC_SETARG1:
2365
                /* get a pointer to the parameter */
2366
                valp = get_param(vmg_ get_op_uint8(&p));
2367
2368
                /* pop the value into the parameter */
2369
                popval(vmg_ valp);
2370
                break;
2371
2372
            case OPC_SETARG2:
2373
                /* get a pointer to the parameter */
2374
                valp = get_param(vmg_ get_op_uint16(&p));
2375
2376
                /* pop the value into the parameter */
2377
                popval(vmg_ valp);
2378
                break;
2379
2380
            case OPC_SETIND:
2381
                /* pop the index */
2382
                popval(vmg_ &val2);
2383
2384
                /* pop the value to be indexed */
2385
                popval(vmg_ &val);
2386
2387
                /* pop the value to assign */
2388
                popval(vmg_ &val3);
2389
2390
                /* assign into the index */
2391
                set_index(vmg_ &val, &val2, &val3);
2392
2393
                /* push the new container value */
2394
                pushval(vmg_ &val);
2395
                break;
2396
2397
            case OPC_SETINDLCL1I8:
2398
                /* get the local */
2399
                valp = get_local(vmg_ get_op_uint8(&p));
2400
2401
                /* get the index value */
2402
                val2.set_int(get_op_uint8(&p));
2403
2404
                /* pop the value to assign */
2405
                popval(vmg_ &val3);
2406
2407
                /* 
2408
                 *   set the index value - this will update the local
2409
                 *   variable directly if the container value changes 
2410
                 */
2411
                set_index(vmg_ valp, &val2, &val3);
2412
                break;
2413
2414
            case OPC_SETPROP:
2415
                /* get the object whose property we're setting */
2416
                pop_obj(vmg_ &val);
2417
2418
                /* pop the value we're setting */
2419
                popval(vmg_ &val2);
2420
2421
                /* set the value */
2422
                set_prop(vmg_ val.val.obj, get_op_uint16(&p), &val2);
2423
                break;
2424
2425
            case OPC_PTRSETPROP:
2426
                /* get the property and object to set */
2427
                pop_prop(vmg_ &val);
2428
                pop_obj(vmg_ &val2);
2429
2430
                /* get the value to set */
2431
                popval(vmg_ &val3);
2432
2433
                /* set it */
2434
                set_prop(vmg_ val2.val.obj, val.val.prop, &val3);
2435
                break;
2436
2437
            case OPC_SETPROPSELF:
2438
                /* get the value to set */
2439
                popval(vmg_ &val);
2440
2441
                /* set it */
2442
                set_prop(vmg_ get_self(vmg0_), get_op_uint16(&p), &val);
2443
                break;
2444
2445
            case OPC_OBJSETPROP:
2446
                /* get the objet */
2447
                obj = (vm_obj_id_t)get_op_uint32(&p);
2448
2449
                /* get the new value */
2450
                popval(vmg_ &val);
2451
2452
                /* set the property */
2453
                set_prop(vmg_ obj, get_op_uint16(&p), &val);
2454
                break;
2455
2456
#ifdef OS_FILL_OUT_CASE_TABLES
2457
            /*
2458
             *   Since we this switch is the innermost inner loop of the VM,
2459
             *   we go to some extra lengths to optimize it where possible.
2460
             *   See tads2/osifc.h for information on how to use
2461
             *   OS_FILL_OUT_CASE_TABLES and OS_IMPOSSIBLE_DEFAULT_CASE.
2462
             *   
2463
             *   Our controlling expression is an unsigned character value,
2464
             *   so we know the range of possible values will be limited to
2465
             *   0-255.  Therefore, we simply need to provide a "case"
2466
             *   alternative for every invalid opcode.  To further encourage
2467
             *   the compiler to favor speed here, we specifically put
2468
             *   different code in every one of these case alternatives, to
2469
             *   force the compiler to generate a separate jump location for
2470
             *   each one; some compilers will generate a two-level jump
2471
             *   table if many cases point to shared code, to reduce the size
2472
             *   of the table, but we don't want that here because this
2473
             *   switch is critical to VM performance so we want it as fast
2474
             *   as possible.  
2475
             */
2476
            case 0x00: val.val.intval = 0x00;
2477
            case 0x10: val.val.intval = 0x10;
2478
            case 0x11: val.val.intval = 0x11;
2479
            case 0x12: val.val.intval = 0x12;
2480
            case 0x13: val.val.intval = 0x13;
2481
            case 0x14: val.val.intval = 0x14;
2482
            case 0x15: val.val.intval = 0x15;
2483
            case 0x16: val.val.intval = 0x16;
2484
            case 0x17: val.val.intval = 0x17;
2485
            case 0x18: val.val.intval = 0x18;
2486
            case 0x19: val.val.intval = 0x19;
2487
            case 0x1A: val.val.intval = 0x1A;
2488
            case 0x1B: val.val.intval = 0x1B;
2489
            case 0x1C: val.val.intval = 0x1C;
2490
            case 0x1D: val.val.intval = 0x1D;
2491
            case 0x1E: val.val.intval = 0x1E;
2492
            case 0x1F: val.val.intval = 0x1F;
2493
            case 0x30: val.val.intval = 0x30;
2494
            case 0x31: val.val.intval = 0x31;
2495
            case 0x32: val.val.intval = 0x32;
2496
            case 0x33: val.val.intval = 0x33;
2497
            case 0x34: val.val.intval = 0x34;
2498
            case 0x35: val.val.intval = 0x35;
2499
            case 0x36: val.val.intval = 0x36;
2500
            case 0x37: val.val.intval = 0x37;
2501
            case 0x38: val.val.intval = 0x38;
2502
            case 0x39: val.val.intval = 0x39;
2503
            case 0x3A: val.val.intval = 0x3A;
2504
            case 0x3B: val.val.intval = 0x3B;
2505
            case 0x3C: val.val.intval = 0x3C;
2506
            case 0x3D: val.val.intval = 0x3D;
2507
            case 0x3E: val.val.intval = 0x3E;
2508
            case 0x3F: val.val.intval = 0x3F;
2509
            case 0x46: val.val.intval = 0x46;
2510
            case 0x47: val.val.intval = 0x47;
2511
            case 0x48: val.val.intval = 0x48;
2512
            case 0x49: val.val.intval = 0x49;
2513
            case 0x4A: val.val.intval = 0x4A;
2514
            case 0x4B: val.val.intval = 0x4B;
2515
            case 0x4C: val.val.intval = 0x4C;
2516
            case 0x4D: val.val.intval = 0x4D;
2517
            case 0x4E: val.val.intval = 0x4E;
2518
            case 0x4F: val.val.intval = 0x4F;
2519
            case 0x53: val.val.intval = 0x53;
2520
            case 0x55: val.val.intval = 0x55;
2521
            case 0x56: val.val.intval = 0x56;
2522
            case 0x57: val.val.intval = 0x57;
2523
            case 0x5A: val.val.intval = 0x5A;
2524
            case 0x5B: val.val.intval = 0x5B;
2525
            case 0x5C: val.val.intval = 0x5C;
2526
            case 0x5D: val.val.intval = 0x5D;
2527
            case 0x5E: val.val.intval = 0x5E;
2528
            case 0x5F: val.val.intval = 0x5F;
2529
            case 0x6E: val.val.intval = 0x6E;
2530
            case 0x6F: val.val.intval = 0x6F;
2531
            case 0x70: val.val.intval = 0x70;
2532
            case 0x71: val.val.intval = 0x71;
2533
            case 0x79: val.val.intval = 0x79;
2534
            case 0x7A: val.val.intval = 0x7A;
2535
            case 0x7B: val.val.intval = 0x7B;
2536
            case 0x7C: val.val.intval = 0x7C;
2537
            case 0x7D: val.val.intval = 0x7D;
2538
            case 0x7E: val.val.intval = 0x7E;
2539
            case 0x7F: val.val.intval = 0x7F;
2540
            case 0x8F: val.val.intval = 0x8F;
2541
            case 0xA2: val.val.intval = 0xA2;
2542
            case 0xA3: val.val.intval = 0xA3;
2543
            case 0xA4: val.val.intval = 0xA4;
2544
            case 0xA5: val.val.intval = 0xA5;
2545
            case 0xA6: val.val.intval = 0xA6;
2546
            case 0xA7: val.val.intval = 0xA7;
2547
            case 0xA8: val.val.intval = 0xA8;
2548
            case 0xA9: val.val.intval = 0xA9;
2549
            case 0xAA: val.val.intval = 0xAA;
2550
            case 0xAB: val.val.intval = 0xAB;
2551
            case 0xAC: val.val.intval = 0xAC;
2552
            case 0xAD: val.val.intval = 0xAD;
2553
            case 0xAE: val.val.intval = 0xAE;
2554
            case 0xAF: val.val.intval = 0xAF;
2555
            case 0xBD: val.val.intval = 0xBD;
2556
            case 0xBE: val.val.intval = 0xBE;
2557
            case 0xBF: val.val.intval = 0xBF;
2558
            case 0xC4: val.val.intval = 0xC4;
2559
            case 0xC5: val.val.intval = 0xC5;
2560
            case 0xC6: val.val.intval = 0xC6;
2561
            case 0xC7: val.val.intval = 0xC7;
2562
            case 0xC8: val.val.intval = 0xC8;
2563
            case 0xC9: val.val.intval = 0xC9;
2564
            case 0xCA: val.val.intval = 0xCA;
2565
            case 0xCB: val.val.intval = 0xCB;
2566
            case 0xCC: val.val.intval = 0xCC;
2567
            case 0xCD: val.val.intval = 0xCD;
2568
            case 0xCE: val.val.intval = 0xCE;
2569
            case 0xCF: val.val.intval = 0xCF;
2570
            case 0xDC: val.val.intval = 0xDC;
2571
            case 0xDD: val.val.intval = 0xDD;
2572
            case 0xDE: val.val.intval = 0xDE;
2573
            case 0xDF: val.val.intval = 0xDF;
2574
            case 0xF0: val.val.intval = 0xF0;
2575
            case 0xF3: val.val.intval = 0xF3;
2576
            case 0xF4: val.val.intval = 0xF4;
2577
            case 0xF5: val.val.intval = 0xF5;
2578
            case 0xF6: val.val.intval = 0xF6;
2579
            case 0xF7: val.val.intval = 0xF7;
2580
            case 0xF8: val.val.intval = 0xF8;
2581
            case 0xF9: val.val.intval = 0xF9;
2582
            case 0xFA: val.val.intval = 0xFA;
2583
            case 0xFB: val.val.intval = 0xFB;
2584
            case 0xFC: val.val.intval = 0xFC;
2585
            case 0xFD: val.val.intval = 0xFD;
2586
            case 0xFE: val.val.intval = 0xFE;
2587
            case 0xFF: val.val.intval = 0xFF;
2588
                err_throw(VMERR_INVALID_OPCODE);
2589
2590
            OS_IMPOSSIBLE_DEFAULT_CASE
2591
2592
#else /* OS_FILL_OUT_CASE_TABLES */
2593
            case 0:
2594
                /* 
2595
                 *   Explicitly call out this invalid instruction case so
2596
                 *   that we can avoid extra work in computing the switch.
2597
                 *   Some compilers will be smart enough to observe that we
2598
                 *   populate the full range of possible values (0-255) for
2599
                 *   the datatype of the switch control expression, and thus
2600
                 *   will build jump tables that can be jumped through
2601
                 *   without range-checking the value.  (No range checking
2602
                 *   is necessary, because a uchar simply cannot hold any
2603
                 *   values outside of the 0-255 range.)  This doesn't
2604
                 *   guarantee that the compiler will be smart, but it does
2605
                 *   help with some compilers and shouldn't hurt performance
2606
                 *   with those that don't make any use of the situation.  
2607
                 */
2608
                err_throw(VMERR_INVALID_OPCODE);
2609
                
2610
            case 0xFF:
2611
                /* 
2612
                 *   explicitly call out this invalid instruction for the
2613
                 *   same reasons we call out case 0 above 
2614
                 */
2615
                err_throw(VMERR_INVALID_OPCODE);
2616
2617
            default:
2618
                /* unrecognized opcode */
2619
                err_throw(VMERR_INVALID_OPCODE);
2620
                break;
2621
2622
#endif /* OS_FILL_OUT_CASE_TABLES */
2623
            }
2624
        }
2625
2626
        /*
2627
         *   We jump to this label when it's time to terminate execution
2628
         *   and return to the host environment which called us. 
2629
         */
2630
    exit_loop:
2631
        /* note that we're ready to return */
2632
        done = TRUE;
2633
    }
2634
    err_catch(err)
2635
    {
2636
        int i;
2637
        volatile int released_reserve = FALSE;
2638
2639
        err_try
2640
        {
2641
            /* 
2642
             *   Return to the start of the most recent instruction - we've
2643
             *   already at least partially decoded the instruction, so we
2644
             *   won't be pointing to its first byte.  Note that last_pc is
2645
             *   a non-register variable (because we take its address to
2646
             *   store in pc_ptr_), so it will correctly indicate the
2647
             *   current instruction even though we've jumped here via
2648
             *   longjmp.  
2649
             */
2650
            p = last_pc;
2651
                
2652
            /* 
2653
             *   Create a new exception object to describe the error.  The
2654
             *   arguments to the constructor are the error number and the
2655
             *   error parameters.
2656
             *   
2657
             *   If the error code is "unhandled exception," it means that
2658
             *   an exception occurred in a recursive interpreter
2659
             *   invocation, and the exception wasn't handled within the
2660
             *   code called recursively; in this case, we can simply
2661
             *   re-throw the original error, and perhaps handle it in the
2662
             *   context of the current code.  
2663
             */
2664
            if (err->get_error_code() == VMERR_UNHANDLED_EXC)
2665
            {
2666
                /* get the original exception object from the error stack */
2667
                obj = (vm_obj_id_t)err->get_param_ulong(0);
2668
            }
2669
            else
2670
            {
2671
                /* step into the debugger, if it's present */
2672
                VM_IF_DEBUGGER(
2673
                {
2674
                    const uchar *dbgp;
2675
                    
2676
                    /* 
2677
                     *   If we're in the process of halting the VM, don't
2678
                     *   bother stepping into the debugger.  We'll check the
2679
                     *   same thing in a moment, after we get back from
2680
                     *   stepping into the debugger, but this check isn't
2681
                     *   redundant: we could already be halting even before
2682
                     *   we enter the debugger here, because we could be
2683
                     *   unwinding the native (C++) error stack on our way
2684
                     *   out from such a halt. 
2685
                     */
2686
                    if (halt_vm_)
2687
                    {
2688
                        done = TRUE;
2689
                        goto skip_throw;
2690
                    }
2691
2692
                    /* make a copy of the PC for the debugger's use */
2693
                    dbgp = p;
2694
                    
2695
                    /* step into the debugger */
2696
                    G_debugger->step(vmg_ &dbgp, entry_ptr_, FALSE,
2697
                                     err->get_error_code());
2698
                    
2699
                    /* 
2700
                     *   if the VM was halted while in the debugger, stop
2701
                     *   running immediately - do not process the exception
2702
                     *   any further 
2703
                     */
2704
                    if (halt_vm_)
2705
                    {
2706
                        done = TRUE;
2707
                        goto skip_throw;
2708
                    }
2709
                    
2710
                    /* 
2711
                     *   if they moved the execution pointer, resume
2712
                     *   execution at the new point, discarding the
2713
                     *   exception 
2714
                     */
2715
                    if (dbgp != p)
2716
                    {
2717
                        /* resume execution at the new location */
2718
                        p = dbgp;
2719
                        
2720
                        /* discard the exception and resume execution */
2721
                        goto skip_throw;
2722
                    }
2723
                }
2724
                );
2725
2726
                /* 
2727
                 *   If this is a stack overflow exception, there's probably
2728
                 *   not enough stack left to create the exception object.
2729
                 *   Fortunately, we have an emergency stack reserve just for
2730
                 *   such conditions, so release it now, hopefully giving us
2731
                 *   enough room to work with to construct the exception.  
2732
                 */
2733
                if (err->get_error_code() == VMERR_STACK_OVERFLOW)
2734
                    released_reserve = G_stk->release_reserve();
2735
            
2736
                /* push the error parameters (in reverse order) */
2737
                for (i = err->get_param_count() ; i > 0 ; )
2738
                {
2739
                    /* go to the next parameter */
2740
                    --i;
2741
                    
2742
                    /* see what we have and push an appropriate value */
2743
                    switch(err->get_param_type(i-1))
2744
                    {
2745
                    case ERR_TYPE_INT:
2746
                        /* push the integer value */
2747
                        push_int(vmg_ err->get_param_int(i));
2748
                        break;
2749
2750
                    case ERR_TYPE_ULONG:
2751
                        /* push the value */
2752
                        push_int(vmg_ (int32)err->get_param_ulong(i));
2753
                        break;
2754
2755
                    case ERR_TYPE_TEXTCHAR:
2756
                        /* push a new string with the text */
2757
                        push_obj(vmg_ CVmObjString::create(vmg_ FALSE,
2758
                            err->get_param_text(i),
2759
                            get_strlen(err->get_param_text(i))));
2760
                        break;
2761
2762
                    case ERR_TYPE_CHAR:
2763
                        /* push a new string with the text */
2764
                        push_obj(vmg_ CVmObjString::create(vmg_ FALSE,
2765
                            err->get_param_char(i),
2766
                            strlen(err->get_param_char(i))));
2767
                        break;
2768
2769
                    default:
2770
                        /* unrecognized type - push nil for now */
2771
                        push_nil(vmg0_);
2772
                        break;
2773
                    }
2774
                }
2775
2776
                /* 
2777
                 *   if there's a RuntimeError base class defined, create an
2778
                 *   instance; otherwise, create a simple instance of the
2779
                 *   basic object type to throw as a placeholder, since the
2780
                 *   program hasn't made any provision to catch run-time
2781
                 *   errors 
2782
                 */
2783
                if (G_predef->rterr != VM_INVALID_OBJ)
2784
                {
2785
                    /* push the error number */
2786
                    push_int(vmg_ err->get_error_code());
2787
                    
2788
                    /* 
2789
                     *   If we're not in the debugger, set up a recursive
2790
                     *   call frame for the constructor invocation.  We'll
2791
                     *   do this on any recursive call into byte code if
2792
                     *   we're running in the debugger, so we only need to
2793
                     *   do this in the non-debug version.
2794
                     *   
2795
                     *   This extra recursive frame is needed in this one
2796
                     *   case when in non-debug mode because the constructor
2797
                     *   to the exception object might want to look at the
2798
                     *   stack trace.  In order for the location where the
2799
                     *   error actually occurred to be included in the stack
2800
                     *   trace, we need to push a recursive call frame that
2801
                     *   points back to that location.  
2802
                     */
2803
                    VM_IF_NOT_DEBUGGER(enter_recursive_frame(
2804
                        vmg_ err->get_param_count() + 1, &last_pc));
2805
2806
                    /* 
2807
                     *   Create the new RuntimeException instance.  Run the
2808
                     *   constructor in a recursive invocation of the
2809
                     *   interpreter (by passing a null PC pointer).  
2810
                     */
2811
                    vm_objp(vmg_ G_predef->rterr)
2812
                        ->create_instance(vmg_ G_predef->rterr, 0,
2813
                                          err->get_param_count() + 1);
2814
2815
                    /* get the object from R0 */
2816
                    if (r0_.typ != VM_OBJ)
2817
                        err_throw(VMERR_OBJ_VAL_REQD);
2818
                    obj = r0_.val.obj;
2819
                }
2820
                else
2821
                {
2822
                    /* 
2823
                     *   There's no RuntimeError object defined by the image
2824
                     *   file, so create a basic object to throw.  This
2825
                     *   won't convey any information to the program except
2826
                     *   that it's not one of the errors they're expecting;
2827
                     *   this is fine, since they have made no provisions to
2828
                     *   catch VM errors, as demonstrated by their lack of a
2829
                     *   RuntimeError definition.  
2830
                     */
2831
                    obj = CVmObjTads::create(vmg_ FALSE, 0, 1);
2832
                }
2833
2834
                /* 
2835
                 *   if possible, set the exceptionMessage property in the
2836
                 *   new exception object to the default error message for
2837
                 *   the run-time error we're processing 
2838
                 */
2839
                if (G_predef->rterrmsg_prop != VM_INVALID_PROP)
2840
                {
2841
                    const char *msg;
2842
                    char buf[256];
2843
                    vm_obj_id_t str_obj;
2844
2845
                    /* format the message text */
2846
                    msg = err_get_msg(vm_messages, vm_message_count,
2847
                                      err->get_error_code(), FALSE);
2848
                    err_format_msg(buf, sizeof(buf), msg, err);
2849
2850
                    /* 
2851
                     *   momentarily push the new exception object, so we
2852
                     *   don't lose track of it if we run garbage collection
2853
                     *   here 
2854
                     */
2855
                    push_obj(vmg_ obj);
2856
2857
                    /* create a string object with the message text */
2858
                    str_obj =
2859
                        CVmObjString::create(vmg_ FALSE, buf, strlen(buf));
2860
2861
                    /* 
2862
                     *   before we can build a stack trace, let the debugger
2863
                     *   synchronize its current position information 
2864
                     */
2865
                    VM_IF_DEBUGGER(
2866
                        G_debugger->sync_exec_pos(vmg_ p, entry_ptr_));
2867
                    
2868
                    /* set the property in the new object */
2869
                    val.set_obj(str_obj);
2870
                    vm_objp(vmg_ obj)
2871
                        ->set_prop(vmg_ G_undo, obj,
2872
                                   G_predef->rterrmsg_prop, &val);
2873
                    
2874
                    /* we don't need gc protection any more */
2875
                    G_stk->discard();
2876
                }
2877
            }
2878
        
2879
            /* 
2880
             *   If we released the stack reserve, take it back.  We've
2881
             *   finished creating the exception object, so we don't need the
2882
             *   emergency stack space any more.  We want to put it back now
2883
             *   that we're done with it so that it'll be there for us if we
2884
             *   should run into another stack overflow in the future.  
2885
             */
2886
            if (released_reserve)
2887
                G_stk->recover_reserve();
2888
2889
            /* throw the exception */
2890
            if ((p = do_throw(vmg_ p, obj)) == 0)
2891
            {
2892
                /* remember the unhandled exception for a moment */
2893
                unhandled_exc = obj;
2894
            }
2895
            
2896
            /* come here to skip throwing the exception */
2897
            VM_IF_DEBUGGER(skip_throw: );
2898
        }
2899
        err_catch(exc2)
2900
        {
2901
            /* 
2902
             *   we got another exception trying to handle the first
2903
             *   exception - just throw the error again, but at least clean
2904
             *   up statics on the way out 
2905
             */
2906
            pc_ptr_ = old_pc_ptr;
2907
2908
            /* if we released the stack reserve, take it back */
2909
            if (released_reserve)
2910
                G_stk->recover_reserve();
2911
2912
            /* re-throw the error */
2913
            err_rethrow();
2914
        }
2915
        err_end;
2916
    }
2917
    err_end;
2918
2919
    /* 
2920
     *   If an unhandled exception occurred, re-throw it.  This will wrap our
2921
     *   exception object in a C++ object and throw it through our C++
2922
     *   err_try/err_catch exception mechanism, so that the exception is
2923
     *   thrown out of the recursive native-code invoker.  
2924
     */
2925
    if (unhandled_exc != VM_INVALID_OBJ)
2926
    {
2927
        /* restore the enclosing PC pointer */
2928
        pc_ptr_ = old_pc_ptr;
2929
        
2930
        /* re-throw the unhandled exception */
2931
        err_throw_a(VMERR_UNHANDLED_EXC, 1,
2932
                    ERR_TYPE_ULONG, (unsigned long)unhandled_exc);
2933
    }
2934
2935
    /* if we're not done, go back and resume execution */
2936
    if (!done)
2937
        goto resume_execution;
2938
2939
    /* restore the enclosing PC pointer */
2940
    pc_ptr_ = old_pc_ptr;
2941
}
2942
2943
2944
/* ------------------------------------------------------------------------ */
2945
/*
2946
 *   Throw an exception of the given class, with the constructor arguments
2947
 *   on the stack.  
2948
 */
2949
void CVmRun::throw_new_class(VMG_ vm_obj_id_t cls, uint argc,
2950
                             const char *fallback_msg)
2951
{
2952
    /* if the class isn't defined, use the basic run-time exception */
2953
    if (cls != VM_INVALID_OBJ)
2954
    {
2955
        /* create the object */
2956
        vm_objp(vmg_ cls)->create_instance(vmg_ cls, 0, argc);
2957
        
2958
        /* make sure we created an object */
2959
        if (r0_.typ == VM_OBJ)
2960
        {
2961
            vm_obj_id_t exc_obj;
2962
            
2963
            /* get the object from R0 */
2964
            exc_obj = r0_.val.obj;
2965
            
2966
            /* 
2967
             *   throw an 'unhandled exception' with this object as the
2968
             *   parameter; the execution loop will catch it and dispatch it
2969
             *   properly 
2970
             */
2971
            err_throw_a(VMERR_UNHANDLED_EXC, 1,
2972
                        ERR_TYPE_ULONG, (unsigned long)exc_obj);
2973
        }
2974
    }
2975
    
2976
    /* 
2977
     *   the imported exception class isn't defined, or we failed to create
2978
     *   it; throw a generic intrinsic class exception with the fallback
2979
     *   message string 
2980
     */
2981
    err_throw_a(VMERR_INTCLS_GENERAL_ERROR, 1, ERR_TYPE_CHAR, fallback_msg);
2982
}
2983
2984
2985
/* ------------------------------------------------------------------------ */
2986
/*
2987
 *   Throw an exception.  Returns true if an exception handler was found,
2988
 *   which means that execution can proceed; returns false if no handler
2989
 *   was found, in which case the execution loop must throw the exception
2990
 *   to its caller.  
2991
 */
2992
const uchar *CVmRun::do_throw(VMG_ const uchar *pc, vm_obj_id_t exception_obj)
2993
{
2994
    /*
2995
     *   Search the stack for a handler for this exception class.  Start
2996
     *   at the current stack frame; if we find a handler here, use it;
2997
     *   otherwise, unwind the stack to the enclosing frame and search for
2998
     *   a handler there; repeat until we exhaust the stack.  
2999
     */
3000
    for (;;)
3001
    {
3002
        CVmExcTablePtr tab;
3003
        const uchar *func_start;
3004
        uint ofs;
3005
3006
        /* get a pointer to the start of the current function */
3007
        func_start = entry_ptr_native_;
3008
3009
        /* set up a pointer to the current exception table */
3010
        if (tab.set(func_start))
3011
        {
3012
            size_t cnt;
3013
            size_t i;
3014
            CVmExcEntryPtr entry;
3015
3016
            /* calculate our offset in the current function */
3017
            ofs = pc - func_start;
3018
3019
            /* set up a pointer to the first table entry */
3020
            tab.set_entry_ptr(vmg_ &entry, 0);
3021
            
3022
            /* loop through the entries */
3023
            for (i = 0, cnt = tab.get_count() ; i < cnt ;
3024
                 ++i, entry.inc(vmg0_))
3025
            {
3026
                /* 
3027
                 *   Check to see if we're in the range for this entry.
3028
                 *   If this entry covers the appropriate range, and the
3029
                 *   exception we're handling is of the class handled by
3030
                 *   this exception (or derives from that class), this
3031
                 *   handler handles this exception. 
3032
                 */
3033
                if (ofs >= entry.get_start_ofs()
3034
                    && ofs <= entry.get_end_ofs()
3035
                    && (entry.get_exception() == VM_INVALID_OBJ
3036
                        || exception_obj == entry.get_exception()
3037
                        || (vm_objp(vmg_ exception_obj)
3038
                            ->is_instance_of(vmg_ entry.get_exception()))))
3039
                {
3040
                    /* 
3041
                     *   this is it - move the program counter to the
3042
                     *   first byte of the handler's code 
3043
                     */
3044
                    pc = func_start + entry.get_handler_ofs();
3045
3046
                    /* push the exception so that the handler can get at it */
3047
                    push_obj(vmg_ exception_obj);
3048
3049
                    /* return the new program counter at which to resume */
3050
                    return pc;
3051
                }
3052
            }
3053
        }
3054
3055
        /* 
3056
         *   We didn't find a handler in the current function - unwind the
3057
         *   stack one level, using an ordinary RETURN operation (we're not
3058
         *   really returning, though, so we don't need to provide a return
3059
         *   value).  First, though, check to make sure there is an enclosing
3060
         *   frame at all - if there's not, we can simply return immediately.
3061
         */
3062
        if (frame_ptr_ == 0)
3063
        {
3064
            /* there's no enclosing frame, so there's nowhere to go */
3065
            return 0;
3066
        }
3067
3068
        /* try unwinding the stack a level */
3069
        if ((pc = do_return(vmg0_)) == 0)
3070
        {
3071
            /* 
3072
             *   The enclosing frame is a recursive invocation, so we cannot
3073
             *   unwind any further at this point.  Return null to indicate
3074
             *   that the exception was not handled and should be thrown out
3075
             *   of the current recursive VM invocation.  
3076
             */
3077
            return 0;
3078
        }
3079
    }
3080
}
3081
3082
3083
/* ------------------------------------------------------------------------ */
3084
/*
3085
 *   Call a built-in function 
3086
 */
3087
void CVmRun::call_bif(VMG_ uint set_index, uint func_index, uint argc)
3088
{
3089
    /* 
3090
     *   Call the function -- presume the compiler has ensured that the
3091
     *   function set index is valid for the load image, and that the
3092
     *   function index is valid for the function set; all of this can be
3093
     *   determined at compile time, since function sets are statically
3094
     *   defined. 
3095
     */
3096
    G_bif_table->call_func(vmg_ set_index, func_index, argc);
3097
}
3098
3099
3100
/* ------------------------------------------------------------------------ */
3101
/*
3102
 *   Call a function pointer 
3103
 */
3104
const uchar *CVmRun::call_func_ptr(VMG_ const vm_val_t *funcptr, uint argc,
3105
                                   const char *recurse_name, uint caller_ofs)
3106
{
3107
    vm_val_t prop_val;
3108
3109
    /* 
3110
     *   if it's an object, and the predefined property ObjectCallProp is
3111
     *   defined, and the object defines this property, call this property
3112
     *   in the object 
3113
     */
3114
    if (funcptr->typ == VM_OBJ
3115
        && G_predef->obj_call_prop != VM_INVALID_PROP)
3116
    {
3117
        vm_obj_id_t srcobj;
3118
        int found;
3119
        uint objcall_argc = 0;
3120
        
3121
        /* make sure the object defines ObjectCallProp */
3122
        found = vm_objp(vmg_ funcptr->val.obj)
3123
                ->get_prop(vmg_ G_predef->obj_call_prop, &prop_val,
3124
                           funcptr->val.obj, &srcobj, &objcall_argc);
3125
        
3126
        /* 
3127
         *   if we didn't find it, this object can't be used in this
3128
         *   fashion - throw an error 
3129
         */
3130
        if (!found)
3131
            err_throw(VMERR_FUNCPTR_VAL_REQD);
3132
        
3133
        /* 
3134
         *   if this is a function pointer, call the function pointer with
3135
         *   the function object as 'self' 
3136
         */
3137
        if (prop_val.typ == VM_FUNCPTR)
3138
        {
3139
            /* call the function and return the new program counter */
3140
            return do_call(vmg_ caller_ofs, prop_val.val.ofs, argc,
3141
                           funcptr->val.obj, VM_INVALID_PROP,
3142
                           funcptr->val.obj, srcobj, recurse_name);
3143
        }
3144
        
3145
        /* proceed with the new value */
3146
        funcptr = &prop_val;
3147
    }
3148
    
3149
    /* if it's not a function pointer, it's an error */
3150
    if (funcptr->typ != VM_FUNCPTR)
3151
        err_throw(VMERR_FUNCPTR_VAL_REQD);
3152
    
3153
    /* call the function */
3154
    return do_call(vmg_ caller_ofs, funcptr->val.ofs, argc,
3155
                   VM_INVALID_OBJ, VM_INVALID_PROP,
3156
                   VM_INVALID_OBJ, VM_INVALID_OBJ, recurse_name);
3157
}
3158
3159
/* ------------------------------------------------------------------------ */
3160
/*
3161
 *   Call a function, non-recursively. 
3162
 *   
3163
 *   This is a separate form of do_call(), but simplified for cases where we
3164
 *   know in advance that we won't need to check for recursion and when we
3165
 *   know in advance that we're calling a function and thus have no 'self'
3166
 *   or other method context objects.  These simplifications reduce the
3167
 *   amount of work we have to do, so that ordinary function calls run a
3168
 *   little faster than they would if we used the full do_call() routine.  
3169
 */
3170
const uchar *CVmRun::do_call_func_nr(VMG_ uint caller_ofs,
3171
                                     pool_ofs_t target_ofs, uint argc)
3172
{
3173
    const uchar *target_ofs_ptr;
3174
    CVmFuncPtr hdr_ptr;
3175
    uint i;
3176
    vm_val_t *fp;
3177
    int lcl_cnt;
3178
3179
    /* store nil in R0 */
3180
    r0_.set_nil();
3181
3182
    /* translate the target address */
3183
    target_ofs_ptr = (const uchar *)G_code_pool->get_ptr(target_ofs);
3184
3185
    /* set up a pointer to the new function header */
3186
    hdr_ptr.set(target_ofs_ptr);
3187
3188
    /* get the number of locals from the header */
3189
    lcl_cnt = hdr_ptr.get_local_cnt();
3190
3191
    /* get the target's stack space needs and check for stack overflow */
3192
    if (!G_stk->check_space(hdr_ptr.get_stack_depth() + 8))
3193
        err_throw(VMERR_STACK_OVERFLOW);
3194
3195
    /* allocate the stack frame */
3196
    fp = G_stk->push(8 + lcl_cnt);
3197
3198
    /* there's no target property, target object, defining object, or self */
3199
    (fp++)->set_propid(VM_INVALID_PROP);
3200
    (fp++)->set_nil();
3201
    (fp++)->set_nil();
3202
    (fp++)->set_nil();
3203
3204
    /* push the caller's code offset */
3205
    (fp++)->set_codeofs(caller_ofs);
3206
3207
    /* push the current entrypoint code offset */
3208
    (fp++)->set_codeofs(entry_ptr_);
3209
3210
    /* push the actual parameter count */
3211
    (fp++)->set_int((int32)argc);
3212
3213
    /* push the current frame pointer */
3214
    (fp++)->set_stack(frame_ptr_);
3215
3216
    /* verify the argument count */
3217
    if (!hdr_ptr.argc_ok(argc))
3218
        err_throw(VMERR_WRONG_NUM_OF_ARGS);
3219
3220
    /* set up the new stack frame */
3221
    frame_ptr_ = fp;
3222
3223
    /* load EP with the new code offset */
3224
    entry_ptr_ = target_ofs;
3225
    entry_ptr_native_ = target_ofs_ptr;
3226
3227
    /* push nil for each local */
3228
    for (i = lcl_cnt ; i != 0 ; --i)
3229
        (fp++)->set_nil();
3230
3231
    /* create and activate the new function's profiler frame */
3232
    VM_IF_PROFILER(if (profiling_)
3233
        prof_enter(target_ofs, VM_INVALID_OBJ, VM_INVALID_PROP));
3234
3235
    /* return the new program counter */
3236
    return target_ofs_ptr + get_funchdr_size();
3237
}
3238
3239
/* ------------------------------------------------------------------------ */
3240
/*
3241
 *   Call a function or method
3242
 */
3243
const uchar *CVmRun::do_call(VMG_ uint caller_ofs,
3244
                             pool_ofs_t target_ofs, uint argc,
3245
                             vm_obj_id_t self, vm_prop_id_t target_prop,
3246
                             vm_obj_id_t orig_target_obj,
3247
                             vm_obj_id_t defining_obj,
3248
                             const char *recurse_name)
3249
{
3250
    const uchar *target_ofs_ptr;
3251
    CVmFuncPtr hdr_ptr;
3252
    uint i;
3253
    vm_val_t *fp;
3254
    int lcl_cnt;
3255
3256
    /* store nil in R0 */
3257
    r0_.set_nil();
3258
3259
    /* 
3260
     *   If we have a debugger, and this is a recursive call, set up a
3261
     *   frame for the recursive call, so that the debugger can look up
3262
     *   the stack to the byte-code caller of the native code that's
3263
     *   recursing into the VM.
3264
     *   
3265
     *   This is unnecessary if there's no debugger; the only reason we
3266
     *   need a special frame on native recursion is to allow the debugger
3267
     *   to traverse the stack correctly through the native call.  
3268
     */
3269
    VM_IF_DEBUGGER(if (caller_ofs == 0)
3270
        enter_recursive_frame(vmg_ argc, pc_ptr_));
3271
3272
    /* 
3273
     *   We're done with the old code segment now, so we can safely
3274
     *   translate a new address.  Get the physical address we're calling
3275
     *   -- this will swap in the new code segment if necessary.  
3276
     */
3277
    target_ofs_ptr = (const uchar *)G_code_pool->get_ptr(target_ofs);
3278
3279
    /* set up a pointer to the new function header */
3280
    hdr_ptr.set(target_ofs_ptr);
3281
3282
    /* get the number of locals from the header */
3283
    lcl_cnt = hdr_ptr.get_local_cnt();
3284
3285
    /* 
3286
     *   Get the space needs of the new function, and ensure we have enough
3287
     *   stack space available.  Include the size of the frame that we store
3288
     *   (the original target object, the target property, the defining
3289
     *   object, the 'self' object, the caller's code offset, the caller's
3290
     *   entrypoint offset, the actual parameter count, and the enclosing
3291
     *   frame pointer) in our space needs.  
3292
     */
3293
    if (!G_stk->check_space(hdr_ptr.get_stack_depth() + 8))
3294
    {
3295
        /* 
3296
         *   If we just entered a recursive frame, remove it.  This will
3297
         *   allow us to stop in the debugger in the byte code that triggered
3298
         *   the recursive call. 
3299
         */
3300
        VM_IF_DEBUGGER(if (caller_ofs == 0)
3301
            leave_recursive_frame(vmg0_));
3302
3303
        /* throw the error */
3304
        err_throw(VMERR_STACK_OVERFLOW);
3305
    }
3306
3307
    /* allocate the stack frame */
3308
    fp = G_stk->push(8 + lcl_cnt);
3309
3310
    /* push the target property */
3311
    (fp++)->set_propid(target_prop);
3312
3313
    /* 
3314
     *   if there's no 'self' object, push nil's for the object context;
3315
     *   otherwise, push the object context 
3316
     */
3317
    if (self == VM_INVALID_OBJ)
3318
    {
3319
        /* push nil for target, defining, and self */
3320
        (fp++)->set_nil();
3321
        (fp++)->set_nil();
3322
        (fp++)->set_nil();
3323
    }
3324
    else
3325
    {
3326
        /* push the original target object */
3327
        (fp++)->set_obj(orig_target_obj);
3328
3329
        /* push the defining object */
3330
        (fp++)->set_obj(defining_obj);
3331
3332
        /* push 'self' */
3333
        (fp++)->set_obj(self);
3334
    }
3335
3336
    /* 
3337
     *   Push the caller's code offset.  Note that if the caller's offset is
3338
     *   zero, it indicates that the caller is not the byte-code interpreter
3339
     *   and that this is a recursive invocation; we represent recursive
3340
     *   frames using a zero caller offset, to we can just use the zero
3341
     *   value as given in this case. 
3342
     */
3343
    (fp++)->set_codeofs(caller_ofs);
3344
3345
    /* push the current entrypoint code offset */
3346
    (fp++)->set_codeofs(entry_ptr_);
3347
3348
    /* push the actual parameter count */
3349
    (fp++)->set_int((int32)argc);
3350
3351
    /* push the current frame pointer */
3352
    (fp++)->set_stack(frame_ptr_);
3353
3354
    /* 
3355
     *   check the argument count - do this before establishing the new
3356
     *   frame and entry pointers, so that if we report a stack traceback in
3357
     *   the debugger, we'll report the error in the calling frame, which is
3358
     *   where it really belongs 
3359
     */
3360
    if (!hdr_ptr.argc_ok(argc))
3361
    {
3362
        /* leave the recursive frame, if we entered one */
3363
        VM_IF_DEBUGGER(if (caller_ofs == 0)
3364
            leave_recursive_frame(vmg0_));
3365
3366
        /* 
3367
         *   if we're making a recursive call, throw an error indicating
3368
         *   what kind of recursive call we're making 
3369
         */
3370
        if (recurse_name != 0)
3371
        {
3372
            /* throw the named generic argument mismatch error */
3373
            err_throw_a(VMERR_WRONG_NUM_OF_ARGS_CALLING, 1,
3374
                        ERR_TYPE_CHAR, recurse_name);
3375
        }
3376
        else
3377
        {
3378
            /* throw the generic argument mismatch error */
3379
            err_throw(VMERR_WRONG_NUM_OF_ARGS);
3380
        }
3381
    }
3382
3383
    /* 
3384
     *   set up the new frame so that the frame pointer points to the old
3385
     *   frame pointer stored in the stack 
3386
     */
3387
    frame_ptr_ = fp;
3388
3389
    /* load EP with the new code offset */
3390
    entry_ptr_ = target_ofs;
3391
    entry_ptr_native_ = target_ofs_ptr;
3392
3393
    /* push nil for each local */
3394
    for (i = lcl_cnt ; i != 0 ; --i)
3395
        (fp++)->set_nil();
3396
3397
    /* create and activate the new function's profiler frame */
3398
    VM_IF_PROFILER(if (profiling_)
3399
        prof_enter(target_ofs, defining_obj, target_prop));
3400
3401
    /* if desired, make a recursive call into the byte code interpreter */
3402
    if (caller_ofs != 0)
3403
    {
3404
        /* 
3405
         *   return the new program counter at the first byte of code in the
3406
         *   new function, which immediately follows the header 
3407
         */
3408
        return target_ofs_ptr + get_funchdr_size();
3409
    }
3410
    else
3411
    {
3412
        VM_IF_DEBUGGER(err_try {)
3413
3414
        /* recursively call the interpreter loop */
3415
        run(vmg_ target_ofs_ptr + get_funchdr_size());
3416
3417
        /* 
3418
         *   if the debugger is present, always remove our recursive frame on
3419
         *   the way out 
3420
         */
3421
        VM_IF_DEBUGGER(
3422
        }
3423
        err_finally
3424
        {
3425
            leave_recursive_frame(vmg0_);
3426
        }
3427
        err_end;)
3428
3429
        /* 
3430
         *   this was a recursive call, so there's no program counter to
3431
         *   return - just return null 
3432
         */
3433
        return 0;
3434
    }
3435
}
3436
3437
/*
3438
 *   Determine if we're in a recursive VM invocation.  If this frame or
3439
 *   any enclosing frame other than the outermost has a code offset of
3440
 *   zero in the return address slot, we are in a recursive VM invocation.
3441
 */
3442
int CVmRun::is_recursive_invocation(VMG0_) const
3443
{
3444
    vm_val_t *p;
3445
    
3446
    /* start with the current frame */
3447
    p = frame_ptr_;
3448
3449
    /* if there's no frame pointer, it's obviously not recursive */
3450
    if (p == 0)
3451
        return FALSE;
3452
3453
    /* scan frames until we get to the outermost frame */
3454
    for (;;)
3455
    {
3456
        /* 
3457
         *   If this is the outermost frame, we can stop now.  The
3458
         *   outermost frame has an enclosing frame pointer value of null.
3459
         *   (A given frame pointer always points directly to the
3460
         *   enclosing frame pointer stored in the stack frame, so the
3461
         *   offset from this frame pointer is zero.)  
3462
         */
3463
        if (get_enclosing_frame_ptr(vmg_ p) == 0)
3464
            break;
3465
3466
        /* 
3467
         *   Check the return address in this frame - if it's at offset
3468
         *   zero, it means that this method was called directly as a
3469
         *   recursive VM invocation.
3470
         */
3471
        if (get_return_addr_from_frame(vmg_ p) == 0)
3472
            return TRUE;
3473
3474
        /* move to the enclosing frame */
3475
        p = get_enclosing_frame_ptr(vmg_ p);
3476
    }
3477
3478
    /* 
3479
     *   we didn't find any direct invocations after the outermost frame,
3480
     *   so this is the top-level VM invocation 
3481
     */
3482
    return FALSE;
3483
}
3484
3485
3486
/*
3487
 *   Return from the current function.  Returns true if execution can
3488
 *   proceed, false if this returns us out of the outermost function, in
3489
 *   which case the execution loop must terminate and return control to
3490
 *   the host environment.  
3491
 */
3492
const uchar *CVmRun::do_return(VMG0_)
3493
{
3494
    int argc;
3495
    pool_ofs_t caller_ofs;
3496
3497
    /*
3498
     *   The frame pointer always points to the location on the stack
3499
     *   where we pushed the enclosing frame pointer.  Reset the stack
3500
     *   pointer to the current frame pointer, then pop the enclosing
3501
     *   frame pointer.  
3502
     */
3503
    G_stk->set_sp(frame_ptr_);
3504
    frame_ptr_ = (vm_val_t *)G_stk->get(0)->val.ptr;
3505
3506
    /* restore the enclosing argument count */
3507
    argc = G_stk->get(1)->val.intval;
3508
3509
    /* restore the enclosing entry pointer */
3510
    entry_ptr_ = G_stk->get(2)->val.ofs;
3511
3512
    /* translate the method entry pointer to a physical address */
3513
    entry_ptr_native_ = (const uchar *)G_code_pool->get_ptr(entry_ptr_);
3514
3515
    /* restore the enclosing code offset */
3516
    caller_ofs = G_stk->get(3)->val.ofs;
3517
3518
    /* 
3519
     *   Discard the actual parameters, plus the 'self', defining object,
3520
     *   original target object, and target property values.  While we're at
3521
     *   it, also discard the enclosing frame pointer, enclosing argument
3522
     *   count, enclosing entry pointer, and enclosing code offset, which
3523
     *   we've already restored.  
3524
     */
3525
    G_stk->discard(argc + 8);
3526
3527
    /* leave the profiler stack level */
3528
    VM_IF_PROFILER(if (profiling_)
3529
        prof_leave());
3530
3531
    /* 
3532
     *   If the enclosing code offset is invalid, we've returned from the
3533
     *   outermost function invoked by the host environment.  0 is an
3534
     *   invalid offset, since offset 0 in a method never contains valid
3535
     *   code.  
3536
     */
3537
    if (caller_ofs == 0)
3538
        return 0;
3539
3540
    /* 
3541
     *   return the new program counter - calculate the PC offset by adding
3542
     *   the offset within the method to the entry pointer 
3543
     */
3544
    return entry_ptr_native_ + caller_ofs;
3545
}
3546
3547
3548
/* ------------------------------------------------------------------------ */
3549
/*
3550
 *   Recursive frame routines.
3551
 */
3552
3553
/*
3554
 *   Enter a recursive call frame from a native routine 
3555
 */
3556
void CVmRun::enter_recursive_frame(VMG_ int argc,
3557
                                   const uchar **pc_ptr)
3558
{
3559
    pool_ofs_t old_ofs;
3560
    int i;
3561
3562
    /* 
3563
     *   don't bother setting up a recursive frame for a recursive call
3564
     *   from the debugger itself - the only purpose of these frames is to
3565
     *   aid the debugger in tracing the stack, which it obviously won't
3566
     *   need to do when it's the native caller 
3567
     */
3568
    VM_IF_DEBUGGER(if (G_debugger->is_in_debugger())
3569
        return);
3570
3571
    /* 
3572
     *   if there's no global PC register, we're being called from the
3573
     *   outermost native caller, so there's no need for a native frame 
3574
     */
3575
    if (pc_ptr == 0)
3576
        return;
3577
    
3578
    /* get the return address from the global PC register */
3579
    old_ofs = pc_to_method_ofs(*pc_ptr);
3580
3581
    /* make sure we have space for the native frame */
3582
    if (!G_stk->check_space(6))
3583
        err_throw(VMERR_STACK_OVERFLOW);
3584
3585
    /* there's no target property for a recursive caller */
3586
    push_nil(vmg0_);
3587
3588
    /* there's no original target object */
3589
    push_nil(vmg0_);
3590
3591
    /* there's no defining object */
3592
    push_nil(vmg0_);
3593
3594
    /* there's no 'self' for a recursive caller */
3595
    push_nil(vmg0_);
3596
3597
    /* push the caller's code offset */
3598
    push_codeofs(vmg_ old_ofs);
3599
3600
    /* push the old entrypoint code offset */
3601
    push_codeofs(vmg_ entry_ptr_);
3602
3603
    /* 
3604
     *   push the argument count to the routine being invoked from the
3605
     *   native code - this isn't actually the argument count to the
3606
     *   native routine, which we don't know, but we must push it anyway
3607
     *   because the arguments are arranged as though they're to this fake
3608
     *   native frame 
3609
     */
3610
    push_int(vmg_ (int32)argc);
3611
3612
    /* push the current frame pointer */
3613
    push_stackptr(vmg_ frame_ptr_);
3614
3615
    /* set up the new frame pointer */
3616
    frame_ptr_ = G_stk->get_sp();
3617
3618
    /* there's no entrypoint address for the native code */
3619
    entry_ptr_ = 0;
3620
    entry_ptr_native_ = 0;
3621
3622
    /* 
3623
     *   call the debugger to do a step trace - the debugger obviously
3624
     *   can't really stop here, but what it can do is note that we've
3625
     *   stepped through this native stack level for the purposes of
3626
     *   determining when it should stop next for step-in, step-over, and
3627
     *   step-out modes 
3628
     */
3629
    VM_IF_DEBUGGER(if (G_debugger->is_single_step())
3630
        G_debugger->step(vmg_ 0, 0, FALSE, 0));
3631
3632
    /* 
3633
     *   Copy the arguments from this frame - this is necessary so that
3634
     *   the recursive frame we'll set up next (after we return) receives
3635
     *   a copy of its arguments, which we hijacked by establishing this
3636
     *   intermediate frame to represent the native caller.  Note that we
3637
     *   must follow the normal convention of pushing arguments in reverse
3638
     *   order.  
3639
     */
3640
    for (i = argc ; i > 0 ; --i)
3641
        G_stk->push(get_param(vmg_ i - 1));
3642
}
3643
3644
#ifdef VM_DEBUGGER
3645
3646
/*
3647
 *   Leave a recursive call frame on our way back out to a native routine 
3648
 */
3649
void CVmRun::leave_recursive_frame(VMG0_)
3650
{
3651
    vm_val_t val;
3652
    int argc;
3653
    
3654
    /* 
3655
     *   if we're in the debugger, we will not have set up a recursive
3656
     *   call frame, so we will not need to remove one 
3657
     */
3658
    if (G_debugger->is_in_debugger())
3659
        return;
3660
3661
    /* 
3662
     *   if there's no global PC pointer, it means that we're at the
3663
     *   outermost native frame, which we suppress 
3664
     */
3665
    if (pc_ptr_ == 0)
3666
        return;
3667
3668
    /* re-activate the enclosing frame */
3669
    G_stk->set_sp(frame_ptr_);
3670
    G_stk->pop(&val);
3671
    frame_ptr_ = (vm_val_t *)val.val.ptr;
3672
3673
    /* pop the argument count */
3674
    G_stk->pop(&val);
3675
    argc = val.val.intval;
3676
3677
    /* pop the enclosing entry pointer */
3678
    G_stk->pop(&val);
3679
    entry_ptr_ = val.val.ofs;
3680
    entry_ptr_native_ = (const uchar *)G_code_pool->get_ptr(entry_ptr_);
3681
3682
    /* 
3683
     *   discard the enclosing code offset - since we know this is
3684
     *   actually a native caller, we pushed the enclosing code offset
3685
     *   only to enable the debugger to find the native caller 
3686
     */
3687
    G_stk->discard();
3688
3689
    /* 
3690
     *   discard the actual parameters, plus the target property, original
3691
     *   target object, defining object, and the 'self' object 
3692
     */
3693
    G_stk->discard(argc + 4);
3694
}
3695
3696
/*
3697
 *   save the execution context 
3698
 */
3699
void CVmRun::save_context(VMG_ vmrun_save_ctx *ctx)
3700
{
3701
    /* save our registers */
3702
    ctx->entry_ptr_ = entry_ptr_;
3703
    ctx->frame_ptr_ = frame_ptr_;
3704
    ctx->pc_ptr_ = pc_ptr_;
3705
3706
    /* save the stack depth */
3707
    ctx->old_stack_depth_ = G_stk->get_depth();
3708
}
3709
3710
/*
3711
 *   restore the execution context 
3712
 */
3713
void CVmRun::restore_context(VMG_ vmrun_save_ctx *ctx)
3714
{
3715
    /* restore our registers */
3716
    entry_ptr_ = ctx->entry_ptr_;
3717
    entry_ptr_native_ = (const uchar *)G_code_pool->get_ptr(entry_ptr_);
3718
    frame_ptr_ = ctx->frame_ptr_;
3719
    pc_ptr_ = ctx->pc_ptr_;
3720
3721
    /* if there's anything extra left on the stack, discard it */
3722
    if (G_stk->get_depth() > ctx->old_stack_depth_)
3723
        G_stk->discard(G_stk->get_depth() - ctx->old_stack_depth_);
3724
}
3725
3726
#endif /* VM_DEBUGGER */
3727
3728
/* ------------------------------------------------------------------------ */
3729
/*
3730
 *   Append a stack trace to a string.  This is only meaningful in a
3731
 *   debugger-equipped version. 
3732
 */
3733
#if VM_DEBUGGER
3734
3735
/*
3736
 *   callback context for stack trace appender 
3737
 */
3738
struct append_stack_ctx
3739
{
3740
    /* the string so far */
3741
    vm_obj_id_t str_obj;
3742
3743
    /* globals */
3744
    vm_globals *vmg;
3745
3746
    /* frame pointer where we pushed our string for gc protection */
3747
    vm_val_t *gc_fp;
3748
};
3749
3750
/*
3751
 *   stack trace callback 
3752
 */
3753
static void append_stack_cb(void *ctx0, const char *str, int strl)
3754
{
3755
    append_stack_ctx *ctx = (append_stack_ctx *)ctx0;
3756
    size_t new_len;
3757
    size_t old_len;
3758
    const char *old_str;
3759
    char *new_str;
3760
3761
    /* set up access to globals */
3762
    VMGLOB_PTR(ctx->vmg);
3763
3764
    /* get the original string text */
3765
    old_str = vm_objp(vmg_ ctx->str_obj)->get_as_string(vmg0_);
3766
    old_len = vmb_get_len(old_str);
3767
    old_str += VMB_LEN;
3768
3769
    /* 
3770
     *   allocate a new string, big enough for the old string plus the new
3771
     *   text, plus a newline 
3772
     */
3773
    new_len = old_len + strl + 1;
3774
    ctx->str_obj = CVmObjString::create(vmg_ FALSE, new_len);
3775
3776
    /* get the new string buffer */
3777
    new_str = ((CVmObjString *)vm_objp(vmg_ ctx->str_obj))->cons_get_buf();
3778
3779
    /* build the new string */
3780
    memcpy(new_str, old_str, old_len);
3781
    new_str[old_len] = '\n';
3782
    memcpy(new_str + old_len + 1, str, strl);
3783
3784
    /* 
3785
     *   replace our gc-protective stack reference to the old string with
3786
     *   the new string - we're done with the old string now, so it's okay
3787
     *   if it gets collected, but we obviously want to keep the new one
3788
     *   around 
3789
     */
3790
    G_stk->get_from_frame(ctx->gc_fp, 0)->set_obj(ctx->str_obj);
3791
}
3792
3793
/*
3794
 *   append a stack trace to the given string 
3795
 */
3796
vm_obj_id_t CVmRun::append_stack_trace(VMG_ vm_obj_id_t str_obj)
3797
{
3798
    append_stack_ctx ctx;
3799
3800
    /* push the string for protection from gc */
3801
    push_obj(vmg_ str_obj);
3802
    
3803
    /* call the debugger to set up the stack traceback */
3804
    ctx.str_obj = str_obj;
3805
    ctx.vmg = VMGLOB_ADDR;
3806
    ctx.gc_fp = G_stk->get_sp();
3807
    G_debugger->build_stack_listing(vmg_ &append_stack_cb, &ctx, TRUE);
3808
3809
    /* discard the gc protection */
3810
    G_stk->discard();
3811
3812
    /* return the result string */
3813
    return ctx.str_obj;
3814
}
3815
3816
#endif /* VM_DEBUGGER */
3817
3818
/* ------------------------------------------------------------------------ */
3819
/*
3820
 *   Set a property of an object 
3821
 */
3822
void CVmRun::set_prop(VMG_ vm_obj_id_t obj, vm_prop_id_t prop,
3823
                      const vm_val_t *new_val)
3824
{
3825
    /* set the property */
3826
    vm_objp(vmg_ obj)->set_prop(vmg_ G_undo, obj, prop, new_val);
3827
}
3828
3829
/* ------------------------------------------------------------------------ */
3830
/*
3831
 *   Evaluate a property of an object 
3832
 */
3833
const uchar *CVmRun::get_prop(VMG_ uint caller_ofs,
3834
                              const vm_val_t *target_obj,
3835
                              vm_prop_id_t target_prop,
3836
                              const vm_val_t *self, uint argc)
3837
{
3838
    vm_val_t val;
3839
    vm_obj_id_t srcobj;
3840
    int found;
3841
    vm_val_t new_self;
3842
3843
    /* find the property without evaluating it */
3844
    found = get_prop_no_eval(vmg_ &target_obj, target_prop,
3845
                             &argc, &srcobj, &val, &self, &new_self);
3846
3847
    /* if we didn't find it, try propNotDefined */
3848
    if (!found && G_predef->prop_not_defined_prop != VM_INVALID_PROP)
3849
    {
3850
        /* 
3851
         *   We didn't find it, so call propNotDefined on the object, with
3852
         *   the property originally called as an additional first argument.
3853
         *   If propNotDefined is not exported by the program, we'll fall
3854
         *   back on the default of evaluating to nil.  
3855
         */
3856
        found = get_prop_no_eval(vmg_ &target_obj,
3857
                                 G_predef->prop_not_defined_prop,
3858
                                 &argc, &srcobj, &val, &self, &new_self);
3859
3860
        /* 
3861
         *   if we found it, and it's code, push the original property ID as
3862
         *   the new first argument 
3863
         */
3864
        if (found && val.typ == VM_CODEOFS)
3865
        {
3866
            /* 
3867
             *   add the property argument (we push backwards, so this will
3868
             *   conveniently become the new first argument, since we're
3869
             *   pushing it last) 
3870
             */
3871
            push_prop(vmg_ target_prop);
3872
            
3873
            /* count the additional argument */
3874
            ++argc;
3875
3876
            /* the target property changes to propNotDefined */
3877
            target_prop = G_predef->prop_not_defined_prop;
3878
        }
3879
    }
3880
    
3881
    /* evaluate whatever we found or didn't find */
3882
    return eval_prop_val(vmg_ found, caller_ofs, &val, self->val.obj,
3883
                         target_prop, target_obj, srcobj, argc);
3884
}
3885
3886
/*
3887
 *   Look up a property without evaluating it. 
3888
 */
3889
inline int CVmRun::get_prop_no_eval(VMG_ const vm_val_t **target_obj,
3890
                                    vm_prop_id_t target_prop,
3891
                                    uint *argc, vm_obj_id_t *srcobj,
3892
                                    vm_val_t *val,
3893
                                    const vm_val_t **self,
3894
                                    vm_val_t *new_self)
3895
{
3896
    int found;
3897
    const char *target_ptr;
3898
    
3899
    /* 
3900
     *   we can evaluate properties of regular objects, as well as string
3901
     *   and list constants - see what we have 
3902
     */
3903
    switch((*target_obj)->typ)
3904
    {
3905
    case VM_LIST:
3906
        /* 'self' must be the same as the target for a constant list */
3907
        if ((*self)->typ != (*target_obj)->typ
3908
            || (*self)->val.ofs != (*target_obj)->val.ofs)
3909
            err_throw(VMERR_OBJ_VAL_REQD);
3910
3911
        /* translate the list offset to a physical pointer */
3912
        target_ptr = G_const_pool->get_ptr((*target_obj)->val.ofs);
3913
3914
        /* evaluate the constant list property */
3915
        found = CVmObjList::const_get_prop(vmg_ val, *target_obj,
3916
                                           target_ptr, target_prop,
3917
                                           srcobj, argc);
3918
3919
        /* 
3920
         *   If the result is a method to run, we need an actual object for
3921
         *   'self'.  In this case, create a dynamic list object with the
3922
         *   same contents as the constant list value.  
3923
         */
3924
        if (found && val->typ == VM_CODEOFS)
3925
        {
3926
            /* create the list */
3927
            new_self->set_obj(CVmObjListConst::create(vmg_ target_ptr));
3928
3929
            /* use it as the new 'self' and the new effective target */
3930
            *self = new_self;
3931
            *target_obj = new_self;
3932
        }
3933
3934
        /* go evaluate the result as normal */
3935
        break;
3936
3937
    case VM_SSTRING:
3938
        /* 'self' must be the same as the target for a constant string */
3939
        if ((*self)->typ != (*target_obj)->typ
3940
            || (*self)->val.ofs != (*target_obj)->val.ofs)
3941
            err_throw(VMERR_OBJ_VAL_REQD);
3942
3943
        /* translate the string offset to a physical pointer */
3944
        target_ptr = G_const_pool->get_ptr((*target_obj)->val.ofs);
3945
3946
        /* evaluate the constant string property */
3947
        found = CVmObjString::const_get_prop(vmg_ val, *target_obj,
3948
                                             target_ptr, target_prop,
3949
                                             srcobj, argc);
3950
3951
        /* 
3952
         *   If the result is a method to run, we need an actual object for
3953
         *   'self'.  In this case, create a dynamic string object with the
3954
         *   same contents as the constant string value.  
3955
         */
3956
        if (found && val->typ == VM_CODEOFS)
3957
        {
3958
            /* create the string */
3959
            new_self->set_obj(CVmObjStringConst::create(vmg_ target_ptr));
3960
3961
            /* it's the new 'self' and the new effective target object */
3962
            *self = new_self;
3963
            *target_obj = new_self;
3964
        }
3965
3966
        /* go evaluate the result as normal */
3967
        break;
3968
3969
    case VM_OBJ:
3970
        /* get the property value from the target object */
3971
        found = vm_objp(vmg_ (*target_obj)->val.obj)
3972
                ->get_prop(vmg_ target_prop, val, (*target_obj)->val.obj,
3973
                           srcobj, argc);
3974
3975
        /* 'self' must be an object as well */
3976
        if ((*self)->typ != VM_OBJ)
3977
            err_throw(VMERR_OBJ_VAL_REQD);
3978
        break;
3979
3980
    case VM_NIL:
3981
        /* nil pointer dereferenced */
3982
        err_throw(VMERR_NIL_DEREF);
3983
3984
    default:
3985
        /* we can't evaluate properties of anything else */
3986
        err_throw(VMERR_OBJ_VAL_REQD);
3987
    }
3988
3989
    /* return the 'found' indication */
3990
    return found;
3991
}
3992
3993
/* ------------------------------------------------------------------------ */
3994
/*
3995
 *   Given a value that has been retrieved from an object property,
3996
 *   evaluate the value.  If the value contains code, we'll execute the
3997
 *   code; if it contains a self-printing string, we'll display the
3998
 *   string; otherwise, we'll just store the value in R0.
3999
 *   
4000
 *   'found' indicates whether or not the property value is defined.
4001
 *   False indicates that the property value is not defined by the object;
4002
 *   true indicates that it is.  
4003
 */
4004
inline const uchar *CVmRun::eval_prop_val(VMG_ int found, uint caller_ofs,
4005
                                          const vm_val_t *val,
4006
                                          vm_obj_id_t self,
4007
                                          vm_prop_id_t target_prop,
4008
                                          const vm_val_t *orig_target_obj,
4009
                                          vm_obj_id_t defining_obj,
4010
                                          uint argc)
4011
{
4012
    /* check whether or not the property is defined */
4013
    if (found)
4014
    {
4015
        /* take appropriate action based on the datatype of the result */
4016
        switch(val->typ)
4017
        {
4018
        case VM_CODEOFS:
4019
            /* 
4020
             *   It's a method - invoke the method.  This will set us up
4021
             *   to start executing this new code, so there's nothing more
4022
             *   we need to do here.  
4023
             */
4024
            return do_call(vmg_ caller_ofs, val->val.ofs, argc,
4025
                           self, target_prop, orig_target_obj->val.obj,
4026
                           defining_obj, 0);
4027
            
4028
        case VM_DSTRING:
4029
            /* no arguments are allowed */
4030
            if (argc != 0)
4031
                err_throw(VMERR_WRONG_NUM_OF_ARGS);
4032
            
4033
            /* 
4034
             *   it's a self-printing string - invoke the default string
4035
             *   output function (this is effectively a do_call()) 
4036
             */
4037
            return disp_dstring(vmg_ val->val.ofs, caller_ofs, self);
4038
            
4039
        default:
4040
            /* for any other value, no arguments are allowed */
4041
            if (argc != 0)
4042
                err_throw(VMERR_WRONG_NUM_OF_ARGS);
4043
            
4044
            /* store the result in R0 */
4045
            r0_ = *val;
4046
4047
            /* resume execution where we left off */
4048
            return entry_ptr_native_ + caller_ofs;
4049
        }
4050
    }
4051
    else
4052
    {
4053
        /* 
4054
         *   the property or method is not defined - discard arguments and
4055
         *   set R0 to nil
4056
         */
4057
        G_stk->discard(argc);
4058
        r0_.set_nil();
4059
4060
        /* resume execution where we left off */
4061
        return entry_ptr_native_ + caller_ofs;
4062
    }
4063
}
4064
4065
/* ------------------------------------------------------------------------ */
4066
/*
4067
 *   Inherit a property or method from the appropriate superclass of the
4068
 *   object that defines currently executing code.  
4069
 */
4070
const uchar *CVmRun::inh_prop(VMG_ uint caller_ofs,
4071
                              vm_prop_id_t prop, uint argc)
4072
{
4073
    vm_val_t orig_target_obj;
4074
    vm_obj_id_t defining_obj;
4075
    vm_val_t val;
4076
    vm_obj_id_t srcobj;
4077
    int found;
4078
    vm_obj_id_t self;
4079
4080
    /* get the defining object from the stack frame */
4081
    defining_obj = get_defining_obj(vmg0_);
4082
4083
    /* get the original target object from the stack frame */
4084
    orig_target_obj.set_obj(get_orig_target_obj(vmg0_));
4085
4086
    /* get the 'self' object */
4087
    self = get_self(vmg0_);
4088
4089
    /* get the inherited property value */
4090
    found = vm_objp(vmg_ self)->inh_prop(vmg_ prop, &val, self,
4091
                                         orig_target_obj.val.obj,
4092
                                         defining_obj, &srcobj, &argc);
4093
4094
    /* if we didn't find it, try inheriting propNotDefined */
4095
    if (!found && G_predef->prop_not_defined_prop != VM_INVALID_PROP)
4096
    {
4097
        /* 
4098
         *   Look up propNotDefined using the same search conditions we used
4099
         *   to find the original inherited property.  This lets us look up
4100
         *   the "inherited" propNotDefined.  
4101
         */
4102
        found = vm_objp(vmg_ self)->inh_prop(vmg_
4103
                                             G_predef->prop_not_defined_prop,
4104
                                             &val, self,
4105
                                             orig_target_obj.val.obj,
4106
                                             defining_obj, &srcobj, &argc);
4107
4108
        /* 
4109
         *   if we found it, and it's code, push the original property ID we
4110
         *   were attempting to inherit - this becomes the new first
4111
         *   parameter to the propNotDefined method 
4112
         */
4113
        if (found && val.typ == VM_CODEOFS)
4114
        {
4115
            /* add the original property pointer argument */
4116
            push_prop(vmg_ prop);
4117
4118
            /* count the additional argument */
4119
            ++argc;
4120
4121
            /* the target property changes to propNotDefined */
4122
            prop = G_predef->prop_not_defined_prop;
4123
        }
4124
    }
4125
4126
    /* 
4127
     *   evaluate and store the result - note that "self" remains the
4128
     *   current "self" object, since we're inheriting within the context
4129
     *   of the original method call 
4130
     */
4131
    return eval_prop_val(vmg_ found, caller_ofs, &val, self, prop,
4132
                         &orig_target_obj, srcobj, argc);
4133
}
4134
4135
4136
/* ------------------------------------------------------------------------ */
4137
/*
4138
 *   Display a dstring via the default string display mechanism 
4139
 */
4140
const uchar *CVmRun::disp_dstring(VMG_ pool_ofs_t ofs, uint caller_ofs,
4141
                                  vm_obj_id_t self)
4142
{
4143
    /* push the string */
4144
    G_stk->push()->set_sstring(ofs);
4145
4146
    /* invoke the default "say" function */
4147
    return disp_string_val(vmg_ caller_ofs, self);
4148
}
4149
4150
/*
4151
 *   Display the value at top of stack via the default string display
4152
 *   mechanism 
4153
 */
4154
const uchar *CVmRun::disp_string_val(VMG_ uint caller_ofs, vm_obj_id_t self)
4155
{
4156
    /* 
4157
     *   if there's a valid 'self' object, and there's a default display
4158
     *   method defined, and 'self' defines or inherits that method,
4159
     *   invoke the method 
4160
     */
4161
    if (say_method_ != VM_INVALID_PROP && self != VM_INVALID_OBJ)
4162
    {
4163
        vm_obj_id_t src_obj;
4164
        vm_val_t val;
4165
4166
        /* 
4167
         *   look up the property - if we find it, and it's a regular
4168
         *   method, invoke it 
4169
         */
4170
        if (vm_objp(vmg_ self)->get_prop(vmg_ say_method_, &val, self,
4171
                                         &src_obj, 0)
4172
            && val.typ == VM_CODEOFS)
4173
        {
4174
            vm_val_t self_val;
4175
4176
            /* set up a 'self' value - this is the target object */
4177
            self_val.set_obj(self);
4178
4179
            /* there's a default display method - invoke it */
4180
            return eval_prop_val(vmg_ TRUE, caller_ofs, &val, self,
4181
                                 say_method_, &self_val, src_obj, 1);
4182
        }
4183
    }
4184
    
4185
    /* if the "say" function isn't initialized, it's an error */
4186
    if (say_func_ == 0 || say_func_->val.typ == VM_NIL)
4187
        err_throw(VMERR_SAY_IS_NOT_DEFINED);
4188
4189
    /* call the "say" function with the argument at top of stack */
4190
    return call_func_ptr(vmg_ &say_func_->val, 1, 0, caller_ofs);
4191
}
4192
4193
/*
4194
 *   Set the "say" function.
4195
 */
4196
void CVmRun::set_say_func(VMG_ const vm_val_t *val)
4197
{
4198
    /* 
4199
     *   if we haven't yet allocated a global to hold the 'say' function,
4200
     *   allocate one now 
4201
     */
4202
    if (say_func_ == 0)
4203
        say_func_ = G_obj_table->create_global_var();
4204
4205
    /* remember the new function */
4206
    say_func_->val = *val;
4207
}
4208
4209
/*
4210
 *   Get the current "say" function 
4211
 */
4212
void CVmRun::get_say_func(vm_val_t *val) const
4213
{
4214
    /* 
4215
     *   if we ever allocated a global to hold the 'say' function, return its
4216
     *   value; otherwise, there's no 'say' function, so the result is nil 
4217
     */
4218
    if (say_func_ != 0)
4219
        *val = say_func_->val;
4220
    else
4221
        val->set_nil();
4222
}
4223
4224
/* ------------------------------------------------------------------------ */
4225
/*
4226
 *   Check a property for speculative evaluation 
4227
 */
4228
void CVmRun::check_prop_spec_eval(VMG_ vm_obj_id_t obj, vm_prop_id_t prop)
4229
{
4230
    vm_val_t val;
4231
    vm_obj_id_t srcobj;
4232
4233
    /* get the property value */
4234
    if (vm_objp(vmg_ obj)->get_prop(vmg_ prop, &val, obj, &srcobj, 0))
4235
    {
4236
        /* check the type of the value */
4237
        switch(val.typ)
4238
        {
4239
        case VM_CODEOFS:
4240
        case VM_DSTRING:
4241
        case VM_NATIVE_CODE:
4242
            /* 
4243
             *   evaulating these types could result in side effects, so
4244
             *   this property cannot be evaulated during a speculative
4245
             *   evaluation 
4246
             */
4247
            err_throw(VMERR_BAD_SPEC_EVAL);
4248
            break;
4249
4250
        default:
4251
            /* evaluating other types causes no side effects, so proceed */
4252
            break;
4253
        }
4254
    }
4255
}
4256
4257
/* ------------------------------------------------------------------------ */
4258
/*
4259
 *   Set up a function header pointer for the current function 
4260
 */
4261
void CVmRun::set_current_func_ptr(VMG_ CVmFuncPtr *func_ptr)
4262
{
4263
    /* set up the pointer based on the current Entry Pointer register */
4264
    func_ptr->set(entry_ptr_native_);
4265
}
4266
4267
/*
4268
 *   Set up a function header pointer for the return address of the given
4269
 *   stack frame 
4270
 */
4271
void CVmRun::set_return_funcptr_from_frame(VMG_ CVmFuncPtr *func_ptr,
4272
                                           vm_val_t *frame_ptr)
4273
{
4274
    pool_ofs_t ep;
4275
    
4276
    /* get the enclosing entry pointer for the frame */
4277
    ep = get_enclosing_entry_ptr_from_frame(vmg_ frame_ptr);
4278
4279
    /* set up the function pointer for the entry pointer */
4280
    func_ptr->set((const uchar *)G_code_pool->get_ptr(ep));
4281
}
4282
4283
/* ------------------------------------------------------------------------ */
4284
/*
4285
 *   Get the frame pointer at a given stack level 
4286
 */
4287
vm_val_t *CVmRun::get_fp_at_level(VMG_ int level) const
4288
{
4289
    vm_val_t *fp;
4290
    
4291
    /* walk up the stack to the desired level */
4292
    for (fp = frame_ptr_ ; fp != 0 && level != 0 ;
4293
         --level, fp = get_enclosing_frame_ptr(vmg_ fp));
4294
4295
    /* 
4296
     *   if we ran out of frames before we reached the desired level,
4297
     *   throw an error 
4298
     */
4299
    if (fp == 0)
4300
        err_throw(VMERR_BAD_FRAME);
4301
4302
    /* return the frame */
4303
    return fp;
4304
}
4305
4306
/* ------------------------------------------------------------------------ */
4307
/*
4308
 *   Get the message from an exception object 
4309
 */
4310
void CVmRun::get_exc_message(VMG_ const CVmException *exc,
4311
                             char *buf, size_t buflen, int add_unh_prefix)
4312
{
4313
    CVmException tmpexc;
4314
    const char *tmpmsg;
4315
    const char *msg;
4316
4317
    /* set up our temporary exception object with no parameters by default */
4318
    tmpexc.param_count_ = 0;
4319
    
4320
    /* check for unhandled program exceptions */
4321
    if (exc->get_error_code() == VMERR_UNHANDLED_EXC)
4322
    {
4323
        size_t msg_len;
4324
        
4325
        /* 
4326
         *   This is not a VM error, but is simply an exception that the
4327
         *   program itself threw but did not handle.  We might be able to
4328
         *   find an informational message in the exception object itself.
4329
         */
4330
4331
        /* get the exception's message, if available */
4332
        msg = get_exc_message(vmg_ exc, &msg_len);
4333
        if (msg != 0)
4334
        {
4335
            /* 
4336
             *   we got a message from the exception object - use it 
4337
             */
4338
4339
            /* set up our parameters for the formatting */
4340
            tmpexc.param_count_ = 1;
4341
            tmpexc.set_param_str(0, msg, msg_len);
4342
4343
            /*
4344
             *   If they want an "unhandled exception" prefix, get the
4345
             *   message for the prefix; otherwise, just use the message
4346
             *   from the exception without further adornment. 
4347
             */
4348
            if (add_unh_prefix)
4349
            {
4350
                /* they want a prefix - get the prefix message */
4351
                tmpmsg = err_get_msg(vm_messages, vm_message_count,
4352
                                     VMERR_UNHANDLED_EXC_PARAM, FALSE);
4353
            }
4354
            else
4355
            {
4356
                /* no prefix desired - just use the message as we got it */
4357
                tmpmsg = "%s";
4358
            }
4359
4360
            /* format the message */
4361
            err_format_msg(buf, buflen, tmpmsg, &tmpexc);
4362
        }
4363
        else
4364
        {
4365
            /* no message - use a generic exception message */
4366
            tmpmsg = err_get_msg(vm_messages, vm_message_count,
4367
                                 VMERR_UNHANDLED_EXC, FALSE);
4368
            err_format_msg(buf, buflen, tmpmsg, &tmpexc);
4369
        }
4370
    }
4371
    else
4372
    {
4373
        /* 
4374
         *   It's a VM exception, so we can determine the error's meaning
4375
         *   from the error code.  Look up the message for the error code
4376
         *   in our error message list.  
4377
         */
4378
        msg = err_get_msg(vm_messages, vm_message_count,
4379
                          exc->get_error_code(), FALSE);
4380
        
4381
        /* if that failed, just show the error number */
4382
        if (msg == 0)
4383
        {
4384
            /* no message - just show the error code */
4385
            tmpmsg = err_get_msg(vm_messages, vm_message_count,
4386
                                 VMERR_VM_EXC_CODE, FALSE);
4387
4388
            /* set up our parameters for formatting */
4389
            tmpexc.param_count_ = 1;
4390
            tmpexc.set_param_int(0, exc->get_error_code());
4391
4392
            /* format the message */
4393
            err_format_msg(buf, buflen, tmpmsg, &tmpexc);
4394
        }
4395
        else
4396
        {
4397
            char tmpbuf[256];
4398
4399
            /* format the message from the exception parameters */
4400
            err_format_msg(tmpbuf, sizeof(tmpbuf), msg, exc);
4401
            
4402
            /* get the prefix message */
4403
            tmpmsg = err_get_msg(vm_messages, vm_message_count,
4404
                                 VMERR_VM_EXC_PARAM, FALSE);
4405
4406
            /* set up our parameters for the formatting */
4407
            tmpexc.param_count_ = 1;
4408
            tmpexc.set_param_str(0, tmpbuf);
4409
4410
            /* format the message */
4411
            err_format_msg(buf, buflen, tmpmsg, &tmpexc);
4412
        }
4413
    }
4414
}
4415
4416
/*
4417
 *   Get the message from an "unhandled exception" error object
4418
 */
4419
const char *CVmRun::get_exc_message(VMG_ const CVmException *exc,
4420
                                    size_t *msg_len)
4421
{
4422
    vm_obj_id_t exc_obj;
4423
4424
    /* 
4425
     *   if the error isn't "unhandled exception," there's not a stored
4426
     *   exception object; likewise, if there's no object parameter in the
4427
     *   exception, there's nothing to use to obtain the message 
4428
     */
4429
    if (exc->get_error_code() != VMERR_UNHANDLED_EXC
4430
        || exc->get_param_count() < 1)
4431
        return 0;
4432
    
4433
    /* get the exception object */
4434
    exc_obj = (vm_obj_id_t)exc->get_param_ulong(0);
4435
4436
    /* get the message from the object */
4437
    return get_exc_message(vmg_ exc_obj, msg_len);
4438
}
4439
4440
/*
4441
 *   Get the message from an exception object 
4442
 */
4443
const char *CVmRun::get_exc_message(VMG_ vm_obj_id_t exc_obj, size_t *msg_len)
4444
{
4445
    vm_val_t val;
4446
    vm_obj_id_t src_obj;
4447
    const char *str;
4448
    uint argc;
4449
    
4450
    /* if there's no object, there's no message */
4451
    if (exc_obj == VM_INVALID_OBJ)
4452
        return 0;
4453
    
4454
    /* 
4455
     *   get the exceptionMessage property value from the object; if
4456
     *   there's not a valid exceptionMessage property defined, or the
4457
     *   object doesn't have a value for the property, there's no message 
4458
     */
4459
    argc = 0;
4460
    if (G_predef->rterrmsg_prop == VM_INVALID_PROP
4461
        || (!vm_objp(vmg_ exc_obj)->get_prop(vmg_ G_predef->rterrmsg_prop,
4462
                                             &val, exc_obj, &src_obj,
4463
                                             &argc)))
4464
        return 0;
4465
4466
    /* 
4467
     *   We got the property.  If it's a string or an object containing a
4468
     *   string, retrieve the string.
4469
     */
4470
    switch(val.typ)
4471
    {
4472
    case VM_SSTRING:
4473
        /* get the constant string */
4474
        str = G_const_pool->get_ptr(val.val.ofs);
4475
        break;
4476
4477
    case VM_OBJ:
4478
        /* get the string value of the object, if possible */
4479
        str = vm_objp(vmg_ val.val.obj)->get_as_string(vmg0_);
4480
        break;
4481
4482
    default:
4483
        /* it's not a string - we can't use it */
4484
        str = 0;
4485
        break;
4486
    }
4487
4488
    /* check to see if we got a string */
4489
    if (str != 0)
4490
    {
4491
        /* 
4492
         *   The string is in the standard VM internal format, which means
4493
         *   it has a 2-byte length prefix followed by the bytes of the
4494
         *   string (with no null termination).  Read the length prefix,
4495
         *   then skip past it so the caller doesn't have to.  
4496
         */
4497
        *msg_len = osrp2(str);
4498
        str += VMB_LEN;
4499
    }
4500
4501
    /* return the string pointer */
4502
    return str;
4503
}
4504
4505
/* ------------------------------------------------------------------------ */
4506
/*
4507
 *   Get the boundaries of the current statement, based on debugging
4508
 *   information.  Returns true if valid debugging information was found for
4509
 *   the given code location, false if not.  
4510
 */
4511
int CVmRun::get_stm_bounds(VMG_ const CVmFuncPtr *func_ptr,
4512
                           ulong method_ofs,
4513
                           CVmDbgLinePtr *caller_line_ptr,
4514
                           ulong *stm_start, ulong *stm_end)
4515
{
4516
    CVmDbgTablePtr dbg_ptr;
4517
    int lo;
4518
    int hi;
4519
    int cur;
4520
4521
    /* presume we won't find anything */
4522
    *stm_start = *stm_end = 0;
4523
4524
    /* 
4525
     *   if the current method has no line records, we can't find the
4526
     *   boundaries 
4527
     */
4528
    if (!func_ptr->set_dbg_ptr(&dbg_ptr)
4529
        || dbg_ptr.get_line_count(vmg0_) == 0)
4530
    {
4531
        /* indicate that we didn't find debug information */
4532
        return FALSE;
4533
    }
4534
4535
    /*
4536
     *   We must perform a binary search of the line records for the line
4537
     *   that contains this program counter offset.  
4538
     */
4539
    lo = 0;
4540
    hi = dbg_ptr.get_line_count(vmg0_) - 1;
4541
    while (lo <= hi)
4542
    {
4543
        ulong start_ofs;
4544
        ulong end_ofs;
4545
        CVmDbgLinePtr line_ptr;
4546
4547
        /* split the difference and get the current entry */
4548
        cur = lo + (hi - lo)/2;
4549
        dbg_ptr.set_line_ptr(vmg_ &line_ptr, cur);
4550
4551
        /* get the current statement's start relative to the method header */
4552
        start_ofs = line_ptr.get_start_ofs();
4553
4554
        /* 
4555
         *   Get the next statement's start offset, which gives us the end
4556
         *   of this statement.  If this is the last statement in the table,
4557
         *   it runs to the end of the function; use the debug records table
4558
         *   offset as the upper bound in this case.  
4559
         */
4560
        if (cur == (int)dbg_ptr.get_line_count(vmg0_) - 1)
4561
        {
4562
            /* 
4563
             *   it's the last record - use the debug table offset as an
4564
             *   upper bound, since we know the function can't have any
4565
             *   executable code past this point 
4566
             */
4567
            end_ofs = func_ptr->get_debug_ofs();
4568
        }
4569
        else
4570
        {
4571
            CVmDbgLinePtr next_line_ptr;
4572
4573
            /* another record follows this one - use it */
4574
            next_line_ptr.copy_from(&line_ptr);
4575
            next_line_ptr.inc(vmg0_);
4576
            end_ofs = next_line_ptr.get_start_ofs();
4577
        }
4578
4579
        /* see where we are relative to this line record */
4580
        if (method_ofs >= end_ofs)
4581
        {
4582
            /* we need to go higher */
4583
            lo = (cur == lo ? cur + 1 : cur);
4584
        }
4585
        else if (method_ofs < start_ofs)
4586
        {
4587
            /* we need to go lower */
4588
            hi = (cur == hi ? hi - 1 : cur);
4589
        }
4590
        else
4591
        {
4592
            /* found it - set the bounds to this record's limits */
4593
            *stm_start = start_ofs;
4594
            *stm_end = end_ofs;
4595
4596
            /* fill in the caller's line pointer if desired */
4597
            if (caller_line_ptr != 0)
4598
                caller_line_ptr->copy_from(&line_ptr);
4599
4600
            /* indicate that we found the line boundaries successfully */
4601
            return TRUE;
4602
        }
4603
    }
4604
4605
    /* return failure */
4606
    return FALSE;
4607
}
4608
4609
/* ------------------------------------------------------------------------ */
4610
/*
4611
 *   Profiler functions
4612
 */
4613
#ifdef VM_PROFILER
4614
4615
/*
4616
 *   Profiler master hash table entry 
4617
 */
4618
class CVmHashEntryProfiler: public CVmHashEntryCI
4619
{
4620
public:
4621
    CVmHashEntryProfiler(const char *str, size_t len,
4622
                         const vm_profiler_rec *rec)
4623
        : CVmHashEntryCI(str, len, TRUE)
4624
    {
4625
        /* copy the profiler record's identifying portion */
4626
        rec_.func = rec->func;
4627
        rec_.obj = rec->obj;
4628
        rec_.prop = rec->prop;
4629
4630
        /* initialize the timers and counters to zero */
4631
        rec_.sum_direct.hi = rec_.sum_direct.lo = 0;
4632
        rec_.sum_chi.hi = rec_.sum_chi.lo = 0;
4633
        rec_.call_cnt = 0;
4634
    }
4635
4636
    /* our profiler record */
4637
    vm_profiler_rec rec_;
4638
};
4639
4640
/*
4641
 *   Begin profiling 
4642
 */
4643
void CVmRun::start_profiling()
4644
{
4645
    /* clear any old profiler data from the master hash table */
4646
    prof_master_table_->delete_all_entries();
4647
4648
    /* reset the profiler stack */
4649
    prof_stack_idx_ = 0;
4650
4651
    /* turn on profiling */
4652
    profiling_ = TRUE;
4653
}
4654
4655
/*
4656
 *   End profiling 
4657
 */
4658
void CVmRun::end_profiling()
4659
{
4660
    /* turn off profiling */
4661
    profiling_ = FALSE;
4662
4663
    /* leave all active profiler stack levels */
4664
    while (prof_stack_idx_ != 0)
4665
        prof_leave();
4666
}
4667
4668
/* context for our profiling callback */
4669
struct vmrun_prof_enum
4670
{
4671
    /* interpreter object */
4672
    CVmRun *terp;
4673
4674
    /* debugger object */
4675
    CVmDebug *dbg;
4676
4677
    /* client callback and its context */
4678
    void (*cb)(void *, const char *, unsigned long, unsigned long,
4679
               unsigned long);
4680
    void *cb_ctx;
4681
};
4682
4683
/*
4684
 *   Get the profiling data 
4685
 */
4686
void CVmRun::get_profiling_data(VMG_
4687
                                void (*cb)(void *,
4688
                                           const char *,
4689
                                           unsigned long,
4690
                                           unsigned long,
4691
                                           unsigned long),
4692
                                void *cb_ctx)
4693
{
4694
    vmrun_prof_enum our_ctx;
4695
4696
    /* if there's no debugger, we can't get symbols, so we can't proceed */
4697
    if (G_debugger == 0)
4698
        return;
4699
4700
    /* set up our callback context */
4701
    our_ctx.terp = this;
4702
    our_ctx.dbg = G_debugger;
4703
    our_ctx.cb = cb;
4704
    our_ctx.cb_ctx = cb_ctx;
4705
4706
    /* enumerate the master table entries through our callback */
4707
    prof_master_table_->enum_entries(&prof_enum_cb, &our_ctx);
4708
}
4709
4710
/*
4711
 *   Callback for enumerating the profiling data 
4712
 */
4713
void CVmRun::prof_enum_cb(void *ctx0, CVmHashEntry *entry0)
4714
{
4715
    vmrun_prof_enum *ctx = (vmrun_prof_enum *)ctx0;
4716
    CVmHashEntryProfiler *entry = (CVmHashEntryProfiler *)entry0;
4717
    char namebuf[128];
4718
    const char *p;
4719
4720
    /* generate the name of the function or method */
4721
    if (entry->rec_.obj != VM_INVALID_OBJ)
4722
    {
4723
        char *dst;
4724
        
4725
        /* look up the object name */
4726
        p = ctx->dbg->objid_to_sym(entry->rec_.obj);
4727
4728
        /* get the original name, if this is a synthetic 'modify' object */
4729
        p = ctx->dbg->get_modifying_sym(p);
4730
4731
        /* 
4732
         *   if we got an object name, use it; otherwise, synthesize a name
4733
         *   using the object number 
4734
         */
4735
        if (p != 0)
4736
            strcpy(namebuf, p);
4737
        else
4738
            sprintf(namebuf, "obj#%lx", (long)entry->rec_.obj);
4739
4740
        /* add a period */
4741
        dst = namebuf + strlen(namebuf);
4742
        *dst++ = '.';
4743
4744
        /* look up the property name */
4745
        p = ctx->dbg->propid_to_sym(entry->rec_.prop);
4746
        if (p != 0)
4747
            strcpy(dst, p);
4748
        else
4749
            sprintf(dst, "prop#%x", (int)entry->rec_.prop);
4750
    }
4751
    else if (entry->rec_.func != 0)
4752
    {
4753
        /* look up the function at the code offset */
4754
        p = ctx->dbg->funcaddr_to_sym(entry->rec_.func);
4755
        if (p != 0)
4756
            strcpy(namebuf, p);
4757
        else
4758
            sprintf(namebuf, "func#%lx", (long)entry->rec_.func);
4759
    }
4760
    else
4761
    {
4762
        /* it must be system code */
4763
        strcpy(namebuf, "<System>");
4764
    }
4765
4766
    /* invoke the callback with the data */
4767
    (*ctx->cb)(ctx->cb_ctx, namebuf,
4768
               os_prof_time_to_ms(&entry->rec_.sum_direct),
4769
               os_prof_time_to_ms(&entry->rec_.sum_chi),
4770
               entry->rec_.call_cnt);
4771
}
4772
4773
4774
/*
4775
 *   Profile entry into a new function or method
4776
 */
4777
void CVmRun::prof_enter(pool_ofs_t call_ofs,
4778
                        vm_obj_id_t obj, vm_prop_id_t prop)
4779
{
4780
    vm_prof_time cur;
4781
4782
    /* get the current time */
4783
    os_prof_curtime(&cur);
4784
4785
    /* if we have a valid previous entry, suspend it */
4786
    if (prof_stack_idx_ > 0 && prof_stack_idx_ - 1 < prof_stack_max_)
4787
    {
4788
        vm_profiler_rec *p;
4789
        vm_prof_time delta;
4790
4791
        /* get a pointer to the outgoing entry */
4792
        p = &prof_stack_[prof_stack_idx_ - 1];
4793
4794
        /* 
4795
         *   add the time since the last start to the cumulative time spent
4796
         *   in this function 
4797
         */
4798
        prof_calc_elapsed(&delta, &cur, &prof_start_);
4799
        prof_add_elapsed(&p->sum_direct, &delta);
4800
    }
4801
4802
    /* if we have room on the profiler stack, add a new level */
4803
    if (prof_stack_idx_ < prof_stack_max_)
4804
    {
4805
        vm_profiler_rec *p;
4806
4807
        /* get a pointer to the new entry */
4808
        p = &prof_stack_[prof_stack_idx_];
4809
4810
        /* remember the identifying data for the method or function */
4811
        p->func = call_ofs;
4812
        p->obj = obj;
4813
        p->prop = prop;
4814
4815
        /* we have no cumulative time yet */
4816
        p->sum_direct.hi = p->sum_direct.lo = 0;
4817
        p->sum_chi.hi = p->sum_chi.lo = 0;
4818
    }
4819
4820
    /* count the level */
4821
    ++prof_stack_idx_;
4822
4823
    /* remember the start time in the new current function */
4824
    os_prof_curtime(&prof_start_);
4825
}
4826
4827
/*
4828
 *   Profile returning from a function or method
4829
 */
4830
void CVmRun::prof_leave()
4831
{
4832
    vm_prof_time delta;
4833
    vm_prof_time cur;
4834
    vm_prof_time chi;
4835
4836
    /* get the current time */
4837
    os_prof_curtime(&cur);
4838
4839
    /* move to the last level */
4840
    --prof_stack_idx_;
4841
4842
    /* presume we won't know the child time */
4843
    chi.hi = chi.lo = 0;
4844
4845
    /* if we're on a valid level, finish the call */
4846
    if (prof_stack_idx_ < prof_stack_max_)
4847
    {
4848
        vm_profiler_rec *p;
4849
        CVmHashEntryProfiler *entry;
4850
4851
        /* get a pointer to the outgoing entry */
4852
        p = &prof_stack_[prof_stack_idx_];
4853
4854
        /* 
4855
         *   add the time since the last start to the cumulative time spent
4856
         *   in this function 
4857
         */
4858
        prof_calc_elapsed(&delta, &cur, &prof_start_);
4859
        prof_add_elapsed(&p->sum_direct, &delta);
4860
4861
        /*
4862
         *   Find or create the master record for the terminating function or
4863
         *   method, and add the cumulative times from this call to the
4864
         *   master record's cumulative times.  Also count the invocation in
4865
         *   the master record.  
4866
         */
4867
        entry = prof_find_master_rec(p);
4868
        prof_add_elapsed(&entry->rec_.sum_direct, &p->sum_direct);
4869
        prof_add_elapsed(&entry->rec_.sum_chi, &p->sum_chi);
4870
        ++(entry->rec_.call_cnt);
4871
4872
        /*
4873
         *   Calculate the cumulative time in the outgoing function - this is
4874
         *   the total time directly in the function plus the cumulative time
4875
         *   in all of its children.  We must add this to the caller's
4876
         *   cumulative child time, since this function and all of its
4877
         *   children are children of the caller and thus must count in the
4878
         *   caller's total child time.  
4879
         */
4880
        chi = p->sum_direct;
4881
        prof_add_elapsed(&chi, &p->sum_chi);
4882
    }
4883
4884
    /* if we're leaving to a valid level, re-activate it */
4885
    if (prof_stack_idx_ > 0 && prof_stack_idx_ < prof_stack_max_)
4886
    {
4887
        vm_profiler_rec *p;
4888
4889
        /* get a pointer to the resuming entry */
4890
        p = &prof_stack_[prof_stack_idx_ - 1];
4891
4892
        /* 
4893
         *   add the time spent in the child and its children to our
4894
         *   cumulative child time 
4895
         */
4896
        prof_add_elapsed(&p->sum_chi, &chi);
4897
    }
4898
4899
    /* 
4900
     *   remember the new start time for the function we're resuming - we
4901
     *   must reset this to the current time, since we measure deltas from
4902
     *   the last call or return on each call or return 
4903
     */
4904
    os_prof_curtime(&prof_start_);
4905
}
4906
4907
/*
4908
 *   Calculate an elapsed 64-bit time value 
4909
 */
4910
void CVmRun::prof_calc_elapsed(vm_prof_time *diff, const vm_prof_time *a,
4911
                               const vm_prof_time *b)
4912
{
4913
    /* calculate the differences of the low and high parts */
4914
    diff->lo = a->lo - b->lo;
4915
    diff->hi = a->hi - b->hi;
4916
4917
    /* 
4918
     *   if the low part ended up higher than it started, then we
4919
     *   underflowed, and hence must borrow from the high part 
4920
     */
4921
    if (diff->lo > a->lo)
4922
        --(diff->hi);
4923
}
4924
4925
/*
4926
 *   Add one elapsed time value to another
4927
 */
4928
void CVmRun::prof_add_elapsed(vm_prof_time *sum, const vm_prof_time *val)
4929
{
4930
    unsigned long orig_lo;
4931
4932
    /* remember the original low part */
4933
    orig_lo = sum->lo;
4934
    
4935
    /* add the low parts and high parts */
4936
    sum->lo += val->lo;
4937
    sum->hi += val->hi;
4938
4939
    /* 
4940
     *   if the low part of the sum is less than where it started, then it
4941
     *   overflowed, and we must hence carry to the high part 
4942
     */
4943
    if (sum->lo < orig_lo)
4944
        ++(sum->hi);
4945
}
4946
4947
/*
4948
 *   Find or create a hash table entry for a profiler record 
4949
 */
4950
CVmHashEntryProfiler *CVmRun::prof_find_master_rec(const vm_profiler_rec *p)
4951
{
4952
    const size_t id_siz = sizeof(p->func) + sizeof(p->obj) + sizeof(p->prop);
4953
    char id[id_siz];
4954
    CVmHashEntryProfiler *entry;
4955
    
4956
    /* 
4957
     *   Build the ID string, which we'll use as our hash key.  We never have
4958
     *   to serialize this, so it doesn't matter that it's dependent on byte
4959
     *   order and word size. 
4960
     */
4961
    memcpy(id, &p->func, sizeof(p->func));
4962
    memcpy(id + sizeof(p->func), &p->obj, sizeof(p->obj));
4963
    memcpy(id + sizeof(p->func) + sizeof(p->obj), &p->prop, sizeof(p->prop));
4964
4965
    /* try to find an existing entry */
4966
    entry = (CVmHashEntryProfiler *)prof_master_table_->find(id, id_siz);
4967
4968
    /* if we didn't find an entry, create one */
4969
    if (entry == 0)
4970
    {
4971
        /* create a new entry */
4972
        entry = new CVmHashEntryProfiler(id, id_siz, p);
4973
4974
        /* add it to the table */
4975
        prof_master_table_->add(entry);
4976
    }
4977
4978
    /* return the entry */
4979
    return entry;
4980
}
4981
4982
#endif /* VM_PROFILER */
4983
4984
/* ------------------------------------------------------------------------ */
4985
/*
4986
 *   Footnote - for the referring code, search the code above for
4987
 *   [REGISTER_P_FOOTNOTE].
4988
 *   
4989
 *   This footnote pertains to a 'register' declaration that causes gcc (and
4990
 *   probably some other compilers) to generate a warning message.  The
4991
 *   'register' declaration is useful on some compilers and will be retained.
4992
 *   Here's a note I sent to Nikos Chantziaras (who asked about the warning)
4993
 *   explaining why I'm choosing to leave the 'register' declaration in, and
4994
 *   why I think this 'register' declaration is actually correct and useful
4995
 *   despite the warning it generates on some compilers.
4996
 *   
4997
 *   The basic issue is that the code takes the address of the variable in
4998
 *   question in expressions passed as parameters to certain function calls.
4999
 *   These function calls all happen to be in-linable functions, and it
5000
 *   happens that in each function, the address operator is always canceled
5001
 *   out by a '*' dereference operator - in other words, we have '*&p', which
5002
 *   the compiler can turn into just plain 'p' when the calls are in-lined,
5003
 *   eliminating the need to actually take the address of 'p'.
5004
 *   
5005
 *   Nikos:
5006
 *.  >I'm no expert, but I think GCC barks at this because it isn't possible
5007
 *.  >at all to store the variable in a register if the code wants its
5008
 *.  >address, therefore the 'register' in the declaration does nothing.
5009
 *   
5010
 *   That's correct, but a compiler is always free to ignore 'register'
5011
 *   declarations *anyway*, even if enregistration is possible.  Therefore a
5012
 *   warning that it's not possible to obey 'register' is unnecessary,
5013
 *   because it's explicit in the language definition that 'register' is not
5014
 *   binding.  It simply is not possible for an ignored 'register' attribute
5015
 *   to cause unexpected behavior.  Warnings really should only be generated
5016
 *   for situations where it is likely that the programmer expects different
5017
 *   behavior than the compiler will deliver; in the case of an ignored
5018
 *   'register' attribute, the programmer is *required* to expect that the
5019
 *   attribute might be ignored, so a warning to this effect is superfluous.
5020
 *   
5021
 *   Now, I understand why they generate the warning - it's because the
5022
 *   compiler believes that the program code itself makes enregistration
5023
 *   impossible, not because the compiler has chosen for optimization
5024
 *   purposes to ignore the 'register' request.  However, as we'll see
5025
 *   shortly, the program code doesn't truly make enregistration impossible;
5026
 *   it is merely impossible in some interpretations of the code.  Therefore
5027
 *   we really are back to the compiler choosing to ignore the 'register'
5028
 *   request due to its own optimization decisions; the 'register' request is
5029
 *   made impossible far downstream of the actual decisions that the compiler
5030
 *   makes (which have to do with in-line vs out-of-line calls), but it
5031
 *   really is compiler decisions that make it impossible, not the inherent
5032
 *   structure of the code.
5033
 *   
5034
 *.  >Furthermore, I'm not sure I understand the relationship
5035
 *.  >between 'register' and inlining; why should "*(&p)" do something
5036
 *.  >else "in calls to inlines" than its obvious meaning?
5037
 *   
5038
 *   When a function is in-lined, the compiler is not required to generate
5039
 *   the same code it would generate for the most general case of the same
5040
 *   function call, as long as the meaning is the same.
5041
 *   
5042
 *   For example, suppose we have some code that contains a call to a
5043
 *   function like so:
5044
 *   
5045
 *   a = myFunc(a + 7, 3);
5046
 *   
5047
 *   In the general out-of-line case, the compiler must generate some
5048
 *   machine-code instructions like this:
5049
 *   
5050
 *.  push #3
5051
 *.  mov [a], d0
5052
 *.  add #7, d0
5053
 *.  push d0
5054
 *.  call #myFunc
5055
 *.  mov d0, [a]
5056
 *   
5057
 *   The compiler doesn't have access to the inner workings of myFunc, so it
5058
 *   must generate the appropriate code for the generic interface to an
5059
 *   external function.
5060
 *   
5061
 *   Now, suppose the function is defined like so:
5062
 *   
5063
 *   int myFunc(int a, int b) { return a - 6; }
5064
 *   
5065
 *   and further suppose that the compiler decides to in-line this function.
5066
 *   In-lining means the compiler will generate the code that implements the
5067
 *   function directly in the caller; there will be no call to an external
5068
 *   linkage point.  This means the compiler can implement the linkage to the
5069
 *   function with a custom one-off interface for this particular invocation
5070
 *   - every in-line invocation can be customized to the exact context where
5071
 *   it appears.  So, for example, if we call myFunc right now and registers
5072
 *   d1 and d2 happens to be available, we can put the parameters in d1 and
5073
 *   d2, and the generated function will refer to those registers for the
5074
 *   parameters rather than having to look in the stack.  Later on, if we
5075
 *   generate a separate call to the same function, but registers d3 and d7
5076
 *   are the ones available, we can use those instead.  Each generated copy
5077
 *   of the function can fit its exact context.
5078
 *   
5079
 *   Furthermore, looking at this function and at the arguments passed, we
5080
 *   can see that the formal parameter 'b' has no effect on the function's
5081
 *   results, and the actual parameter '3' passed for 'b' has no side
5082
 *   effects.  Therefore, the compiler is free to completely ignore this
5083
 *   parameter - there's no need to generate any code for it at all, since we
5084
 *   have sufficient knowledge to see that it has no effect on the meaning of
5085
 *   the code.
5086
 *   
5087
 *   Further still, we can globally optimize the entire function.  So, we can
5088
 *   see that myFunc(a+7, 3) is going to turn into the expression (a+7-6).
5089
 *   We can fold constants to arrive at (a+1) as the result of the function.
5090
 *   We can therefore generate the entire code for the function's invocation
5091
 *   like so:
5092
 *   
5093
 *   inc [a]
5094
 *   
5095
 *   Okay, now let's look at the &p case.  In the specific examples in
5096
 *   vmrun.cpp, we have a bunch of function invocations like this:
5097
 *   
5098
 *   register const char *p;
5099
 *.  int x = myfunc(&p);
5100
 *   
5101
 *   In the most general case, we have to generate code like this:
5102
 *   
5103
 *.  lea [p], d0        ; load effective address
5104
 *.  push d0
5105
 *.  call #myfunc
5106
 *.  mov d0, [x]
5107
 *   
5108
 *   So, in the most general case of a call with external linkage, we need
5109
 *   'p' to have a main memory address so that we can push it on the stack as
5110
 *   the parameter to this call.  Registers don't have main memory addresses,
5111
 *   so 'p' can't go in a register.
5112
 *   
5113
 *   However, we know what myfunc() looks like:
5114
 *   
5115
 *.  char myfunc(const char **p)
5116
 *.  {
5117
 *.      char c = **p;
5118
 *.      *p += 1;
5119
 *.      return c;
5120
 *.  }
5121
 *   
5122
 *   If the compiler chooses to in-line this function, it can globally
5123
 *   optimize its linkage and implementation as we saw earlier.  So, the
5124
 *   compiler can rewrite the code like so:
5125
 *   
5126
 *   register const char *p;
5127
 *.  int x = **(&p);
5128
 *.  *(&p) += 1;
5129
 *   
5130
 *   which can be further rewritten to:
5131
 *   
5132
 *.  register const char *p;
5133
 *.  int x = *p;
5134
 *.  p += 1;
5135
 *   
5136
 *   Now we can generate the machine code for the final optimized form:
5137
 *   
5138
 *.  mov [p], a0         ; get the *value* of p into index register 0
5139
 *.  mov.byte [a0+0], d0 ; get the value index register 0 points to
5140
 *.  mov.byte d0, [x]    ; store it in x
5141
 *.  inc [p]             ; inc the value of p
5142
 *   
5143
 *   Nowhere do we need a main memory address for p.  This means the compiler
5144
 *   can keep p in a register, say d5:
5145
 *   
5146
 *.  mov d5, a0
5147
 *.  mov.byte [a0+0], d0
5148
 *.  mov.byte d0, [x]
5149
 *.  inc d5
5150
 *   
5151
 *   And this is indeed exactly what the code that comes out of vc++ looks
5152
 *   like (changed from my abstract machine to 32-bit x86, of course).  
5153
 *   
5154
 *   So: if the compiler chooses to in-line the functions that are called
5155
 *   with '&p' as a parameter, and the compiler performs the available
5156
 *   optimizations on those calls once they're in-lined, then a memory
5157
 *   address for 'p' is never needed.  Thus there is a valid interpretation
5158
 *   of the code where 'register p' can be obeyed.  If the compiler doesn't
5159
 *   choose to in-line the functions or make those optimizations, then the
5160
 *   compiler will be unable to satisfy the 'register p' request and will be
5161
 *   forced to put 'p' in addressable main memory.  But it really is entirely
5162
 *   up to the compiler whether to obey the 'register p' request; the
5163
 *   program's structure does not make the request impossible to satisfy.
5164
 *   Therefore there is no reason for the compiler to warn about this, any
5165
 *   more than there would be if the compiler chose not to obey the 'register
5166
 *   p' simply because it thought it could make more optimal use of the
5167
 *   available registers.  That gcc warns is understandable, in that a
5168
 *   superficial reading of the code would not reveal the optimization
5169
 *   opportunity; but the warning is nonetheless unnecessary, and the
5170
 *   'register' does provide useful optimization hinting to at least vc++, so
5171
 *   I think it's best to leave it in and ignore the warning.  
5172
 */