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 */ set(const uchar * p)75 void set(const uchar *p) { p_ = p; } 76 77 /* copy from another function pointer */ copy_from(const CVmFuncPtr * fp)78 void copy_from(const CVmFuncPtr *fp) { p_ = fp->p_; } 79 80 /* get the minimum argument count */ get_min_argc()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? */ is_varargs()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 */ argc_ok(int argc)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 */ get_argc()130 uchar get_argc() const { return *(p_ + 0); } 131 132 /* get the number of locals */ get_local_cnt()133 uint get_local_cnt() const { return osrp2(p_ + 2); } 134 135 /* get the total stack slots required by the function */ get_stack_depth()136 uint get_stack_depth() const { return osrp2(p_ + 4); } 137 138 /* get the exception table offset */ get_exc_ofs()139 uint get_exc_ofs() const { return osrp2(p_ + 6); } 140 141 /* get the debugger records table offset */ get_debug_ofs()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 */ get_start_ofs()201 uint get_start_ofs() const { return osrp2(p_); } get_end_ofs()202 uint get_end_ofs() const { return osrp2(p_ + 2); } 203 204 /* get the exception class's object ID */ get_exception()205 vm_obj_id_t get_exception() const { return (vm_obj_id_t)osrp4(p_ + 4); } 206 207 /* get the handler byte code offset */ get_handler_ofs()208 uint get_handler_ofs() const { return osrp2(p_ + 8); } 209 210 /* increment the pointer to the next entry in the table */ inc(VMG0_)211 void inc(VMG0_) { p_ += G_exc_entry_size; } 212 213 private: 214 /* initialize with a pointer to the first byte of our entry */ set(const uchar * p)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 */ set(const uchar * p)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 */ get_count()252 size_t get_count() const { return osrp2(p_); } 253 254 /* initialize a CVmExcEntryPtr with the entry at the given index */ set_entry_ptr(VMG_ CVmExcEntryPtr * entry,size_t idx)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 */ get_start_ofs()289 uint get_start_ofs() const { return osrp2(p_); } 290 291 /* get the source file ID (an index into the global source file list) */ get_source_id()292 uint get_source_id() const { return osrp2(p_ + 2); } 293 294 /* get the line number in the source file */ get_source_line()295 ulong get_source_line() const { return osrp4(p_ + 4); } 296 297 /* get the frame ID (a 1-based index into our local frame table) */ get_frame_id()298 uint get_frame_id() const { return osrp2(p_ + 8); } 299 300 /* increment the pointer to the next entry in the table */ inc(VMG0_)301 void inc(VMG0_) { p_ += G_line_entry_size; } 302 303 /* set from another line pointer */ copy_from(const CVmDbgLinePtr * p)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 */ set(const uchar * p)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 */ get_var_num()324 uint get_var_num() const { return osrp2(p_); } 325 326 /* get the context array index (for context locals) */ get_ctx_arr_idx()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 */ is_local()331 int is_local() const { return (get_flags() & 1) == 0; } is_param()332 int is_param() const 333 { return (((get_flags() & 1) != 0) && !is_ctx_local()); } 334 335 /* determine if I'm a context local */ is_ctx_local()336 int is_ctx_local() const { return (get_flags() & 2) != 0; } 337 338 /* get the length of my name string */ get_sym_len(VMG0_)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 */ get_sym(VMG0_)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 */ inc(VMG0_)347 void inc(VMG0_) 348 { p_ += G_dbg_lclsym_hdr_size + 2 + get_sym_len(vmg0_); } 349 350 private: 351 /* get my flags value */ get_flags()352 uint get_flags() const { return osrp2(p_ + 2); } 353 354 /* initialize with a pointer to the first byte of our entry */ set(const uchar * p)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 */ copy_from(const CVmDbgFramePtr * frame)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 */ get_enclosing_frame()378 uint get_enclosing_frame() const { return osrp2(p_); } 379 380 /* get the number of symbols in the frame */ get_sym_count()381 uint get_sym_count() const { return osrp2(p_ + 2); } 382 383 /* set up a pointer to the first symbol */ set_first_sym_ptr(CVmDbgFrameSymPtr * entry)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 */ set(const uchar * p)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 */ set(const uchar * p)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 */ copy_from(const CVmDbgTablePtr * table)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 */ get_line_count(VMG0_)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 */ get_frame_count(VMG0_)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 */ set_line_ptr(VMG_ CVmDbgLinePtr * entry,size_t idx)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 */ set_frame_ptr(VMG_ CVmDbgFramePtr * entry,size_t idx)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 */ get_frame_ofs(VMG0_)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 503