cfad47cfa3/tads3/vmrun.h

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
/* $Header: d:/cvsroot/tads/tads3/vmrun.h,v 1.4 1999/07/11 00:46:59 MJRoberts Exp $ */
2
3
/* 
4
 *   Copyright (c) 1998, 2002 Michael J. Roberts.  All Rights Reserved.
5
 *   
6
 *   Please see the accompanying license file, LICENSE.TXT, for information
7
 *   on using and copying this software.  
8
 */
9
/*
10
Name
11
  vmrun.h - VM Execution
12
Function
13
  
14
Notes
15
  
16
Modified
17
  11/12/98 MJRoberts  - Creation
18
*/
19
20
#ifndef VMRUN_H
21
#define VMRUN_H
22
23
#include "vmglob.h"
24
#include "vmtype.h"
25
#include "vmstack.h"
26
#include "vmpool.h"
27
#include "vmobj.h"
28
#include "vmerr.h"
29
#include "vmerrnum.h"
30
#include "vmprofty.h"
31
32
33
/* ------------------------------------------------------------------------ */
34
/*
35
 *   for debugger use - interpreter context save structure 
36
 */
37
struct vmrun_save_ctx
38
{
39
    pool_ofs_t entry_ptr_;
40
    vm_val_t *frame_ptr_;
41
    size_t old_stack_depth_;
42
    const uchar **pc_ptr_;
43
};
44
45
/* ------------------------------------------------------------------------ */
46
/*
47
 *   Procedure activation frame.  The activation frame is arranged as
48
 *   follows (the stack index increases reading down the list):
49
 *   
50
 *.  argument N
51
 *.  argument N-1
52
 *.  ...
53
 *.  argument 2
54
 *.  argument 1
55
 *.  target property
56
 *.  original target object
57
 *.  defining object
58
 *.  self
59
 *.  offset in calling method of next instruction to execute
60
 *.  caller's entry pointer (EP) register value
61
 *.  actual parameter count
62
 *.  caller's frame pointer <<<--- CURRENT FRAME POINTER
63
 *.  local variable 1
64
 *.  local variable 2
65
 *.  local variable 3
66
 *   
67
 *   So, local variable 1 is at (FP+1), local variable 2 is at (FP+2), and
68
 *   so on; the argument count is at (FP-1); 'self' is at (FP-4); argument 1
69
 *   is at (FP-5), argument 2 is at (FP-6), and so on.  
70
 */
71
72
/* offset from FP of first argument */
73
const int VMRUN_FPOFS_ARG1 = -8;
74
75
/* offset from FP of target property */
76
const int VMRUN_FPOFS_PROP = -7;
77
78
/* offset from FP of original target object */
79
const int VMRUN_FPOFS_ORIGTARG = -6;
80
81
/* offset from FP of defining object (definer of current method) */
82
const int VMRUN_FPOFS_DEFOBJ = -5;
83
84
/* offset from FP of 'self' */
85
const int VMRUN_FPOFS_SELF = -4;
86
87
/* offset from FP of return address */
88
const int VMRUN_FPOFS_RET = -3;
89
90
/* offset from FP of enclosing entry pointer */
91
const int VMRUN_FPOFS_ENC_EP = -2;
92
93
/* offset from FP of argument count */
94
const int VMRUN_FPOFS_ARGC = -1;
95
96
/* offset from FP of enclosing frame pointer */
97
const int VMRUN_FPOFS_ENC_FP = 0;
98
99
/* offset from FP of first local variable */
100
const int VMRUN_FPOFS_LCL1 = 1;
101
102
103
/* ------------------------------------------------------------------------ */
104
/*
105
 *   VM Execution Engine class.  This class handles execution of byte
106
 *   code.  
107
 */
