cfad47cfa3/tads3/vmfunc.h

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