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