108
class CVmRun
109
{
110
    friend class CVmDebug;
111
    
112
public:
113
    CVmRun();
114
    ~CVmRun();
115
116
    /* initialize */
117
    void init();
118
119
    /* terminate */
120
    void terminate();
121
122
    /*
123
     *   Get/set the method header size.  This size is stored in the image
124
     *   file; the image loader sets this at load time to the value
125
     *   retrieved from the image file.  All method headers in an image
126
     *   file use the same size.  
127
     */
128
    void set_funchdr_size(size_t siz);
129
    size_t get_funchdr_size() const { return funchdr_size_; }
130
    
131
    /* 
132
     *   Call a function or method.  'ofs' is the offset (in the code pool)
133
     *   of the code to invoke, and 'argc' is the number of arguments that
134
     *   the caller has pushed onto the stack.
135
     *   
136
     *   'caller_ofs' is the method offset (the byte code offset from the
137
     *   current entry pointer) in the caller.  If 'caller_ofs' is non-zero,
138
     *   we'll set up to begin execution in the target code and return the
139
     *   new program counter.  If 'caller_ofs' is zero, we'll invoke the VM
140
     *   byte code interpreter recursively, so this function will return
141
     *   only after the called code returns.  When calling recursively, set
142
     *   'recurse_calling' to a descriptive string that can be used to show
143
     *   the system code calling the recursive code in case of error.
144
     *   
145
     *   When calling a function, 'self' should be VM_INVALID_OBJ.
146
     *   Otherwise, this value gives the object whose method is being
147
     *   invoked.  
148
     *   
149
     *   The return value is the new program counter.  For recursive
150
     *   invocations, this will simply return null.  
151
     */
152
    const uchar *do_call(VMG_ uint caller_ofs, pool_ofs_t ofs, uint argc,
153
                         vm_obj_id_t self, vm_prop_id_t target_prop,
154
                         vm_obj_id_t orig_target_obj,
155
                         vm_obj_id_t defining_obj,
156
                         const char *recurse_calling);
157
158
    /* call a function, non-recursively */
159
    const uchar *do_call_func_nr(VMG_ uint caller_ofs, pool_ofs_t ofs,
160
                                 uint argc);
161
162
    /*
163
     *   Call a function pointer value.  If 'funcptr' contains a function
164
     *   pointer, we'll simply call the function; if it contains an
165
     *   anonymous function object, we'll call the anonymous function.  
166
     */
167
    const uchar *call_func_ptr(VMG_ const vm_val_t *funcptr, uint argc,
168
                               const char *recurse_name, uint caller_ofs);
169
        
170
    /*
171
     *   Get the descriptive message, if any, from an exception object.
172
     *   The returned string will not be null-terminated, but the length
173
     *   will be stored in *msg_len.  The returned string might point to
174
     *   constant pool data or data in an object, so it might not remain
175
     *   valid after a constant pool translation or garbage collection
176
     *   operation.  If the exception has no message, we will return a
177
     *   null pointer.  
178
     */
179
    static const char *get_exc_message(VMG_ const CVmException *exc,
180
                                       size_t *msg_len);
181
    static const char *get_exc_message(VMG_ vm_obj_id_t obj, size_t *msg_len);
182
183
    /* 
184
     *   Get the descriptive message from an exception.  If the exception
185
     *   has a program-generated exception object, we'll try to get the
186
     *   message from that object; if it's a VM exception with no
187
     *   underlying object, we'll retrieve the VM message.
188
     *   
189
     *   If add_unh_prefix is true, we'll add an "unhandled exception:"
190
     *   prefix to the message if we retrieve the message from a
191
     *   program-defined exception.  Otherwise, if it's a program
192
     *   exception, we won't add any prefix at all.  
193
     */
194
    static void get_exc_message(VMG_ const CVmException *exc,
195
                                char *buf, size_t buflen, int add_unh_prefix);
196
197
    /*
198
     *   Evaluate a property of an object.  'target_obj' is the object whose
199
     *   property is to be evaluated, 'target_prop' is the ID of the
200
     *   property to evaluate, and 'argc' is the number of arguments to the
201
     *   method.
202
     *   
203
     *   'caller_ofs' is the current method offset (the offset from the
204
     *   current entry pointer to the current program counter) in the
205
     *   caller.  If this is zero, we'll make a recursive call to the
206
     *   interpreter loop to execute any method code; thus, any method code
207
     *   will have run to completion by the time we return in this case.
208
     *   
209
     *   'self' is the object in whose context we're to perform the code
210
     *   execution, if the property is a method.  Note that 'self' may
211
     *   differ from 'target_obj' in some cases, particularly when
212
     *   inheriting.
213
     *   
214
     *   The return value is the new program counter from which execution
215
     *   should resume.  This will be null (and can be ignored) for
216
     *   recursive invocations.  
217
     */
218
    const uchar *get_prop(VMG_ uint caller_ofs,
219
                          const vm_val_t *target_obj,
220
                          vm_prop_id_t target_prop,
221
                          const vm_val_t *self, uint argc);
222
223
    /*
224
     *   Set a property of an object 
225
     */
226
    void set_prop(VMG_ vm_obj_id_t obj, vm_prop_id_t prop,
227
                  const vm_val_t *new_val);
228
229
    /* get data register 0 (R0) */
230
    vm_val_t *get_r0() { return &r0_; }
231
232
    /* set the default "say" function */
233
    void set_say_func(VMG_ const vm_val_t *val);
234
235
    /* get the current default "say" function */
236
    void get_say_func(vm_val_t *val) const;
237
238
    /* set the default "say" method */
239
    void set_say_method(vm_prop_id_t prop)
240
    {
241
        /* remember the property */
242
        say_method_ = prop;
243
    }
244
245
    /* get the current "say" method */
246
    vm_prop_id_t get_say_method() const { return say_method_; }
247
248
    /* pop an integer value; throws an error if the value is not an integer */
249
    void pop_int(VMG_ vm_val_t *val)
250
    {
251
        G_stk->pop(val);
252
        if (val->typ != VM_INT)
253
            err_throw(VMERR_INT_VAL_REQD);
254
    }
255
256
    /* 
257
     *   Pop a numeric value; throws an error if the value is not numeric.
258
     *   (At the moment, this is equivalent to pop_int, since int is the
259
     *   only numeric type; however, we distinguish between numbers in
260
     *   general and integers in particular, in case additional numeric
261
     *   types [such as floating-point numbers] are added in the future.) 
262
     */
263
    void pop_num(VMG_ vm_val_t *val)
264
    {
265
        G_stk->pop(val);
266
        if (!val->is_numeric())
267
            err_throw(VMERR_NUM_VAL_REQD);
268
    }
269
270
    /* pop an object value */
271
    void pop_obj(VMG_ vm_val_t *val)
272
    {
273
        G_stk->pop(val);
274
        if (val->typ != VM_OBJ)
275
            err_throw(VMERR_OBJ_VAL_REQD);
276
    }
277
278
    /* pop a property pointer value */
279
    void pop_prop(VMG_ vm_val_t *val)
280
    {
281
        G_stk->pop(val);
282
        if (val->typ != VM_PROP)
283
            err_throw(VMERR_PROPPTR_VAL_REQD);
284
    }
285
286
    /* pop a function pointer value */
287
    void pop_funcptr(VMG_ vm_val_t *val)
288
    {
289
        G_stk->pop(val);
290
        if (val->typ != VM_FUNCPTR)
291
            err_throw(VMERR_FUNCPTR_VAL_REQD);
292
    }
293
294
    /* 
295
     *   Pop two values from the stack.  The values are popped in reverse
296
     *   order, so val2 has the value at the top of the stack.  If the
297
     *   left operand was pushed first, this results in placing the left
298
     *   operand in val1 and the right operand in val2.  
299
     */
300
    void popval_2(VMG_ vm_val_t *val1, vm_val_t *val2)
301
    {
302
        popval(vmg_ val2);
303
        popval(vmg_ val1);
304
    }
305
306
    /* 
307
     *   Pop two integers, throwing an error if either value is not an
308
     *   integer.  Pops the item at the top of the stack into val2, and
309
     *   the next value into val1. 
310
     */
311
    void pop_int_2(VMG_ vm_val_t *val1, vm_val_t *val2)
312
    {
313
        popval(vmg_ val2);
314
        popval(vmg_ val1);
315
        if (val1->typ != VM_INT || val2->typ != VM_INT)
316
            err_throw(VMERR_INT_VAL_REQD);
317
    }
318
319
    /*
320
     *   Pop two numbers, throwing an error if either value is not
321
     *   numeric. 
322
     */
323
    void pop_num_2(VMG_ vm_val_t *val1, vm_val_t *val2)
324
    {
325
        popval(vmg_ val2);
326
        popval(vmg_ val1);
327
        if (!val1->is_numeric() || !val2->is_numeric())
328
            err_throw(VMERR_NUM_VAL_REQD);
329
    }
330
331
    /* 
332
     *   get the active function's argument count - we read the value from
333
     *   the first item below the frame pointer in the current frame 
334
     */
335
    int get_cur_argc(VMG0_) const
336
    {
337
        return G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_ARGC)
338
            ->val.intval;
339
    }
