| | 1 | /* $Header: d:/cvsroot/tads/tads3/vmfunc.h,v 1.3 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 | vmfunc.h - T3 VM Function Header definitions |
| | 12 | Function |
| | 13 | Defines the layout of a function header, which is the block of data |
| | 14 | immediately preceding the first byte code instruction in every function. |
| | 15 | The function header is stored in binary portable format, so that image |
| | 16 | files can be loaded directly into memory and executed without translation. |
| | 17 | Notes |
| | 18 | |
| | 19 | Modified |
| | 20 | 11/20/98 MJRoberts - Creation |
| | 21 | */ |
| | 22 | |
| | 23 | #ifndef VMFUNC_H |
| | 24 | #define VMFUNC_H |
| | 25 | |
| | 26 | #include "t3std.h" |
| | 27 | #include "vmtype.h" |
| | 28 | #include "vmglob.h" |
| | 29 | |
| | 30 | |
| | 31 | /* ------------------------------------------------------------------------ */ |
| | 32 | /* |
| | 33 | * FUNCTION HEADER |
| | 34 | */ |
| | 35 | |
| | 36 | /* |
| | 37 | * The function header is a packed array of bytes, with each element |
| | 38 | * stored in a canonical binary format for binary portability. No |
| | 39 | * padding is present except where otherwise specified. The fields in |
| | 40 | * the function header, starting at offset zero, are: |
| | 41 | * |
| | 42 | * UBYTE argc - the number of parameters the function expects to |
| | 43 | * receive. If the high-order bit is set (i.e., (argc & 0x80) != 0), |
| | 44 | * then the function takes a variable number of parameters, with a |
| | 45 | * minimum of (argc & 0x7f) and no maximum. If the high-order bit is |
| | 46 | * clear, argc gives the exact number of parameters required. |
| | 47 | * |
| | 48 | * UBYTE reserved - reserved for future use. This value should always |
| | 49 | * be set to zero in the current version. |
| | 50 | * |
| | 51 | * UINT2 locals - the number of local variables the function uses. This |
| | 52 | * does not count the implicit argument counter local variable, which is |
| | 53 | * always pushed by the VM after setting up a new activation frame. |
| | 54 | * |
| | 55 | * UINT2 total_stack - the total number of stack slots required by the |
| | 56 | * function, including local variables, intermediate results of |
| | 57 | * calculations, and actual parameters to functions invoked by this code. |
| | 58 | * |
| | 59 | * UINT2 exception_table_ofs - the byte offset from the start of this |
| | 60 | * method header of the function's exception table. This value is zero |
| | 61 | * if the function has no exception table. |
| | 62 | * |
| | 63 | * UINT2 debug_ofs - the byte offset from the start of this method |
| | 64 | * header of the function's debugger records. This value is zero if the |
| | 65 | * function has no debugger records. |
| | 66 | */ |
| | 67 | |
| | 68 | /* minimum function header size supported by this version of the VM */ |
| | 69 | const size_t VMFUNC_HDR_MIN_SIZE = 10; |
| | 70 | |
| | 71 | class CVmFuncPtr |
| | 72 | { |
| | 73 | public: |
| | 74 | /* initialize with a pointer to the start of the function */ |
| | 75 | void set(const uchar *p) { p_ = p; } |
| | 76 | |
| | 77 | /* copy from another function pointer */ |
| | 78 | void copy_from(const CVmFuncPtr *fp) { p_ = fp->p_; } |
| | 79 | |
| | 80 | /* get the minimum argument count */ |
| | 81 | int get_min_argc() const |
| | 82 | { |
| | 83 | /* get the argument count, but mask out the varargs bit */ |
| | 84 | return (int)(get_argc() & 0x7f); |
| | 85 | } |
| | 86 | |
| | 87 | /* is this a varargs function? */ |
| | 88 | int is_varargs() const { return ((get_argc() & 0x80) != 0); } |
| | 89 | |
| | 90 | /* |
| | 91 | * check an actual parameter count for correctness; returns true if |
| | 92 | * the count is correct for this function, false if not |
| | 93 | */ |
| | 94 | int argc_ok(int argc) const |
| | 95 | { |
| | 96 | /* check for an exact match */ |
| | 97 | if (argc == get_min_argc()) |
| | 98 | { |
| | 99 | /* we have an exact match, so we're fine */ |
| | 100 | return TRUE; |
| | 101 | } |
| | 102 | else if (is_varargs() && argc > get_min_argc()) |
| | 103 | { |
| | 104 | /* |
| | 105 | * we have variable arguments, and we have at least the |
| | 106 | * minimum, so we're okay |
| | 107 | */ |
| | 108 | return TRUE; |
| | 109 | } |
| | 110 | else |
| | 111 | { |
| | 112 | /* |
| | 113 | * either we don't have variable arguments, or we don't have |
| | 114 | * the minimum varargs count - in either case, we have an |
| | 115 | * argument count mistmatch |
| | 116 | */ |
| | 117 | return FALSE; |
| | 118 | } |
| | 119 | } |
| | 120 | |
| | 121 | /* |
| | 122 | * Get the internal argument count. This has the high bit set for a |
| | 123 | * varargs function, and the low-order seven bits give the nominal |
| | 124 | * argument count. If this is a varargs function, the nominal |
| | 125 | * argument count is the minimum count: any actual number of arguments |
| | 126 | * at least the nominal count is valid. If this is not a varargs |
| | 127 | * function, the nominal count is the exact count: the actual number |
| | 128 | * of arguments must match the nominal count. |
| | 129 | */ |
| | 130 | uchar get_argc() const { return *(p_ + 0); } |
| | 131 | |
| | 132 | /* get the number of locals */ |
| | 133 | uint get_local_cnt() const { return osrp2(p_ + 2); } |
| | 134 | |
| | 135 | /* get the total stack slots required by the function */ |
| | 136 | uint get_stack_depth() const { return osrp2(p_ + 4); } |
| | 137 | |
| | 138 | /* get the exception table offset */ |
| | 139 | uint get_exc_ofs() const { return osrp2(p_ + 6); } |
| | 140 | |
| | 141 | /* get the debugger records table offset */ |
| | 142 | uint get_debug_ofs() const { return osrp2(p_ + 8); } |
| | 143 | |
| | 144 | /* |
| | 145 | * Set up an exception table pointer for this function. Returns |
| | 146 | * true if successful, false if there's no exception table. |
| | 147 | */ |
| | 148 | int set_exc_ptr(class CVmExcTablePtr *exc_ptr) const; |
| | 149 | |
| | 150 | /* |
| | 151 | * Set up a debug table pointer for this function. Returns true if |
| | 152 | * successful, false if there's no debug table. |
| | 153 | */ |
| | 154 | int set_dbg_ptr(class CVmDbgTablePtr *dbg_ptr) const; |
| | 155 | |
| | 156 | private: |
| | 157 | /* pointer to the method header */ |
| | 158 | const uchar *p_; |
| | 159 | }; |
| | 160 | |
| | 161 | /* ------------------------------------------------------------------------ */ |
| | 162 | /* |
| | 163 | * EXCEPTION TABLE |
| | 164 | */ |
| | 165 | |
| | 166 | /* |
| | 167 | * The exception table starts with a count indicating how many elements |
| | 168 | * are in the table, followed by the table entries. Each entry in the |
| | 169 | * table specifies the handler for one protected range of code. We |
| | 170 | * search the table in forward order, so the handlers must be stored in |
| | 171 | * order of precedence. |
| | 172 | * |
| | 173 | * Each table entry contains: |
| | 174 | * |
| | 175 | * UINT2 start_ofs - the starting offset (as a byte offset from the |
| | 176 | * start of the function) of the protected range for this handler. |
| | 177 | * |
| | 178 | * UINT2 end_ofs - the ending offset (as a byte offset from the start of |
| | 179 | * the function) of the protected range for this handler. The range is |
| | 180 | * inclusive of this offset, so a one-byte range would have start_ofs |
| | 181 | * and end_ofs set to the same value. |
| | 182 | * |
| | 183 | * UINT4 exception_class - the object ID of the class of exception |
| | 184 | * handled by this handler. |
| | 185 | * |
| | 186 | * UINT2 handler_ofs - the handler offset (as a byte offset from the |
| | 187 | * start of the function). This is the offset of the first instruction |
| | 188 | * of the code to be invoked to handle this exception. |
| | 189 | */ |
| | 190 | |
| | 191 | /* |
| | 192 | * Exception Table Entry Pointer |
| | 193 | */ |
| | 194 | class CVmExcEntryPtr |
| | 195 | { |
| | 196 | /* let CVmExcTablePtr initialize us */ |
| | 197 | friend class CVmExcTablePtr; |
| | 198 | |
| | 199 | public: |
| | 200 | /* get the starting/ending offset from this entry */ |
| | 201 | uint get_start_ofs() const { return osrp2(p_); } |
| | 202 | uint get_end_ofs() const { return osrp2(p_ + 2); } |
| | 203 | |
| | 204 | /* get the exception class's object ID */ |
| | 205 | vm_obj_id_t get_exception() const { return (vm_obj_id_t)t3rp4u(p_ + 4); } |
| | 206 | |
| | 207 | /* get the handler byte code offset */ |
| | 208 | uint get_handler_ofs() const { return osrp2(p_ + 8); } |
| | 209 | |
| | 210 | /* increment the pointer to the next entry in the table */ |
| | 211 | void inc(VMG0_) { p_ += G_exc_entry_size; } |
| | 212 | |
| | 213 | private: |
| | 214 | /* initialize with a pointer to the first byte of our entry */ |
| | 215 | void set(const uchar *p) { p_ = p; } |
| | 216 | |
| | 217 | /* pointer to the first byte of our entry */ |
| | 218 | const uchar *p_; |
| | 219 | }; |
| | 220 | |
| | 221 | /* |
| | 222 | * Exception Table Pointer |
| | 223 | */ |
| | 224 | class CVmExcTablePtr |
| | 225 | { |
| | 226 | public: |
| | 227 | /* |
| | 228 | * Initialize with a pointer to the start of the function -- we'll |
| | 229 | * read the exception table offset out of the method header. |
| | 230 | * Returns true if the function has an exception table, false if |
| | 231 | * there is no exception table defined in the function. |
| | 232 | */ |
| | 233 | int set(const uchar *p) |
| | 234 | { |
| | 235 | CVmFuncPtr func; |
| | 236 | |
| | 237 | /* set up the function pointer */ |
| | 238 | func.set(p); |
| | 239 | |
| | 240 | /* if there's no exception table, simply return this information */ |
| | 241 | if (func.get_exc_ofs() == 0) |
| | 242 | return FALSE; |
| | 243 | |
| | 244 | /* set up our pointer by reading from the header */ |
| | 245 | p_ = p + func.get_exc_ofs(); |
| | 246 | |
| | 247 | /* indicate that there is a valid exception table */ |
| | 248 | return TRUE; |
| | 249 | } |
| | 250 | |
| | 251 | /* get the number of entries in the table */ |
| | 252 | size_t get_count() const { return osrp2(p_); } |
| | 253 | |
| | 254 | /* initialize a CVmExcEntryPtr with the entry at the given index */ |
| | 255 | void set_entry_ptr(VMG_ CVmExcEntryPtr *entry, size_t idx) const |
| | 256 | { entry->set(p_ + 2 + (idx * G_exc_entry_size)); } |
| | 257 | |
| | 258 | private: |
| | 259 | /* pointer to the first byte of the exception table */ |
| | 260 | const uchar *p_; |
| | 261 | }; |
| | 262 | |
| | 263 | /* ------------------------------------------------------------------------ */ |
| | 264 | /* |
| | 265 | * DEBUGGER RECORDS TABLE |
| | 266 | */ |
| | 267 | |
| | 268 | /* |
| | 269 | * The debugger table consists of three sections. The first section is |
| | 270 | * the header, with general information on the method or function. The |
| | 271 | * second section is the line records, which give the code boundaries of |
| | 272 | * the source lines. The third section is the frame records, giving the |
| | 273 | * local symbol tables. |
| | 274 | */ |
| | 275 | |
| | 276 | /* |
| | 277 | * Debugger source line entry pointer |
| | 278 | */ |
| | 279 | class CVmDbgLinePtr |
| | 280 | { |
| | 281 | /* let CVmDbgTablePtr initialize us */ |
| | 282 | friend class CVmDbgTablePtr; |
| | 283 | |
| | 284 | public: |
| | 285 | /* |
| | 286 | * get the byte-code offset (from method start) of the start of the |
| | 287 | * byte-code for this line |
| | 288 | */ |
| | 289 | uint get_start_ofs() const { return osrp2(p_); } |
| | 290 | |
| | 291 | /* get the source file ID (an index into the global source file list) */ |
| | 292 | uint get_source_id() const { return osrp2(p_ + 2); } |
| | 293 | |
| | 294 | /* get the line number in the source file */ |
| | 295 | ulong get_source_line() const { return t3rp4u(p_ + 4); } |
| | 296 | |
| | 297 | /* get the frame ID (a 1-based index into our local frame table) */ |
| | 298 | uint get_frame_id() const { return osrp2(p_ + 8); } |
| | 299 | |
| | 300 | /* increment the pointer to the next entry in the table */ |
| | 301 | void inc(VMG0_) { p_ += G_line_entry_size; } |
| | 302 | |
| | 303 | /* set from another line pointer */ |
| | 304 | void copy_from(const CVmDbgLinePtr *p) { p_ = p->p_; } |
| | 305 | |
| | 306 | private: |
| | 307 | /* initialize with a pointer to the first byte of our entry */ |
| | 308 | void set(const uchar *p) { p_ = p; } |
| | 309 | |
| | 310 | /* pointer to the first byte of our entry */ |
| | 311 | const uchar *p_; |
| | 312 | }; |
| | 313 | |
| | 314 | /* |
| | 315 | * Debugger frame symbol entry pointer |
| | 316 | */ |
| | 317 | class CVmDbgFrameSymPtr |
| | 318 | { |
| | 319 | /* let CVmDbgFramePtr initialize us */ |
| | 320 | friend class CVmDbgFramePtr; |
| | 321 | |
| | 322 | public: |
| | 323 | /* get the local/parameter number */ |
| | 324 | uint get_var_num() const { return osrp2(p_); } |
| | 325 | |
| | 326 | /* get the context array index (for context locals) */ |
| | 327 | vm_prop_id_t get_ctx_arr_idx() const |
| | 328 | { return (vm_prop_id_t)osrp2(p_ + 4); } |
| | 329 | |
| | 330 | /* determine if I'm a local or a parameter */ |
| | 331 | int is_local() const { return (get_flags() & 1) == 0; } |
| | 332 | int is_param() const |
| | 333 | { return (((get_flags() & 1) != 0) && !is_ctx_local()); } |
| | 334 | |
| | 335 | /* determine if I'm a context local */ |
| | 336 | int is_ctx_local() const { return (get_flags() & 2) != 0; } |
| | 337 | |
| | 338 | /* get the length of my name string */ |
| | 339 | uint get_sym_len(VMG0_) const |
| | 340 | { return osrp2(p_ + G_dbg_lclsym_hdr_size); } |
| | 341 | |
| | 342 | /* get a pointer to my name string - this is not null-terminated */ |
| | 343 | const char *get_sym(VMG0_) const |
| | 344 | { return (char *)(p_ + G_dbg_lclsym_hdr_size + 2); } |
| | 345 | |
| | 346 | /* increment this pointer to point to the next symbol in the frame */ |
| | 347 | void inc(VMG0_) |
| | 348 | { p_ += G_dbg_lclsym_hdr_size + 2 + get_sym_len(vmg0_); } |
| | 349 | |
| | 350 | private: |
| | 351 | /* get my flags value */ |
| | 352 | uint get_flags() const { return osrp2(p_ + 2); } |
| | 353 | |
| | 354 | /* initialize with a pointer to the first byte of our entry */ |
| | 355 | void set(const uchar *p) { p_ = p; } |
| | 356 | |
| | 357 | /* pointer to the first byte of our entry */ |
| | 358 | const uchar *p_; |
| | 359 | }; |
| | 360 | |
| | 361 | /* |
| | 362 | * Debugger frame entry pointer |
| | 363 | */ |
| | 364 | class CVmDbgFramePtr |
| | 365 | { |
| | 366 | /* let CVmDbgTablePtr initialize us */ |
| | 367 | friend class CVmDbgTablePtr; |
| | 368 | |
| | 369 | public: |
| | 370 | /* copy from another frame pointer */ |
| | 371 | void copy_from(const CVmDbgFramePtr *frame) |
| | 372 | { |
| | 373 | /* copy the original frame's pointer */ |
| | 374 | p_ = frame->p_; |
| | 375 | } |
| | 376 | |
| | 377 | /* get the ID of the enclosing frame */ |
| | 378 | uint get_enclosing_frame() const { return osrp2(p_); } |
| | 379 | |
| | 380 | /* get the number of symbols in the frame */ |
| | 381 | uint get_sym_count() const { return osrp2(p_ + 2); } |
| | 382 | |
| | 383 | /* set up a pointer to the first symbol */ |
| | 384 | void set_first_sym_ptr(CVmDbgFrameSymPtr *entry) |
| | 385 | { entry->set(p_ + 4); } |
| | 386 | |
| | 387 | private: |
| | 388 | /* initialize with a pointer to the first byte of our entry */ |
| | 389 | void set(const uchar *p) { p_ = p; } |
| | 390 | |
| | 391 | /* pointer to the first byte of our entry */ |
| | 392 | const uchar *p_; |
| | 393 | }; |
| | 394 | |
| | 395 | |
| | 396 | /* |
| | 397 | * Debugger Records Table Pointer |
| | 398 | */ |
| | 399 | class CVmDbgTablePtr |
| | 400 | { |
| | 401 | public: |
| | 402 | /* |
| | 403 | * Initialize with a pointer to the start of the function -- we'll |
| | 404 | * read the debugger table offset out of the method header. Returns |
| | 405 | * true if the function has debugger records, false if there is no |
| | 406 | * debugger table defined in the function. |
| | 407 | */ |
| | 408 | int set(const uchar *p) |
| | 409 | { |
| | 410 | CVmFuncPtr func; |
| | 411 | |
| | 412 | /* if the pointer is null, there's obviously no function pointer */ |
| | 413 | if (p == 0) |
| | 414 | return FALSE; |
| | 415 | |
| | 416 | /* set up the function pointer */ |
| | 417 | func.set(p); |
| | 418 | |
| | 419 | /* if there's no debugger table, simply return this information */ |
| | 420 | if (func.get_debug_ofs() == 0) |
| | 421 | return FALSE; |
| | 422 | |
| | 423 | /* set up our pointer by reading from the header */ |
| | 424 | p_ = p + func.get_debug_ofs(); |
| | 425 | |
| | 426 | /* indicate that there is a valid debugger records table */ |
| | 427 | return TRUE; |
| | 428 | } |
| | 429 | |
| | 430 | /* copy from another debug table pointer */ |
| | 431 | void copy_from(const CVmDbgTablePtr *table) |
| | 432 | { |
| | 433 | /* copy the other table's location */ |
| | 434 | p_ = table->p_; |
| | 435 | } |
| | 436 | |
| | 437 | /* get the number of source line entries in the table */ |
| | 438 | size_t get_line_count(VMG0_) const |
| | 439 | { return osrp2(p_ + G_dbg_hdr_size); } |
| | 440 | |
| | 441 | /* get the number of frame entries in the table */ |
| | 442 | size_t get_frame_count(VMG0_) const |
| | 443 | { return osrp2(p_ + get_frame_ofs(vmg0_)); } |
| | 444 | |
| | 445 | /* initialize a CVmDbgLinePtr with the entry at the given index */ |
| | 446 | void set_line_ptr(VMG_ CVmDbgLinePtr *entry, size_t idx) const |
| | 447 | { entry->set(p_ + G_dbg_hdr_size + 2 + (idx * G_line_entry_size)); } |
| | 448 | |
| | 449 | /* initialize a CVmDbgFramePtr with the entry at the given index */ |
| | 450 | void set_frame_ptr(VMG_ CVmDbgFramePtr *entry, size_t idx) const |
| | 451 | { |
| | 452 | size_t index_ofs; |
| | 453 | size_t frame_ofs; |
| | 454 | |
| | 455 | /* |
| | 456 | * Compute the location of the index table entry - note that |
| | 457 | * 'idx' is a one-based index value, so we must decrement it |
| | 458 | * before performing our offset arithmetic. Note also that we |
| | 459 | * must add two bytes to get past the count field at the start |
| | 460 | * of the frame table. Each index entry is two bytes long. |
| | 461 | * |
| | 462 | * (If we were clever, we would distribute the multiply-by-two, |
| | 463 | * which would yield a constant subtraction of two, which would |
| | 464 | * cancel the constant addition of two. Let's hope the C++ |
| | 465 | * catches on to this, because we would rather not be clever and |
| | 466 | * instead write it explicitly for greater clarity.) |
| | 467 | */ |
| | 468 | index_ofs = get_frame_ofs(vmg0_) + 2 + (2 * (idx - 1)); |
| | 469 | |
| | 470 | /* read the frame offset from the entry */ |
| | 471 | frame_ofs = osrp2(p_ + index_ofs); |
| | 472 | |
| | 473 | /* |
| | 474 | * the frame offset in the table is relative to the location of |
| | 475 | * the table location containing the entry, so add the index |
| | 476 | * offset to the frame offset to get the actual location of the |
| | 477 | * frame entry |
| | 478 | */ |
| | 479 | entry->set(p_ + index_ofs + frame_ofs); |
| | 480 | } |
| | 481 | |
| | 482 | private: |
| | 483 | /* get the offset to the start of the frame table */ |
| | 484 | size_t get_frame_ofs(VMG0_) const |
| | 485 | { |
| | 486 | /* |
| | 487 | * the frame table follows the line records, which follow the |
| | 488 | * debug table header and the line counter, plus another two |
| | 489 | * bytes for the post-frame offset pointer |
| | 490 | */ |
| | 491 | return (G_dbg_hdr_size |
| | 492 | + 2 |
| | 493 | + (get_line_count(vmg0_) * G_line_entry_size) |
| | 494 | + 2); |
| | 495 | } |
| | 496 | |
| | 497 | /* pointer to the first byte of the debugger records table */ |
| | 498 | const uchar *p_; |
| | 499 | }; |
| | 500 | |
| | 501 | #endif /* VMFUNC_H */ |
| | 502 | |