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);
get_funchdr_size()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) */
get_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 */
set_say_method(vm_prop_id_t prop)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 */
get_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 */
pop_int(VMG_ vm_val_t * val)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      */
pop_num(VMG_ vm_val_t * val)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 */
pop_obj(VMG_ vm_val_t * val)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 */
pop_prop(VMG_ vm_val_t * val)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 */
pop_funcptr(VMG_ vm_val_t * val)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      */
popval_2(VMG_ vm_val_t * val1,vm_val_t * val2)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      */
pop_int_2(VMG_ vm_val_t * val1,vm_val_t * val2)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      */
pop_num_2(VMG_ vm_val_t * val1,vm_val_t * val2)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      */
get_cur_argc(VMG0_)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      */
get_param(VMG_ int idx)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 */
get_param_from_frame(VMG_ vm_val_t * fp,int idx)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 */
get_param_at_level(VMG_ int idx,int 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      */
get_local(VMG_ int idx)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 */
get_local_from_frame(VMG_ vm_val_t * fp,int idx)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 */
get_local_at_level(VMG_ int idx,int 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      */
get_frame_depth(VMG0_)389     size_t get_frame_depth(VMG0_) const
390         { return G_stk->ptr_to_index(frame_ptr_); }
391 
392     /* get the current frame pointer */
get_frame_ptr()393     vm_val_t *get_frame_ptr() const { return frame_ptr_; }
394 
395     /* given a frame pointer, get the enclosing frame pointer */
get_enclosing_frame_ptr(VMG_ vm_val_t * fp)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 */
get_argc_from_frame(VMG_ vm_val_t * fp)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 */
get_argc_at_level(VMG_ int 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 */
get_self_from_frame(VMG_ vm_val_t * fp)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 */
get_self_at_level(VMG_ int 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 */
get_target_prop_from_frame(VMG_ vm_val_t * fp)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 */
get_target_prop_at_level(VMG_ int 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 */
get_defining_obj_from_frame(VMG_ vm_val_t * fp)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 */
get_defining_obj_at_level(VMG_ int 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 */
get_orig_target_obj_from_frame(VMG_ vm_val_t * fp)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 */
get_orig_target_obj_at_level(VMG_ int 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      */
get_enclosing_entry_ptr_from_frame(VMG_ vm_val_t * fp)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      */
get_return_ofs_from_frame(VMG_ vm_val_t * fp)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      */
get_return_addr_from_frame(VMG_ vm_val_t * fp)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.
551      */
get_self(VMG0_)552     vm_obj_id_t get_self(VMG0_) const
553     {
554         /* get the object value of the 'self' slot in the current frame */
555         return G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF)->val.obj;
556     }
557 
558     /* set the current 'self' object */
set_self(VMG_ const vm_val_t * val)559     void set_self(VMG_ const vm_val_t *val)
560     {
561         /* store the given value in the 'self' slot in the current frame */
562         *G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF) = *val;
563     }
564 
565     /*
566      *   Set the current execution context: the 'self' value, the target
567      *   property, the original target object, and the defining object.
568      */
set_method_ctx(VMG_ vm_obj_id_t new_self,vm_prop_id_t new_target_prop,vm_obj_id_t new_target_obj,vm_obj_id_t new_defining_obj)569     void set_method_ctx(VMG_ vm_obj_id_t new_self,
570                         vm_prop_id_t new_target_prop,
571                         vm_obj_id_t new_target_obj,
572                         vm_obj_id_t new_defining_obj)
573     {
574         /* set the "self" slot in the current stack frame */
575         G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF)
576             ->set_obj(new_self);
577 
578         /* set the target property slot in the frame */
579         G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_PROP)
580             ->set_propid(new_target_prop);
581 
582         /* set the original target object slot in the frame */
583         G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_ORIGTARG)
584             ->set_obj(new_target_obj);
585 
586         /* set the defining object slot in the frame */
587         G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_DEFOBJ)
588             ->set_obj(new_defining_obj);
589     }
590 
591     /* get the current target property value */
get_target_prop(VMG0_)592     vm_prop_id_t get_target_prop(VMG0_) const
593     {
594         return G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_PROP)->val.prop;
595     }
596 
597     /* get the current defining object */
get_defining_obj(VMG0_)598     vm_obj_id_t get_defining_obj(VMG0_) const
599     {
600         return G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_DEFOBJ)->val.obj;
601     }
602 
603     /* get the current original target object */
get_orig_target_obj(VMG0_)604     vm_obj_id_t get_orig_target_obj(VMG0_) const
605     {
606         return G_stk->get_from_frame(frame_ptr_, VMRUN_FPOFS_ORIGTARG)
607             ->val.obj;
608     }
609 
610     /* push an object ID */
push_obj(VMG_ vm_obj_id_t obj)611     static void push_obj(VMG_ vm_obj_id_t obj)
612         { G_stk->push()->set_obj(obj); }
613 
614     /* push a property ID */
push_prop(VMG_ vm_prop_id_t prop)615     static void push_prop(VMG_ vm_prop_id_t prop)
616         { G_stk->push()->set_propid(prop); }
617 
618     /* push a boolean value */
push_bool(VMG_ int flag)619     static void push_bool(VMG_ int flag)
620         { G_stk->push()->set_logical(flag); }
621 
622     /* push nil */
push_nil(VMG0_)623     static void push_nil(VMG0_)
624         { G_stk->push()->set_nil(); }
625 
626     /* push a code offset value */
push_codeofs(VMG_ pool_ofs_t ofs)627     static void push_codeofs(VMG_ pool_ofs_t ofs)
628         { G_stk->push()->set_codeofs(ofs); }
629 
630     /* push a stack pointer value */
push_stackptr(VMG_ vm_val_t * stack_ptr)631     static void push_stackptr(VMG_ vm_val_t *stack_ptr)
632         { G_stk->push()->set_stack((void *)stack_ptr); }
633 
634     /* push an integer value */
push_int(VMG_ int32 intval)635     static void push_int(VMG_ int32 intval)
636         { G_stk->push()->set_int(intval); }
637 
638     /* push an enumerator value */
push_enum(VMG_ uint32 intval)639     static void push_enum(VMG_ uint32 intval)
640         { G_stk->push()->set_enum(intval); }
641 
642     /*
643      *   Touch the code page containing the currently executing code.  This
644      *   should be called just after any code translates another code page
645      *   pointer, to ensure that the current method's code page is
646      *   re-established as the most recently used and is thus not swapped
647      *   out.
648      */
touch_entry_ptr_page(VMG0_)649     void touch_entry_ptr_page(VMG0_)
650     {
651         /* translate the entry pointer */
652         G_code_pool->get_ptr(entry_ptr_);
653     }
654 
655     /*
656      *   get the current entry pointer (the code pool offset of the start of
657      *   the current method)
658      */
get_entry_ptr()659     pool_ofs_t get_entry_ptr() const { return entry_ptr_; }
660 
661     /* get the current program counter offset from the entry pointer */
get_method_ofs()662     uint get_method_ofs() const
663     {
664         /*
665          *   Return the current program counter minus the current entry
666          *   pointer.  If there is no current program counter, we're not
667          *   executing in byte code, so there's no method offset.
668          */
669         if (pc_ptr_ != 0)
670             return *pc_ptr_ - entry_ptr_native_;
671         else
672             return 0;
673     }
674 
675     /*
676      *   Convert a pointer to the currently executing method into an offset
677      *   from the start of the current method.
678      */
pc_to_method_ofs(const uchar * p)679     ulong pc_to_method_ofs(const uchar *p)
680     {
681         /*
682          *   get the memory address of the current entry pointer, and
683          *   subtract that from the given memory pointer to get an offset
684          *   from the start of the current method
685          */
686         return p - entry_ptr_native_;
687     }
688 
689     /*
690      *   Create an exception of the given imported class and throw it.  If
691      *   the class is not exported, we'll create a basic run-time exception;
692      *   if that's not defined, we'll create an arbitrary object.
693      *
694      *   Arguments to the exception constructor are on the stack, with the
695      *   argument count in argc.  If the imported class doesn't exist, we'll
696      *   instead throw an intrinsic-class-general-error exception with the
697      *   given fallback message as explanatory text.
698      */
699     void throw_new_class(VMG_ vm_obj_id_t cls, uint argc,
700                          const char *fallback_msg);
701 
702     /*
703      *   Save/restore the interpreter context.  This is for use by the
704      *   debugger when evaluating an expression in the course of execution,
705      *   to ensure that everything is reset properly to the enclosing
706      *   execution context when it's finished.
707      */
708     void save_context(VMG_ vmrun_save_ctx *ctx);
709     void restore_context(VMG_ vmrun_save_ctx *ctx);
710 
711     /*
712      *   Get the boundaries of the given source-code statement in the given
713      *   function.  Fills in the line pointer and *stm_start and *stm_end
714      *   with information on the source line containing the given offset in
715      *   the given method.  Returns true if source information is
716      *   successfully located for the given machine code address, false if
717      *   not.
718      *
719      *   If no debugging information is available for the given code
720      *   location, this function cannot get the source-code statement
721      *   bounds, and returns false.
722      */
723     static int get_stm_bounds(VMG_ const class CVmFuncPtr *func_ptr,
724                               ulong pc_ofs,
725                               class CVmDbgLinePtr *line_ptr,
726                               ulong *stm_start, ulong *stm_end);
727 
728 
729     /* -------------------------------------------------------------------- */
730     /*
731      *   Set the HALT VM flag.  This allows the debugger to terminate the
732      *   program immediately, without allowing any more byte-code
733      *   instructions to execute.
734      */
set_halt_vm(int f)735     void set_halt_vm(int f) { halt_vm_ = f; }
736 
737     /* -------------------------------------------------------------------- */
738     /*
739      *   Start profiling.  This deletes any old profiling records and starts
740      *   a new profiling session.  We'll capture profiling data until
741      *   end_profiling() is called.  This function is only included in the
742      *   build if the profiler is included in the build.
743      */
744     void start_profiling();
745 
746     /* end profiling */
747     void end_profiling();
748 
749     /*
750      *   get the profiling data - we'll invoke the callback once for each
751      *   function in our table of data
752      */
753     void get_profiling_data(VMG_
754                             void (*cb)(void *ctx, const char *func_name,
755                                        unsigned long time_direct,
756                                        unsigned long time_in_children,
757                                        unsigned long call_cnt),
758                             void *cb_ctx);
759 
760 protected:
761     /*
762      *   Execute byte code starting at a given address.  This function
763      *   retains control until the byte code function invoked returns or
764      *   throws an unhandled exception.
765      *
766      *   If an exception occurs and is not handled by the byte code, we'll
767      *   throw VMERR_UNHANDLED_EXC with the exception object as the first
768      *   parameter.
769      */
770     void run(VMG_ const uchar *p);
771 
772     /*
773      *   Display a dstring via the default string display function.  This
774      *   function pushes a string value (with the given constant pool
775      *   offset), then does the same work as do_call() to invoke a function
776      *   with one argument.
777      *
778      *   The string is identified by its offset in the constant pool.
779      */
780     const uchar *disp_dstring(VMG_ pool_ofs_t ofs, uint caller_ofs,
781                               vm_obj_id_t self);
782 
783     /*
784      *   Display the value at top of stack via the default string display
785      *   function.  does the same work as do_call() to invoke the function
786      *   with one argument, which must already be on the stack.
787      */
788     const uchar *disp_string_val(VMG_ uint caller_ofs, vm_obj_id_t self);
789 
790     /*
791      *   Convert a pointer to the currently executing method into a code
792      *   pool offset.
793      */
pc_to_code_ofs(VMG_ const uchar * p)794     pool_ofs_t pc_to_code_ofs(VMG_ const uchar *p)
795     {
796         /*
797          *   get the offset from the start of the current method, and add
798          *   it to the code pool offset of the start of the current method
799          *   to yield the code pool offset of the pointer
800          */
801         return (pool_ofs_t)pc_to_method_ofs(p) + entry_ptr_;
802     }
803 
804     /*
805      *   Set up a function header pointer for the current function
806      */
807     void set_current_func_ptr(VMG_ class CVmFuncPtr *func_ptr);
808 
809     /* call a built-in function */
810     void call_bif(VMG_ uint set_index, uint func_index, uint argc);
811 
812     /*
813      *   Throw an exception.  Returns a non-null program counter if a
814      *   handler was found, false if not.  If a handler was found, byte-code
815      *   execution can proceed; if not, the byte-code execution loop must
816      *   pass the exception up to its caller.
817      */
818     const uchar *do_throw(VMG_ const uchar *pc, vm_obj_id_t exception_obj);
819 
820     /*
821      *   Inherit a property - this is essentially the same as get_prop,
822      *   but rather than getting the property from the given object, this
823      *   ignores any such property defined directly by the object and goes
824      *   directly to the inherited definition.  However, the "self" object
825      *   is still the same as the current "self" object, since we want to
826      *   evaluate the inherited method in the context of the original
827      *   target "self" object.
828      */
829     const uchar *inh_prop(VMG_ uint caller_ofs, vm_prop_id_t prop, uint argc);
830 
831     /*
832      *   Look up a property value without evaluating it.  Returns true if we
833      *   found the property, false if not.
834      */
835     inline static int get_prop_no_eval(VMG_ const vm_val_t **target_obj,
836                                        vm_prop_id_t target_prop,
837                                        uint *argc, vm_obj_id_t *srcobj,
838                                        vm_val_t *val,
839                                        const vm_val_t **self,
840                                        vm_val_t *new_self);
841 
842     /*
843      *   Evaluate a property value.  If the value contains code, we'll
844      *   execute the code; if it contains a self-printing string, we'll
845      *   display the string; otherwise, we'll push the value onto the stack.
846      *
847      *   'found' indicates whether or not the property is defined by the
848      *   object.  False indicates that the property is not defined, true
849      *   that it is defined.  If the property isn't defined, we'll simply
850      *   discard arguments and push nil.
851      *
852      *   If 'caller_ofs' is zero, we'll recursively invoke the interpreter
853      *   loop if it's necessary to run a method; otherwise, we'll set up at
854      *   the beginning of the method's code and let the caller proceed into
855      *   the code.
856      */
857     inline const uchar *eval_prop_val(VMG_ int found, uint caller_ofs,
858                                       const vm_val_t *val, vm_obj_id_t self,
859                                       vm_prop_id_t target_prop,
860                                       const vm_val_t *orig_target_obj,
861                                       vm_obj_id_t defining_obj,
862                                       uint argc);
863 
864     /*
865      *   Check a property for validity in a speculative evaluation.  If
866      *   evaulating the property would cause any side effects, we'll throw
867      *   an error (VMERR_BAD_SPEC_EXPR); otherwise, we won't do anything.
868      *   Side effects include displaying a dstring or invoking a method.
869      */
870     void check_prop_spec_eval(VMG_ vm_obj_id_t obj, vm_prop_id_t prop);
871 
872     /*
873      *   Return from a function or method.  Returns the new program counter
874      *   at which to continue execution, and restore machine registers to
875      *   the enclosing frame.
876      *
877      *   Returns a non-null program counter if execution should proceed,
878      *   null if we're returning from the outermost stack level.  When we
879      *   return null, the caller must return control to the host
880      *   environment, since the host environment called the function from
881      *   which we're returning.
882      */
883     const uchar *do_return(VMG0_);
884 
885     /*
886      *   Enter/leave a recursive call frame.  These functions are used only
887      *   when a debugger is present.  'pc_ptr' is a pointer to the program
888      *   counter in the calling byte-code frame.
889      */
890     void enter_recursive_frame(VMG_ int argc, const uchar **pc_ptr);
891     void leave_recursive_frame(VMG0_);
892 
893     /*
894      *   append a stack trace to the given string, returning a new string
895      *   object
896      */
897     vm_obj_id_t append_stack_trace(VMG_ vm_obj_id_t str_obj);
898 
899     /* push a value onto the stack */
pushval(VMG_ const vm_val_t * val)900     static void pushval(VMG_ const vm_val_t *val) { G_stk->push(val); }
901 
902     /* pop a value off the stack */
popval(VMG_ vm_val_t * val)903     void popval(VMG_ vm_val_t *val) { G_stk->pop(val); }
904 
905     /* add two values, leaving the result in *val1 */
906     void compute_sum(VMG_ vm_val_t *val1, vm_val_t *val2);
907 
908     /* subtract one value from another, leaving the result in *val1 */
909     void compute_diff(VMG_ vm_val_t *val1, vm_val_t *val2);
910 
911     /* compute the product, leaving the result in *val1 */
912     void compute_product(VMG_ vm_val_t *val1, vm_val_t *val2);
913 
914     /* compute the quotient val1/val2, leaving the result in *val2 */
915     void compute_quotient(VMG_ vm_val_t *val1, vm_val_t *val2);
916 
917     /* XOR two values and push the result */
918     void xor_and_push(VMG_ vm_val_t *val1, vm_val_t *val2);
919 
920     /*
921      *   index container_val by index_val (i.e., compute
922      *   container_val[index_val]), storing the result at *result
923      */
924     void apply_index(VMG_ vm_val_t *result,
925                      const vm_val_t *container_val,
926                      const vm_val_t *index_val);
927 
928     /*
929      *   Set the element at index index_val in container_val to new_val,
930      *   and push the new container value.  The container may be a new
931      *   object, since some types (lists, for example) cannot have their
932      *   values changed but instead create new objects when an indexed
933      *   element is modified.
934      */
935     void set_index(VMG_ vm_val_t *container_val,
936                    const vm_val_t *index_val,
937                    const vm_val_t *new_val);
938 
939     /*
940      *   create a new object of the given index into the metaclass
941      *   dependency table for the load image file, using the given number
942      *   of parameters; removes the parameters from the stack, and leaves
943      *   the new object reference in register R0
944      */
945     const uchar *new_and_store_r0(VMG_ const uchar *pc,
946                                   uint metaclass_idx, uint argc,
947                                   int is_transient);
948 
949     /*
950      *   Compare the two values at top of stack for equality; returns true
951      *   if the values are equal, false if not.  Removes the two values from
952      *   the stack.
953      */
pop2_equal(VMG0_)954     int pop2_equal(VMG0_)
955     {
956         /* compare the values and return the result */
957         int ret = G_stk->get(1)->equals(vmg_ G_stk->get(0));
958 
959         /* discard the values */
960         G_stk->discard(2);
961 
962         /* return the result */
963         return ret;
964     }
965 
966     /*
967      *   Compare the magnitude of the two values at the top of the stack.
968      *   Returns 1 if the value at (TOS-1) is greater than the value at TOS,
969      *   -1 if (TOS-1) is less than (TOS), and 0 if the two value are equal.
970      *   Removes the two values from the stack.
971      */
pop2_compare(VMG0_)972     int pop2_compare(VMG0_)
973     {
974         /* compare the values and return the result */
975         int ret = G_stk->get(1)->compare_to(vmg_ G_stk->get(0));
976 
977         /* discard the values */
978         G_stk->discard(2);
979 
980         /* return the result */
981         return ret;
982     }
983 
984     /* is TOS-1 < TOS ? */
pop2_compare_lt(VMG0_)985     int pop2_compare_lt(VMG0_)
986     {
987         /* compare the values and return the result */
988         int ret = G_stk->get(1)->is_lt(vmg_ G_stk->get(0));
989 
990         /* discard the values */
991         G_stk->discard(2);
992 
993         /* return the result */
994         return ret;
995     }
996 
997     /* is TOS-1 <= TOS ? */
pop2_compare_le(VMG0_)998     int pop2_compare_le(VMG0_)
999     {
1000         /* compare the values and return the result */
1001         int ret = G_stk->get(1)->is_le(vmg_ G_stk->get(0));
1002 
1003         /* discard the values */
1004         G_stk->discard(2);
1005 
1006         /* return the result */
1007         return ret;
1008     }
1009 
1010     /* is TOS-1 > TOS ? */
pop2_compare_gt(VMG0_)1011     int pop2_compare_gt(VMG0_)
1012     {
1013         /* compare the values and return the result */
1014         int ret = G_stk->get(1)->is_gt(vmg_ G_stk->get(0));
1015 
1016         /* discard the values */
1017         G_stk->discard(2);
1018 
1019         /* return the result */
1020         return ret;
1021     }
1022 
1023     /* is TOS-1 >= TOS ? */
pop2_compare_ge(VMG0_)1024     int pop2_compare_ge(VMG0_)
1025     {
1026         /* compare the values and return the result */
1027         int ret = G_stk->get(1)->is_ge(vmg_ G_stk->get(0));
1028 
1029         /* discard the values */
1030         G_stk->discard(2);
1031 
1032         /* return the result */
1033         return ret;
1034     }
1035 
1036     /* given a constant pool offset, get a pointer to the constant data */
get_const_ptr(VMG_ pool_ofs_t ofs)1037     const char *get_const_ptr(VMG_ pool_ofs_t ofs) const
1038         { return G_const_pool->get_ptr(ofs); }
1039 
1040     /*
1041      *   get a signed 16-bit byte-code operand, incrementing the
1042      *   instruction pointer past the operand
1043      */
get_op_int16(const uchar ** p)1044     int16 get_op_int16(const uchar **p)
1045     {
1046         int16 ret = (int16)osrp2s(*p);
1047         *p += 2;
1048         return ret;
1049     }
1050 
1051     /* get an unsigned 16-bit byte-code operand */
get_op_uint16(const uchar ** p)1052     uint16 get_op_uint16(const uchar **p)
1053     {
1054         uint16 ret = (uint16)osrp2(*p);
1055         *p += 2;
1056         return ret;
1057     }
1058 
1059     /* get a signed 32-bit byte-code operand */
get_op_int32(const uchar ** p)1060     int32 get_op_int32(const uchar **p)
1061     {
1062         int32 ret = (int32)osrp4(*p);
1063         *p += 4;
1064         return ret;
1065     }
1066 
1067     /* get an unsigned 32-bit byte-code operand */
get_op_uint32(const uchar ** p)1068     uint32 get_op_uint32(const uchar **p)
1069     {
1070         uint32 ret = (uint32)osrp4(*p);
1071         *p += 4;
1072         return ret;
1073     }
1074 
1075     /* get a signed 8-bit byte-code operand */
get_op_int8(const uchar ** p)1076     int get_op_int8(const uchar **p)
1077     {
1078         int ret = (int)(signed char)**p;
1079         ++(*p);
1080         return ret;
1081     }
1082 
1083     /* get an unsigned 8-bit byte-code operand */
get_op_uint8(const uchar ** p)1084     uint get_op_uint8(const uchar **p)
1085     {
1086         uint ret = (uint)**p;
1087         ++(*p);
1088         return ret;
1089     }
1090 
1091     /* record a function or method entry in the profiler data */
1092     void prof_enter(pool_ofs_t call_ofs,
1093                     vm_obj_id_t obj, vm_prop_id_t prop);
1094 
1095     /* record a function or method exit in the profiler data */
1096     void prof_leave();
1097 
1098     /* find or create a function entry in the master profiler table */
1099     class CVmHashEntryProfiler
1100         *prof_find_master_rec(const struct vm_profiler_rec *p);
1101 
1102     /* calculate an elapsed time */
1103     void prof_calc_elapsed(vm_prof_time *diff, const vm_prof_time *a,
1104                            const vm_prof_time *b);
1105 
1106     /* add an elapsed time value to a cumulative elapsed time value */
1107     void prof_add_elapsed(vm_prof_time *sum, const vm_prof_time *val);
1108 
1109     /* hash table enumeration callback for dumping profiler data */
1110     static void prof_enum_cb(void *ctx0, class CVmHashEntry *entry0);
1111 
1112     /*
1113      *   Function header size - obtained from the image file upon loading
1114      */
1115     size_t funchdr_size_;
1116 
1117     /*
1118      *   A pointer to a global variable in the object table (CVmObjTable)
1119      *   containing the function to invoke for the SAY and SAYVAL opcodes.
1120      *   This can be a function pointer, a function object, or nil.  If this
1121      *   is nil, the SAY opcode will throw an error.
1122      */
1123     struct vm_globalvar_t *say_func_;
1124 
1125     /*
1126      *   The method to invoke for the SAY and SAYVAL opcodes when a valid
1127      *   "self" object is available.  If no method is defined, this will
1128      *   be set to VM_INVALID_PROP.
1129      */
1130     vm_prop_id_t say_method_;
1131 
1132     /*
1133      *   R0 - data register 0.  This register stores function return
1134      *   values.
1135      */
1136     vm_val_t r0_;
1137 
1138     /*
1139      *   EP - entrypoint register.  This register stores the code offset
1140      *   of the method header of the currently executing code.
1141      */
1142     pool_ofs_t entry_ptr_;
1143 
1144     /*
1145      *   native entry pointer value - this is simply the translated value of
1146      *   the entry pointer (i.e., G_code_pool->get_ptr(entry_ptr_))
1147      */
1148     const uchar *entry_ptr_native_;
1149 
1150     /*
1151      *   FP - frame pointer register.  This points to the base of the
1152      *   current stack activation frame.  Local variables and parameters
1153      *   are reachable relative to this register.
1154      */
1155     vm_val_t *frame_ptr_;
1156 
1157     /*
1158      *   Pointer to program counter - we use this in the debugger to create
1159      *   pseudo-stack frames for system code when we recursively invoke the
1160      *   VM, and for finding the current PC from intrinsic function code.
1161      */
1162     const uchar **pc_ptr_;
1163 
1164     /*
1165      *   Flag: VM is halting.  This is used by the debugger to force the
1166      *   program to stop executing.  This is not used except with the
1167      *   debug-mode interpreter.
1168      */
1169     int halt_vm_;
1170 
1171     /* flag: profiling is active */
1172     int profiling_;
1173 
1174     /* in case we have a profiler, include the profiler stack */
1175     struct vm_profiler_rec *prof_stack_;
1176     size_t prof_stack_max_;
1177 
1178     /* next available index in the profiler stack */
1179     size_t prof_stack_idx_;
1180 
1181     /*
1182      *   Start of execution in the currently active function, since the last
1183      *   call or return.  This uses the OS-specific high-precision timer
1184      *   (defined by os_prof_curtime() in vmprof.h).
1185      *
1186      *   Each time we call a function or return from a function, we measure
1187      *   the delta from this value, then add that in to the cumulative time
1188      *   for the function.
1189      */
1190     vm_prof_time prof_start_;
1191 
1192     /*
1193      *   profiler master hash table - this is a table with one entry for
1194      *   every function and method called since we began profiling, keyed by
1195      *   method or function ID (the object.property for a method, or the
1196      *   entrypoint code offset for a function)
1197      */
1198     class CVmHashTable *prof_master_table_;
1199 };
1200 
1201 #endif /* VMRUN_H */
1202 
1203