340
    
341
    /*
342
     *   Get a parameter value; 0 is the first parameter, 1 is the second,
343
     *   and so on.  
344
     */
345
    vm_val_t *get_param(VMG_ int idx) const
346
        { return get_param_from_frame(vmg_ frame_ptr_, idx); }
347
348
    /* get a parameter from a given frame */
349
    vm_val_t *get_param_from_frame(VMG_ vm_val_t *fp, int idx) const
350
        { return G_stk->get_from_frame(fp, VMRUN_FPOFS_ARG1 - idx); }
351
352
    /* get a parameter at the given stack level */
353
    vm_val_t *get_param_at_level(VMG_ int idx, int level) const
354
    {
355
        return get_param_from_frame(vmg_ get_fp_at_level(vmg_ level), idx);
356
    }
357
358
    /* 
359
     *   get a local variable's value; 0 is the first local variable, 1 is
360
     *   the second, and so on 
361
     */
362
    vm_val_t *get_local(VMG_ int idx) const
363
        { return get_local_from_frame(vmg_ frame_ptr_, idx); }
364
365
    /* get a local from a given frame */
366
    vm_val_t *get_local_from_frame(VMG_ vm_val_t *fp, int idx) const
367
        { return G_stk->get_from_frame(fp, VMRUN_FPOFS_LCL1 + idx); }
368
369
    /* get a local at the given stack level */
370
    vm_val_t *get_local_at_level(VMG_ int idx, int level) const
371
    {
372
        return get_local_from_frame(vmg_ get_fp_at_level(vmg_ level), idx);
373
    }
374
375
376
    /*
377
     *   Get the frame pointer at the given stack level.  Level 0 is the
378
     *   currently active frame, 1 is the first enclosing level, and so
379
     *   on.  Throws an error if the enclosing frame is invalid. 
380
     */
381
    vm_val_t *get_fp_at_level(VMG_ int level) const;
382
383
    /*
384
     *   Get the current frame depth.  This is the stack depth of the
385
     *   current frame pointer.  This can be used to compare two frame
386
     *   pointers to determine if one encloses the other - the pointer
387
     *   with the smaller depth value encloses the larger one.  
388
     */
389
    size_t get_frame_depth(VMG0_) const
390
        { return G_stk->ptr_to_index(frame_ptr_); }
391
392
    /* get the current frame pointer */
393
    vm_val_t *get_frame_ptr() const { return frame_ptr_; }
394
395
    /* given a frame pointer, get the enclosing frame pointer */
396
    static vm_val_t *get_enclosing_frame_ptr(VMG_ vm_val_t *fp)
397
    {
398
        return (vm_val_t *)
399
            G_stk->get_from_frame(fp, VMRUN_FPOFS_ENC_FP)->val.ptr;
400
    }
401
402
    /* get the number of arguments from a given frame */
403
    int get_argc_from_frame(VMG_ vm_val_t *fp) const
404
        { return G_stk->get_from_frame(fp, VMRUN_FPOFS_ARGC)->val.intval; }
405
406
    /* get the argument counter from a given stack level */
407
    int get_argc_at_level(VMG_ int level) const
408
        { return get_argc_from_frame(vmg_ get_fp_at_level(vmg_ level)); }
409
410
    /* given a frame pointer, get the 'self' object for the frame */
411
    static vm_obj_id_t get_self_from_frame(VMG_ vm_val_t *fp)
412
    {
413
        vm_val_t *self_val;
414
415
        /* get the 'self' slot on the stack */
416
        self_val = G_stk->get_from_frame(fp, VMRUN_FPOFS_SELF);
417
418
        /* return the appropriate value */
419
        return (self_val->typ == VM_NIL ? VM_INVALID_OBJ : self_val->val.obj);
420
    }
421
422
    /* get the 'self' object at a given stack level */
423
    vm_obj_id_t get_self_at_level(VMG_ int level) const
424
        { return get_self_from_frame(vmg_ get_fp_at_level(vmg_ level)); }
425
426
    /* given a frame pointer, get the target property for the frame */
427
    static vm_prop_id_t get_target_prop_from_frame(VMG_ vm_val_t *fp)
428
    {
429
        vm_val_t *val;
430
431
        /* get the 'self' slot on the stack */
432
        val = G_stk->get_from_frame(fp, VMRUN_FPOFS_PROP);
433
434
        /* return the appropriate value */
435
        return (val->typ == VM_NIL ? VM_INVALID_PROP : val->val.prop);
436
    }
437
438
    /* get the target property at a given stack level */
439
    vm_prop_id_t get_target_prop_at_level(VMG_ int level) const
440
    {
441
        return get_target_prop_from_frame(vmg_ get_fp_at_level(vmg_ level));
442
    }
443
444
    /* given a frame pointer, get the defining object from the frame */
445
    vm_obj_id_t get_defining_obj_from_frame(VMG_ vm_val_t *fp) const
