| | 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 | |