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