446
    {
447
        vm_val_t *val;
448
449
        /* get the defining object slot on the stack */
450
        val = G_stk->get_from_frame(fp, VMRUN_FPOFS_DEFOBJ);
451
452
        /* return the appropriate value */
453
        return (val->typ == VM_NIL ? VM_INVALID_OBJ : val->val.obj);
454
    }
455
456
    /* get the defining object at a given stack level */
457
    vm_obj_id_t get_defining_obj_at_level(VMG_ int level) const
458
    {
459
        return get_defining_obj_from_frame(vmg_ get_fp_at_level(vmg_ level));
460
    }
461
462
    /* given a frame pointer, get the original target object */
463
    vm_obj_id_t get_orig_target_obj_from_frame(VMG_ vm_val_t *fp) const
464
    {
465
        vm_val_t *val;
466
467
        /* get the original target object slot on the stack */
468
        val = G_stk->get_from_frame(fp, VMRUN_FPOFS_ORIGTARG);
469
470
        /* return the appropriate value */
471
        return (val->typ == VM_NIL ? VM_INVALID_OBJ : val->val.obj);
472
    }
473
474
    /* get the current original target object at a given stack level */
475
    vm_obj_id_t get_orig_target_obj_at_level(VMG_ int level) const
476
    {
477
        return get_orig_target_obj_from_frame(
478
            vmg_ get_fp_at_level(vmg_ level));
479
    }
480
481
    /* 
482
     *   get the enclosing entry pointer from a given frame (it's the
483
     *   second item pushed before the enclosing frame ponter, hence it's
484
     *   at offset -2 in the frame) 
485
     */
486
    static pool_ofs_t get_enclosing_entry_ptr_from_frame(VMG_ vm_val_t *fp)
487
        { return G_stk->get_from_frame(fp, VMRUN_FPOFS_ENC_EP)->val.ofs; }
488
489
    /*
490
     *   Get the return offset from a given frame.  This is the offset of
491
     *   the return address from the start of the method header for the
492
     *   frame. 
493
     */
494
    static ulong get_return_ofs_from_frame(VMG_ vm_val_t *fp)
495
        { return G_stk->get_from_frame(fp, VMRUN_FPOFS_RET)->val.ofs; }
496
497
    /* 
498
     *   Get the return address from a given frame.  (The return address
499
     *   is the third item pushed before the enclosing frame pointer,
500
     *   hence it's at offset -3 from the frame pointer.)  Returns zero if
501
     *   we were called by recursive invocation of the VM - this is not
502
     *   ambiguous with an actual return address of zero, since zero is
503
     *   never a valid code address.
504
     */
505
    static pool_ofs_t get_return_addr_from_frame(VMG_ vm_val_t *fp)
506
    {
507
        pool_ofs_t ofs;
508
509
        /* get the return method offset from the stack */
510
        ofs = get_return_ofs_from_frame(vmg_ fp);
511
512
        /* 
513
         *   zero is never a valid method offset, so if the offset is zero
514
         *   it means that we're at a recursive invocation of the VM -
515
         *   indicate this by returning zero as the absolute pool address.
516
         */
517
        if (ofs == 0)
518
            return 0;
519
520
        /* 
521
         *   add the offset to the enclosing entry pointer to yield the
522
         *   absolute pool address of the return point 
523
         */
524
        return ofs + get_enclosing_entry_ptr_from_frame(vmg_ fp);
525
    }
526
                 
527
    /* 
528
     *   Given a frame pointer, set up a function pointer for the return
529
     *   address from the frame.  
530
     */
531
    static void set_return_funcptr_from_frame(VMG_ class CVmFuncPtr *func_ptr,
532
                                              vm_val_t *frame_ptr);
533
534
    /*
535
     *   Determine if we're in a recursive VM invocation.  Returns true if
536
     *   we're in a recursive invocation, false if we're in the top-level
537
     *   invocation.  (A recursive invocation occurs when native code
538
     *   called from byte code calls back into the VM to call byte code
539
     *   itself; because the invocation is recursive, we must return to
540
     *   the native code when the inner byte code returns rather than
541
     *   continuing with enclosing byte code directly.)
542
     */
543
    int is_recursive_invocation(VMG0_) const;
544
545
    /* reset the machine registers to the initial conditions */
546
    void reset(VMG0_);
547
    
548
    /*
549
     *   Get the current "self" object.  The "self" object is always the
550
     *   implicit first parameter to any method.  Note that this version of
551
     *   the method *doesn't* check for nil - it assumes that the caller
552
     *   knows for sure that there's a valid "self", so dispenses with any
553
     *   checks to save time.  
554
     */
555
    vm_obj_id_t get_self(VMG0_) const
556
    {
557
        /* get the object value of the 'self' slot in the current frame */
558
        return G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF)->val.obj;
559
    }
560
561
    /* get the pointer to the current "self" value */
562
    vm_val_t *get_self_val(VMG0_) const
563
        { return G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF); }
564
565
    /*
566
     *   Get the current "self" object, checking for nil.  If "self" is nil,
567
     *   we'll return VM_INVALID_OBJ.  This version (not get_self()) should
568
     *   be used whenever it's not certain from context that there's a valid
569
     *   "self".  
570
     */
571
    vm_obj_id_t get_self_check(VMG0_) const
572
    {
573
        /* get the 'self' slot from the stack frame */
574
        vm_val_t *valp = G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF);
575
576
        /* if it's nil, return VM_INVALID_OBJ; otherwise, return the obj ID */
577
        return (valp->typ == VM_NIL ? VM_INVALID_OBJ : valp->val.obj);
578
    }
579
580
    /* set the current 'self' object */
581
    void set_self(VMG_ const vm_val_t *val)
582
    {
583
        /* store the given value in the 'self' slot in the current frame */
584
        *G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF) = *val;
585
    }
586
587
    /*
588
     *   Set the current execution context: the 'self' value, the target
589
     *   property, the original target object, and the defining object.  
590
     */
591
    void set_method_ctx(VMG_ vm_obj_id_t new_self,
592
                        vm_prop_id_t new_target_prop,
593
                        vm_obj_id_t new_target_obj,
594
                        vm_obj_id_t new_defining_obj)
595
    {
596
        /* set the "self" slot in the current stack frame */
597
        G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF)
598
            ->set_obj(new_self);
599
600
        /* set the target property slot in the frame */
601
        G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_PROP)
602
            ->set_propid(new_target_prop);
603
604
        /* set the original target object slot in the frame */
605
        G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_ORIGTARG)
606
            ->set_obj(new_target_obj);
607
608
        /* set the defining object slot in the frame */
609
        G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_DEFOBJ)
610
            ->set_obj(new_defining_obj);
611
    }
612
613
    /* get the current target property value */
614
    vm_prop_id_t get_target_prop(VMG0_) const
615
    {
616
        return G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_PROP)->val.prop;
617
    }
618
619
    /* get the current defining object */
620
    vm_obj_id_t get_defining_obj(VMG0_) const
621
    {
622
        return G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_DEFOBJ)->val.obj;
623
    }
624
625
    /* get the current original target object */
626
    vm_obj_id_t get_orig_target_obj(VMG0_) const
627
    {
628
        return G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_ORIGTARG)
629
            ->val.obj;
630
    }
631
632
    /* push an object ID */
633
    static void push_obj(VMG_ vm_obj_id_t obj)
634
        { G_stk->push()->set_obj(obj); }
635
636
    /* push a property ID */
637
    static void push_prop(VMG_ vm_prop_id_t prop)
638
        { G_stk->push()->set_propid(prop); }
639
640
    /* push a boolean value */
641
    static void push_bool(VMG_ int flag)
642
        { G_stk->push()->set_logical(flag); }
643
644
    /* push nil */
645
    static void push_nil(VMG0_)
646
        { G_stk->push()->set_nil(); }
647
648
    /* push a code offset value */
649
    static void push_codeofs(VMG_ pool_ofs_t ofs)
650
        { G_stk->push()->set_codeofs(ofs); }
651
652
    /* push a stack pointer value */
653
    static void push_stackptr(VMG_ vm_val_t *stack_ptr)
654
        { G_stk->push()->set_stack((void *)stack_ptr); }
655
656
    /* push an integer value */
657
    static void push_int(VMG_ int32 intval)
658
        { G_stk->push()->set_int(intval); }
659
660
    /* push an enumerator value */
661
    static void push_enum(VMG_ uint32 intval)
662
        { G_stk->push()->set_enum(intval); }
663
664
    /*
665
     *   Touch the code page containing the currently executing code.  This
666
     *   should be called just after any code translates another code page
667
     *   pointer, to ensure that the current method's code page is
668
     *   re-established as the most recently used and is thus not swapped
669
     *   out.  
670
     */
671
    void touch_entry_ptr_page(VMG0_)
672
    {
673
        /* translate the entry pointer */
674
        G_code_pool->get_ptr(entry_ptr_);
675
    }
676
677
    /* 
678
     *   get the current entry pointer (the code pool offset of the start of
679
     *   the current method) 
680
     */
681
    pool_ofs_t get_entry_ptr() const { return entry_ptr_; }
682
683
    /* get the current program counter offset from the entry pointer */
684
    uint get_method_ofs() const
685
    {
686
        /* 
687
         *   Return the current program counter minus the current entry
688
         *   pointer.  If there is no current program counter, we're not
689
         *   executing in byte code, so there's no method offset.  
690
         */
691
        if (pc_ptr_ != 0)
692
            return *pc_ptr_ - entry_ptr_native_;
693
        else
694
            return 0;
695
    }
696
697
    /*
698
     *   Convert a pointer to the currently executing method into an offset
699
     *   from the start of the current method.  
700
     */
701
    ulong pc_to_method_ofs(const uchar *p)
702
    {
703
        /* 
704
         *   get the memory address of the current entry pointer, and
705
         *   subtract that from the given memory pointer to get an offset
706
         *   from the start of the current method 
707
         */
708
        return p - entry_ptr_native_;
709
    }
710
711
    /*
712
     *   Create an exception of the given imported class and throw it.  If
713
     *   the class is not exported, we'll create a basic run-time exception;
714
     *   if that's not defined, we'll create an arbitrary object.
715
     *   
716
     *   Arguments to the exception constructor are on the stack, with the
717
     *   argument count in argc.  If the imported class doesn't exist, we'll
718
     *   instead throw an intrinsic-class-general-error exception with the
719
     *   given fallback message as explanatory text.  
720
     */
721
    void throw_new_class(VMG_ vm_obj_id_t cls, uint argc,
722
                         const char *fallback_msg);
723
724
    /*
725
     *   Save/restore the interpreter context.  This is for use by the
726
     *   debugger when evaluating an expression in the course of execution,
727
     *   to ensure that everything is reset properly to the enclosing
728
     *   execution context when it's finished.  
729
     */
730
    void save_context(VMG_ vmrun_save_ctx *ctx);
731
    void restore_context(VMG_ vmrun_save_ctx *ctx);
732
733
    /*
734
     *   Get the boundaries of the given source-code statement in the given
735
     *   function.  Fills in the line pointer and *stm_start and *stm_end
736
     *   with information on the source line containing the given offset in
737
     *   the given method.  Returns true if source information is
738
     *   successfully located for the given machine code address, false if
739
     *   not.
740
     *   
741
     *   If no debugging information is available for the given code
742
     *   location, this function cannot get the source-code statement
743
     *   bounds, and returns false.  
744
     */
745
    static int get_stm_bounds(VMG_ const class CVmFuncPtr *func_ptr,
746
                              ulong pc_ofs,
747
                              class CVmDbgLinePtr *line_ptr,
748
                              ulong *stm_start, ulong *stm_end);
749
750
751
    /* -------------------------------------------------------------------- */
752
    /* 
753
     *   Set the HALT VM flag.  This allows the debugger to terminate the
754
     *   program immediately, without allowing any more byte-code
755
     *   instructions to execute.  
756
     */
757
    void set_halt_vm(int f) { halt_vm_ = f; }
758
759
    /* -------------------------------------------------------------------- */
760
    /*
761
     *   Start profiling.  This deletes any old profiling records and starts
762
     *   a new profiling session.  We'll capture profiling data until
763
     *   end_profiling() is called.  This function is only included in the
764
     *   build if the profiler is included in the build.  
765
     */
766
    void start_profiling();
767
768
    /* end profiling */
769
    void end_profiling();
770
771
    /* 
772
     *   get the profiling data - we'll invoke the callback once for each
773
     *   function in our table of data 
774
     */
775
    void get_profiling_data(VMG_
776
                            void (*cb)(void *ctx, const char *func_name,
777
                                       unsigned long time_direct,
778
                                       unsigned long time_in_children,
779
                                       unsigned long call_cnt),
780
                            void *cb_ctx);
781
782
protected:
783
    /* 
784
     *   Execute byte code starting at a given address.  This function
785
     *   retains control until the byte code function invoked returns or
786
     *   throws an unhandled exception.
787
     *   
788
     *   If an exception occurs and is not handled by the byte code, we'll
789
     *   throw VMERR_UNHANDLED_EXC with the exception object as the first
790
     *   parameter.  
791
     */
792
    void run(VMG_ const uchar *p);
793
794
    /* 
795
     *   Display a dstring via the default string display function.  This
796
     *   function pushes a string value (with the given constant pool
797
     *   offset), then does the same work as do_call() to invoke a function
798
     *   with one argument.
799
     *   
800
     *   The string is identified by its offset in the constant pool.  
801
     */
802
    const uchar *disp_dstring(VMG_ pool_ofs_t ofs, uint caller_ofs,
803
                              vm_obj_id_t self);
804
805
    /*
806
     *   Display the value at top of stack via the default string display
807
     *   function.  does the same work as do_call() to invoke the function
808
     *   with one argument, which must already be on the stack.  
809
     */
810
    const uchar *disp_string_val(VMG_ uint caller_ofs, vm_obj_id_t self);
811
812
    /*
813
     *   Convert a pointer to the currently executing method into a code
814
     *   pool offset. 
815
     */
816
    pool_ofs_t pc_to_code_ofs(VMG_ const uchar *p)
817
    {
818
        /* 
819
         *   get the offset from the start of the current method, and add
820
         *   it to the code pool offset of the start of the current method
821
         *   to yield the code pool offset of the pointer 
822
         */
823
        return (pool_ofs_t)pc_to_method_ofs(p) + entry_ptr_;
824
    }
825
826
    /*
827
     *   Set up a function header pointer for the current function 
828
     */
829
    void set_current_func_ptr(VMG_ class CVmFuncPtr *func_ptr);
830
831
    /* call a built-in function */
832
    void call_bif(VMG_ uint set_index, uint func_index, uint argc);
833
834
    /* 
835
     *   Throw an exception.  Returns a non-null program counter if a
836
     *   handler was found, false if not.  If a handler was found, byte-code
837
     *   execution can proceed; if not, the byte-code execution loop must
838
     *   pass the exception up to its caller.  
839
     */
840
    const uchar *do_throw(VMG_ const uchar *pc, vm_obj_id_t exception_obj);
841
    
842
    /* 
843
     *   Inherit a property - this is essentially the same as get_prop,
844
     *   but rather than getting the property from the given object, this
845
     *   ignores any such property defined directly by the object and goes
846
     *   directly to the inherited definition.  However, the "self" object
847
     *   is still the same as the current "self" object, since we want to
848
     *   evaluate the inherited method in the context of the original
849
     *   target "self" object.  
850
     */
851
    const uchar *inh_prop(VMG_ uint caller_ofs, vm_prop_id_t prop, uint argc);
852
853
    /*
854
     *   Look up a property value without evaluating it.  Returns true if we
855
     *   found the property, false if not.  
856
     */
857
    inline static int get_prop_no_eval(VMG_ const vm_val_t **target_obj,
858
                                       vm_prop_id_t target_prop,
859
                                       uint *argc, vm_obj_id_t *srcobj,
860
                                       vm_val_t *val,
861
                                       const vm_val_t **self,
862
                                       vm_val_t *new_self);
863
864
    /*
865
     *   Evaluate a property value.  If the value contains code, we'll
866
     *   execute the code; if it contains a self-printing string, we'll
867
     *   display the string; otherwise, we'll push the value onto the stack.
868
     *   
869
     *   'found' indicates whether or not the property is defined by the
870
     *   object.  False indicates that the property is not defined, true
871
     *   that it is defined.  If the property isn't defined, we'll simply
872
     *   discard arguments and push nil.
873
     *   
874
     *   If 'caller_ofs' is zero, we'll recursively invoke the interpreter
875
     *   loop if it's necessary to run a method; otherwise, we'll set up at
876
     *   the beginning of the method's code and let the caller proceed into
877
     *   the code.  
878
     */
879
    inline const uchar *eval_prop_val(VMG_ int found, uint caller_ofs,
880
                                      const vm_val_t *val, vm_obj_id_t self,
881
                                      vm_prop_id_t target_prop,
882
                                      const vm_val_t *orig_target_obj,
883
                                      vm_obj_id_t defining_obj,
884
                                      uint argc);
885
886
    /*
887
     *   Check a property for validity in a speculative evaluation.  If
888
     *   evaulating the property would cause any side effects, we'll throw
889
     *   an error (VMERR_BAD_SPEC_EXPR); otherwise, we won't do anything.
890
     *   Side effects include displaying a dstring or invoking a method. 
891
     */
892
    void check_prop_spec_eval(VMG_ vm_obj_id_t obj, vm_prop_id_t prop);
893
894
    /*
895
     *   Return from a function or method.  Returns the new program counter
896
     *   at which to continue execution, and restore machine registers to
897
     *   the enclosing frame.
898
     *   
899
     *   Returns a non-null program counter if execution should proceed,
900
     *   null if we're returning from the outermost stack level.  When we
901
     *   return null, the caller must return control to the host
902
     *   environment, since the host environment called the function from
903
     *   which we're returning.  
904
     */
905
    const uchar *do_return(VMG0_);
906
907
    /*
908
     *   Enter/leave a recursive call frame.  These functions are used only
909
     *   when a debugger is present.  'pc_ptr' is a pointer to the program
910
     *   counter in the calling byte-code frame.  
911
     */
912
    void enter_recursive_frame(VMG_ int argc, const uchar **pc_ptr);
913
    void leave_recursive_frame(VMG0_);
914
915
    /* 
916
     *   append a stack trace to the given string, returning a new string
917
     *   object 
918
     */
919
    vm_obj_id_t append_stack_trace(VMG_ vm_obj_id_t str_obj);
920
921
    /* push a value onto the stack */
922
    static void pushval(VMG_ const vm_val_t *val) { G_stk->push(val); }
923
924
    /* pop a value off the stack */
925
    void popval(VMG_ vm_val_t *val) { G_stk->pop(val); }
926
  
927
    /* add two values, leaving the result in *val1 */
928
    void compute_sum(VMG_ vm_val_t *val1, vm_val_t *val2);
929
930
    /* subtract one value from another, leaving the result in *val1 */
931
    void compute_diff(VMG_ vm_val_t *val1, vm_val_t *val2);
932
933
    /* compute the product, leaving the result in *val1 */
934
    void compute_product(VMG_ vm_val_t *val1, vm_val_t *val2);
935
936
    /* compute the quotient val1/val2, leaving the result in *val2 */
937
    void compute_quotient(VMG_ vm_val_t *val1, vm_val_t *val2);
938
939
    /* XOR two values and push the result */
940
    void xor_and_push(VMG_ vm_val_t *val1, vm_val_t *val2);
941
942
    /* 
943
     *   index container_val by index_val (i.e., compute
944
     *   container_val[index_val]), storing the result at *result 
945
     */
946
    void apply_index(VMG_ vm_val_t *result,
947
                     const vm_val_t *container_val,
948
                     const vm_val_t *index_val);
949
950
    /* 
951
     *   Set the element at index index_val in container_val to new_val,
952
     *   and push the new container value.  The container may be a new
953
     *   object, since some types (lists, for example) cannot have their
954
     *   values changed but instead create new objects when an indexed
955
     *   element is modified.  
956
     */
957
    void set_index(VMG_ vm_val_t *container_val,
958
                   const vm_val_t *index_val,
959
                   const vm_val_t *new_val);
960
961
    /* 
962
     *   create a new object of the given index into the metaclass
963
     *   dependency table for the load image file, using the given number
964
     *   of parameters; removes the parameters from the stack, and leaves
965
     *   the new object reference in register R0 
966
     */
967
    const uchar *new_and_store_r0(VMG_ const uchar *pc,
968
                                  uint metaclass_idx, uint argc,
969
                                  int is_transient);
970
971
    /* 
972
     *   Compare the two values at top of stack for equality; returns true
973
     *   if the values are equal, false if not.  Removes the two values from
974
     *   the stack.  
975
     */
976
    int pop2_equal(VMG0_)
977
    {
978
        /* compare the values and return the result */
979
        int ret = G_stk->get(1)->equals(vmg_ G_stk->get(0));
980
981
        /* discard the values */
982
        G_stk->discard(2);
983
984
        /* return the result */
985
        return ret;
986
    }
987
988
    /* 
989
     *   Compare the magnitude of the two values at the top of the stack.
990
     *   Returns 1 if the value at (TOS-1) is greater than the value at TOS,
991
     *   -1 if (TOS-1) is less than (TOS), and 0 if the two value are equal.
992
     *   Removes the two values from the stack.  
993
     */
994
    int pop2_compare(VMG0_)
995
    {
996
        /* compare the values and return the result */
997
        int ret = G_stk->get(1)->compare_to(vmg_ G_stk->get(0));
998
999
        /* discard the values */
1000
        G_stk->discard(2);
1001
1002
        /* return the result */
1003
        return ret;
1004
    }
1005
1006
    /* is TOS-1 < TOS ? */
1007
    int pop2_compare_lt(VMG0_)
1008
    {
1009
        /* compare the values and return the result */
1010
        int ret = G_stk->get(1)->is_lt(vmg_ G_stk->get(0));
1011
1012
        /* discard the values */
1013
        G_stk->discard(2);
1014
1015
        /* return the result */
1016
        return ret;
1017
    }
1018
1019
    /* is TOS-1 <= TOS ? */
1020
    int pop2_compare_le(VMG0_)
1021
    {
1022
        /* compare the values and return the result */
1023
        int ret = G_stk->get(1)->is_le(vmg_ G_stk->get(0));
1024
1025
        /* discard the values */
1026
        G_stk->discard(2);
1027
1028
        /* return the result */
1029
        return ret;
1030
    }
1031
1032
    /* is TOS-1 > TOS ? */
1033
    int pop2_compare_gt(VMG0_)
1034
    {
1035
        /* compare the values and return the result */
1036
        int ret = G_stk->get(1)->is_gt(vmg_ G_stk->get(0));
1037
1038
        /* discard the values */
1039
        G_stk->discard(2);
1040
1041
        /* return the result */
1042
        return ret;
1043
    }
1044
1045
    /* is TOS-1 >= TOS ? */
1046
    int pop2_compare_ge(VMG0_)
1047
    {
1048
        /* compare the values and return the result */
1049
        int ret = G_stk->get(1)->is_ge(vmg_ G_stk->get(0));
1050
1051
        /* discard the values */
1052
        G_stk->discard(2);
1053
1054
        /* return the result */
1055
        return ret;
1056
    }
1057
1058
    /* given a constant pool offset, get a pointer to the constant data */
1059
    const char *get_const_ptr(VMG_ pool_ofs_t ofs) const
1060
        { return G_const_pool->get_ptr(ofs); }
1061
1062
    /* 
1063
     *   get a signed 16-bit byte-code operand, incrementing the
1064
     *   instruction pointer past the operand 
1065
     */
1066
    int16 get_op_int16(const uchar **p)
1067
    {
1068
        int16 ret = (int16)osrp2s(*p);
1069
        *p += 2;
1070
        return ret;
1071
    }
1072
1073
    /* get an unsigned 16-bit byte-code operand */
1074
    uint16 get_op_uint16(const uchar **p)
1075
    {
1076
        uint16 ret = (uint16)osrp2(*p);
1077
        *p += 2;
1078
        return ret;
1079
    }
1080
1081
    /* get a signed 32-bit byte-code operand */
1082
    int32 get_op_int32(const uchar **p)
1083
    {
1084
        int32 ret = (int32)osrp4(*p);
1085
        *p += 4;
1086
        return ret;
1087
    }
1088
1089
    /* get an unsigned 32-bit byte-code operand */
1090
    uint32 get_op_uint32(const uchar **p)
1091
    {
1092
        uint32 ret = (uint32)t3rp4u(*p);
1093
        *p += 4;
1094
        return ret;
1095
    }
1096
1097
    /* get a signed 8-bit byte-code operand */
1098
    int get_op_int8(const uchar **p)
1099
    {
1100
        int ret = (int)(signed char)**p;
1101
        ++(*p);
1102
        return ret;
1103
    }
1104
1105
    /* get an unsigned 8-bit byte-code operand */
1106
    uint get_op_uint8(const uchar **p)
1107
    {
1108
        uint ret = (uint)**p;
1109
        ++(*p);
1110
        return ret;
1111
    }
1112
1113
    /* record a function or method entry in the profiler data */
1114
    void prof_enter(pool_ofs_t call_ofs,
1115
                    vm_obj_id_t obj, vm_prop_id_t prop);
1116
1117
    /* record a function or method exit in the profiler data */
1118
    void prof_leave();
1119
1120
    /* find or create a function entry in the master profiler table */
1121
    class CVmHashEntryProfiler
1122
        *prof_find_master_rec(const struct vm_profiler_rec *p);
1123
1124
    /* calculate an elapsed time */
1125
    void prof_calc_elapsed(vm_prof_time *diff, const vm_prof_time *a,
1126
                           const vm_prof_time *b);
1127
1128
    /* add an elapsed time value to a cumulative elapsed time value */
1129
    void prof_add_elapsed(vm_prof_time *sum, const vm_prof_time *val);
1130
1131
    /* hash table enumeration callback for dumping profiler data */
1132
    static void prof_enum_cb(void *ctx0, class CVmHashEntry *entry0);
1133
1134
    /*
1135
     *   Function header size - obtained from the image file upon loading 
1136
     */
1137
    size_t funchdr_size_;
1138
1139
    /*
1140
     *   A pointer to a global variable in the object table (CVmObjTable)
1141
     *   containing the function to invoke for the SAY and SAYVAL opcodes.
1142
     *   This can be a function pointer, a function object, or nil.  If this
1143
     *   is nil, the SAY opcode will throw an error.  
1144
     */
1145
    struct vm_globalvar_t *say_func_;
1146
1147
    /*
1148
     *   The method to invoke for the SAY and SAYVAL opcodes when a valid
1149
     *   "self" object is available.  If no method is defined, this will
1150
     *   be set to VM_INVALID_PROP.  
1151
     */
1152
    vm_prop_id_t say_method_;
1153
1154
    /*
1155
     *   R0 - data register 0.  This register stores function return
1156
     *   values. 
1157
     */
1158
    vm_val_t r0_;
1159
1160
    /* 
1161
     *   EP - entrypoint register.  This register stores the code offset
1162
     *   of the method header of the currently executing code.  
1163
     */
1164
    pool_ofs_t entry_ptr_;
1165
1166
    /* 
1167
     *   native entry pointer value - this is simply the translated value of
1168
     *   the entry pointer (i.e., G_code_pool->get_ptr(entry_ptr_)) 
1169
     */
1170
    const uchar *entry_ptr_native_;
1171
1172
    /*
1173
     *   FP - frame pointer register.  This points to the base of the
1174
     *   current stack activation frame.  Local variables and parameters
1175
     *   are reachable relative to this register. 
1176
     */
1177
    vm_val_t *frame_ptr_;
1178
1179
    /* 
1180
     *   Pointer to program counter - we use this in the debugger to create
1181
     *   pseudo-stack frames for system code when we recursively invoke the
1182
     *   VM, and for finding the current PC from intrinsic function code.  
1183
     */
1184
    const uchar **pc_ptr_;
1185
1186
    /* 
1187
     *   Flag: VM is halting.  This is used by the debugger to force the
1188
     *   program to stop executing.  This is not used except with the
1189
     *   debug-mode interpreter.  
1190
     */
1191
    int halt_vm_;
1192
1193
    /* flag: profiling is active */
1194
    int profiling_;
1195
1196
    /* in case we have a profiler, include the profiler stack */
1197
    struct vm_profiler_rec *prof_stack_;
1198
    size_t prof_stack_max_;
1199
1200
    /* next available index in the profiler stack */
1201
    size_t prof_stack_idx_;
1202
1203
    /*
1204
     *   Start of execution in the currently active function, since the last
1205
     *   call or return.  This uses the OS-specific high-precision timer
1206
     *   (defined by os_prof_curtime() in vmprof.h).
1207
     *   
1208
     *   Each time we call a function or return from a function, we measure
1209
     *   the delta from this value, then add that in to the cumulative time
1210
     *   for the function.  
1211
     */
1212
    vm_prof_time prof_start_;
1213
1214
    /* 
1215
     *   profiler master hash table - this is a table with one entry for
1216
     *   every function and method called since we began profiling, keyed by
1217
     *   method or function ID (the object.property for a method, or the
1218
     *   entrypoint code offset for a function) 
1219
     */
1220
    class CVmHashTable *prof_master_table_;
1221
};
1222
1223
#endif /* VMRUN_H */
1224