cfad47cfa3/tads3/vmtype.h

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
/* $Header: d:/cvsroot/tads/tads3/VMTYPE.H,v 1.3 1999/05/17 02:52:29 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
  vmtype.h - VM types
12
Function
13
  
14
Notes
15
  
16
Modified
17
  10/21/98 MJRoberts  - Creation
18
*/
19
20
#ifndef VMTYPE_H
21
#define VMTYPE_H
22
23
#include <string.h>
24
#include <stdlib.h>
25
26
#include "t3std.h"
27
#include "vmerr.h"
28
#include "vmerrnum.h"
29
#include "vmglob.h"
30
31
32
/*
33
 *   Constant pool/code offset.  This is an address of an object in the
34
 *   pool.  Pool offsets are 32-bit values.  
35
 */
36
typedef uint32 pool_ofs_t;
37
38
/*
39
 *   Savepoint ID's are stored in a single byte; since we store many
40
 *   copies of savepoint ID's (since they need to be stored with each undo
41
 *   list head), we want to save some space on this type.  This limits us
42
 *   to 256 simultaneous savepoints, but this should be more than we
43
 *   actually want to keep around anyway, because of the amount of memory
44
 *   it would consume to try to keep more than that around.  
45
 */
46
typedef uchar vm_savept_t;
47
const vm_savept_t VM_SAVEPT_MAX = 255;
48
49
/* 
50
 *   Object ID type.  VM_INVALID_OBJ is a distinguished value that serves
51
 *   as an invalid object ID (a null pointer, effectively); no object can
52
 *   ever have this ID.  
53
 */
54
typedef uint32 vm_obj_id_t;
55
const vm_obj_id_t VM_INVALID_OBJ = 0;
56
57
/*
58
 *   Property ID.  Property ID's are 16-bit values.  VM_INVALID_PROP is a
59
 *   distinguished value that serves as an invalid property ID, which can
60
 *   be used to indicate the absence of a property value.  
61
 */
62
typedef uint16 vm_prop_id_t;
63
const vm_prop_id_t VM_INVALID_PROP = 0;
64
65
/*
66
 *   Maximum recursion depth for recursive equality tests and hash
67
 *   calculations.
68
 *   
69
 *   When we're comparing or hashing a tree of references by value, such as
70
 *   when we're comparing two vectors or hashing a vector, we'll keep track
71
 *   of the recursion depth of our tree traversal.  If we reach this depth,
72
 *   we'll throw an error on the assumption that the tree contains cycles and
73
 *   thus cannot be hashed or compared by value.  This depth is chosen to be
74
 *   large enough that it's unlikely we'll exceed it with acyclical trees,
75
 *   but small enough that we probably won't blow the C++ stack before we
76
 *   reach this depth.  
77
 */
78
const int VM_MAX_TREE_DEPTH_EQ = 256;
79
80
/*
81
 *   Datatypes
82
 */
83
enum vm_datatype_t
84
{
85
    /* nil - doubles as a null pointer and a boolean false */
86
    VM_NIL = 1,
87
88
    /* true - boolean true */
89
    VM_TRUE,
90
91
    /* 
92
     *   Stack pointer (this is used to store a pointer to the enclosing
93
     *   frame in a stack frame).  This is a native machine pointer.  
94
     */
95
    VM_STACK,
96
97
    /* 
98
     *   Code pointer (this is used to store a pointer to the return
99
     *   address in a stack frame, for example).  This is a native machine
100
     *   pointer.  This differs from VM_CODEOFS in that this is a native
101
     *   machine pointer.  
102
     */
103
    VM_CODEPTR,
104
105
    /* object reference */
106
    VM_OBJ,
107
108
    /* property ID */
109
    VM_PROP,
110
111
    /* 32-bit signed integer */
112
    VM_INT,
113
    
114
    /* 
115
     *   string constant value - the value is an offset into the constant
116
     *   pool of the string descriptor 
117
     */
118
    VM_SSTRING,
119
    
120
    /* 
121
     *   self-printing string value - the value is an offset into the
122
     *   constant pool of the string descriptor 
123
     */
124
    VM_DSTRING,
125
126
    /* 
127
     *   list constant - the value is an offset into the constant pool of
128
     *   the list descriptor 
129
     */
130
    VM_LIST,
131
132
    /* 
133
     *   byte-code constant offset - this is an offset into the byte-code
134
     *   pool.  This differs from VM_CODEPTR in that this is an offset in
135
     *   the byte-code constant pool rather than a native machine pointer.
136
     *   
137
     */
138
    VM_CODEOFS,
139
140
    /*
141
     *   function pointer - this is represented as an offset into the
142
     *   byte-code pool.  This differs from VM_CODEOFS in that the code
143
     *   referenced by a VM_CODEOFS value is generally invoked directly
144
     *   whenever the value is evaluated, whereas VM_FUNCPTR values are
145
     *   used to convey function pointers, so the underlying code is not
146
     *   executed implicitly on evaluation of such a value but must be
147
     *   explicitly invoked. 
148
     */
149
    VM_FUNCPTR,
150
151
    /*
152
     *   This is a special pseudo-type used to indicate that a value is
153
     *   not present.  This differs from nil, in that nil is a null
154
     *   reference or false value, whereas this indicates that there's no
155
     *   specified value at all.  This is used, for example, to indicate
156
     *   in an undo record that a property did not previously exist. 
157
     */
158
    VM_EMPTY,
159
160
    /*
161
     *   This is a special pseudo-type used to indicate that evaluating an
162
     *   expression requires executing system code.  The value stored is a
163
     *   pointer to a constant CVmNativeCodeDesc object, which describes a
164
     *   native code method.  
165
     */
166
    VM_NATIVE_CODE,
167
168
    /*
169
     *   Enumerated constant 
170
     */
171
    VM_ENUM,
172
173
    /*
174
     *   First invalid type ID.  Tools (such as compilers and debuggers)
175
     *   can use this ID and any higher ID values to flag their own
176
     *   internal types.  
177
     */
178
    VM_FIRST_INVALID_TYPE
179
};
180
181
/* macro to create a private type constant for internal use in a tool */
182
#define VM_MAKE_INTERNAL_TYPE(idx) \
183
    ((vm_datatype_t)(((int)VM_FIRST_INVALID_TYPE) + (idx)))
184
185
/*
186
 *   Value container.  Local variables, stack locations, and other value
187
 *   holders use this structure to store a value and its type.  
188
 */
189
struct vm_val_t
190
{
191
    vm_datatype_t typ;
192
    union
193
    {
194
        /* stack/code pointer */
195
        void *ptr;
196
197
        /* object reference */
198
        vm_obj_id_t obj;
199
200
        /* property ID */
201
        vm_prop_id_t prop;
202
203
        /* 32-bit integer */
204
        int32 intval;
205
206
        /* enumerated constant */
207
        uint32 enumval;
208
209
        /* sstring/dstring/list constant pool offset/pcode pool offset */
210
        pool_ofs_t ofs;
211
212
        /* native code descriptor */
213
        const class CVmNativeCodeDesc *native_desc;
214
    } val;
215
216
    /* set various types of values */
217
    void set_empty() { typ = VM_EMPTY; }
218
    void set_nil() { typ = VM_NIL; }
219
    void set_true() { typ = VM_TRUE; }
220
    void set_stack(void *ptr) { typ = VM_STACK; val.ptr = ptr; }
221
    void set_codeptr(void *ptr) { typ = VM_CODEPTR; val.ptr = ptr; }
222
    void set_obj(vm_obj_id_t obj) { typ = VM_OBJ; val.obj = obj; }
223
    void set_nil_obj() { typ = VM_NIL; val.obj = VM_INVALID_OBJ; }
224
    void set_propid(vm_prop_id_t prop) { typ = VM_PROP; val.prop = prop; }
225
    void set_int(int32 intval) { typ = VM_INT; val.intval = intval; }
226
    void set_enum(uint32 enumval) { typ = VM_ENUM; val.enumval = enumval; }
227
    void set_sstring(pool_ofs_t ofs) { typ = VM_SSTRING; val.ofs = ofs; }
228
    void set_dstring(pool_ofs_t ofs) { typ = VM_DSTRING; val.ofs = ofs; }
229
    void set_list(pool_ofs_t ofs) { typ = VM_LIST; val.ofs = ofs; }
230
    void set_codeofs(pool_ofs_t ofs) { typ = VM_CODEOFS; val.ofs = ofs; }
231
    void set_fnptr(pool_ofs_t ofs) { typ = VM_FUNCPTR; val.ofs = ofs; }
232
    void set_native(const class CVmNativeCodeDesc *desc)
233
        { typ = VM_NATIVE_CODE; val.native_desc = desc; }
234
235
    /* 
236
     *   set an object or nil value: if the object ID is VM_INVALID_OBJ,
237
     *   we'll set the type to nil 
238
     */
239
    void set_obj_or_nil(vm_obj_id_t obj)
240
    {
241
        /* set the object value initially */
242
        typ = VM_OBJ;
243
        val.obj = obj;
244
245
        /* if the object is invalid, set the type to nil */
246
        if (obj == VM_INVALID_OBJ)
247
            typ = VM_NIL;
248
    }
249
250
    /* set to an integer giving the datatype of the given value */
251
    void set_datatype(VMG_ const vm_val_t *val);
252
253
    /* set to nil if 'val' is zero, true if 'val' is non-zero */
254
    void set_logical(int v) { typ = (v != 0 ? VM_TRUE : VM_NIL); }
255
256
    /* determine if the value is logical (nil or true) */
257
    int is_logical() const { return (typ == VM_NIL || typ == VM_TRUE); }
258
259
    /* 
260
     *   Get a logical as numeric TRUE or FALSE.  This does not perform
261
     *   any type checking; the caller must ensure that the value is
262
     *   either true or nil, or this may return meaningless results.  
263
     */
264
    int get_logical() const { return (typ == VM_TRUE); }
265
266
    /*
267
     *   Get the underlying string constant value.  If the value does not
268
     *   have an underlying string constant (because it is of a type that
269
     *   does not store a string value), this will return null. 
270
     */
271
    const char *get_as_string(VMG0_) const;
272
273
    /*
274
     *   Get the underlying list constant value.  If the value does not
275
     *   have an underlying list constant (because it is of a type that
276
     *   does not store list data), this returns null. 
277
     */
278
    const char *get_as_list(VMG0_) const;
279
280
    /*
281
     *   Get the effective number of elements from this value when the
282
     *   value is used as the right-hand side of a '+' or '-' operator
283
     *   whose left-hand side implies that the operation involved is a set
284
     *   operation (this is the case is the left-hand side is of certain
285
     *   collection types, such as list, array, or vector); and get the
286
     *   nth element in that context.  Most types of values contribute
287
     *   only one element to these operations, but some collection types
288
     *   supply their elements individually, rather than the collection
289
     *   itself, for these operations.  'idx' is the 1-based index of the
290
     *   element to retrieve.  
291
     */
292
    size_t get_coll_addsub_rhs_ele_cnt(VMG0_) const;
293
    void get_coll_addsub_rhs_ele(VMG_ size_t idx, vm_val_t *result) const;
294
295
    /*
296
     *   Convert a numeric value to an integer value.  If the value isn't
297
     *   numeric, throws an error. 
298
     */
299
    void num_to_logical()
300
    {
301
        /* check the type */
302
        if (typ == VM_INT)
303
        {
304
            /* it's an integer - treat 0 as nil, all else as true */
305
            typ = (val.intval == 0 ? VM_NIL : VM_TRUE);
306
        }
307
        else
308
        {
309
            /* it's not a number - throw an error */
310
            err_throw(VMERR_NO_LOG_CONV);
311
        }
312
    }
313
314
    /* determine if the value is some kind of number */
315
    int is_numeric() const { return (typ == VM_INT); }
316
317
    /*
318
     *   Convert a numeric value to an integer.  If the value is not
319
     *   numeric, we'll throw an error. 
320
     */
321
    int32 num_to_int() const
322
    {
323
        /* check the type */
324
        if (typ == VM_INT)
325
        {
326
            /* it's an integer already - return the value directly */
327
            return val.intval;
328
        }
329
        else
330
        {
331
            /* 
332
             *   other types are not numeric and can't be directly
333
             *   converted to integer by arithmetic conversion
334
             */
335
            err_throw(VMERR_NUM_VAL_REQD);
336
337
            /* the compiler might not know we'll never get here */
338
            AFTER_ERR_THROW(return 0;)
339
        }
340
    }
341
342
    /* 
343
     *   determine if the numeric value is zero; throws an error if the
344
     *   value is not numeric 
345
     */
346
    int num_is_zero() const
347
    {
348
        /* check the type */
349
        if (typ == VM_INT)
350
        {
351
            /* check the integer value to see if it's zero */
352
            return (val.intval == 0);
353
        }
354
        else
355
        {
356
            /* it's not a number */
357
            err_throw(VMERR_NUM_VAL_REQD);
358
359
            /* in case the compiler doesn't know we'll never get here */
360
            AFTER_ERR_THROW(return 0;)
361
        }
362
    }
363
364
    /*
365
     *   Determine if this value equals a given value.  The nature of the
366
     *   match depends on the type of this value:
367
     *   
368
     *   integers, property ID's, code offsets: the types and values must
369
     *   match exactly.
370
     *   
371
     *   string and list constants: the other value must either be the same
372
     *   type of constant, or an object that has an underlying value of the
373
     *   same type; and the contents of the strings or lists must match.
374
     *   
375
     *   objects: the match depends on the type of the object.  We invoke the
376
     *   object's virtual equals() routine to make this determination.
377
     *   
378
     *   'depth' has the same meaning as in calc_hash().  
379
     */
380
    int equals(VMG_ const vm_val_t *v) const { return equals(vmg_ v, 0); }
381
    int equals(VMG_ const vm_val_t *val, int depth) const;
382
383
    /*
384
     *   Calculate a hash for the value.  The meaning of the hash varies by
385
     *   type, but is stable for a given value.  'depth' is a recursion depth
386
     *   counter, with the same meaning as in CVmObject::calc_hash().  
387
     */
388
    uint calc_hash(VMG0_) const { return calc_hash(vmg_ 0); }
389
    uint calc_hash(VMG_ int depth) const;
390
391
    /*
392
     *   Compare this value to the given value.  Returns a value greater than
393
     *   zero if this value is greater than 'val', a value less than zero if
394
     *   this value is less than 'val', or 0 if the two values are equal.
395
     *   Throws an error if the two values are not comparable.
396
     *   
397
     *   By far the most common type of comparison is between integers, so we
398
     *   test in-line to see if we have two integer values, and if so, use a
399
     *   fast in-line comparison.  If we don't have two integers, we'll use
400
     *   our full out-of-line test, which will look at other more interesting
401
     *   type combinations.  
402
     */
403
    int compare_to(VMG_ const vm_val_t *b) const
404
    {
405
        if (typ == VM_INT && b->typ == VM_INT)
406
            return (val.intval > b->val.intval
407
                    ? 1 : val.intval < b->val.intval ? -1 : 0);
408
        else
409
            return gen_compare_to(vmg_ b);
410
    }
411
412
    /*
413
     *   relative value comparisons 
414
     */
415
416
    /* self > b */
417
    int is_gt(VMG_ const vm_val_t *b) const
418
    {
419
        if (typ == VM_INT && b->typ == VM_INT)
420
            return val.intval > b->val.intval;
421
        else
422
            return gen_compare_to(vmg_ b) > 0;
423
    }
424
425
    /* self >= b */
426
    int is_ge(VMG_ const vm_val_t *b) const
427
    {
428
        if (typ == VM_INT && b->typ == VM_INT)
429
            return val.intval >= b->val.intval;
430
        else
431
            return gen_compare_to(vmg_ b) >= 0;
432
    }
433
434
    /* self < b */
435
    int is_lt(VMG_ const vm_val_t *b) const
436
    {
437
        if (typ == VM_INT && b->typ == VM_INT)
438
            return val.intval < b->val.intval;
439
        else
440
            return gen_compare_to(vmg_ b) < 0;
441
    }
442
443
    /* self <= b */
444
    int is_le(VMG_ const vm_val_t *b) const
445
    {
446
        if (typ == VM_INT && b->typ == VM_INT)
447
            return val.intval <= b->val.intval;
448
        else
449
            return gen_compare_to(vmg_ b) <= 0;
450
    }
451
452
private:
453
    /* out-of-line comparison, used when we don't have two integers */
454
    int gen_compare_to(VMG_ const vm_val_t *val) const;
455
};
456
457
/* ------------------------------------------------------------------------ */
458
/*
459
 *   Native code descriptor.  This describes a native method call of an
460
 *   intrinsic class object.  
461
 */
462
class CVmNativeCodeDesc
463
{
464
public:
465
    /* create a descriptor with an exact number of arguments */
466
    CVmNativeCodeDesc(int argc)
467
    {
468
        /* remember the parameters - there are no optional arguments */
469
        min_argc_ = argc;
470
        opt_argc_ = 0;
471
        varargs_ = FALSE;
472
    }
473
474
    /* create a descriptor with optional arguments (but not varargs) */
475
    CVmNativeCodeDesc(int min_argc, int opt_argc)
476
    {
477
        /* remember the parameters */
478
        min_argc_ = min_argc;
479
        opt_argc_ = opt_argc;
480
        varargs_ = FALSE;
481
    }
482
483
    /* create a descriptor with optional arguments and/or varargs */
484
    CVmNativeCodeDesc(int min_argc, int opt_argc, int varargs)
485
    {
486
        /* remember the parameters */
487
        min_argc_ = min_argc;
488
        opt_argc_ = opt_argc;
489
        varargs_ = varargs;
490
    }
491
492
    /* check the given number of arguments for validity */
493
    int args_ok(int argc) const
494
    {
495
        /* 
496
         *   the actual parameters must number at least the minimum, and
497
         *   cannot exceed the maximum (i.e., the minimum plus the
498
         *   optionals) unless we have varargs, in which case there is no
499
         *   maximum 
500
         */
501
        return (argc >= min_argc_
502
                && (varargs_ || argc <= min_argc_ + opt_argc_));
503
    }
504
505
    /* minimum argument count */
506
    int min_argc_;
507
508
    /* number of optional named arguments beyond the minimum */
509
    int opt_argc_;
510
511
    /* 
512
     *   true -> varargs: any number of arguments greater than or equal to
513
     *   min_argc_ are valid
514
     */
515
    int varargs_;
516
};
517
518
/* ------------------------------------------------------------------------ */
519
/*
520
 *   String handling - these routines are provided as covers to allow for
521
 *   easier adjustment for Unicode or other encodings.  Don't include these
522
 *   if we're compiling interface routines for the HTML TADS environment,
523
 *   since HTML TADS has its own similar definitions for these, and we don't
524
 *   need these for interface code.
525
 *   
526
 *   NOTE: The purpose of this macro is to indicate that we're being
527
 *   #included by an HTML TADS source file - that is, the current compile run
528
 *   is for an htmltads/xxx.cpp file.  Do NOT #define this macro when
529
 *   compiling tads3/xxx.cpp files.  The tads3/xxx.cpp files have absolutely
530
 *   no explicit dependencies on the htmltads subsystem and thus don't
531
 *   #include the htmltads headers that would provide the conflicting
532
 *   interface definitions for these functions.  The conflict is in the other
533
 *   direction: some of the htmltads source files explicitly depend on tads3
534
 *   headers, so they indirectly #include this header.  So, this macro needs
535
 *   to be defined only when compiling htmltads/xxx.cpp files.  
536
 */
537
#ifdef T3_COMPILING_FOR_HTML
538
#include "tadshtml.h"
539
#else
540
541
inline size_t get_strlen(const textchar_t *str) { return strlen(str); }
542
inline void do_strcpy(textchar_t *dst, const textchar_t *src)
543
    { strcpy(dst, src); }
544
545
#endif /* T3_COMPILING_FOR_HTML */
546
547
/* ------------------------------------------------------------------------ */
548
/*
549
 *   Portable Binary Representations.  When we store certain types of
550
 *   information in memory, we store it in a format that is identical to
551
 *   the format we use in portable binary files; using this format allows
552
 *   us to read and write binary files as byte images, without any
553
 *   interpretation, which greatly improves I/O performance in many cases.
554
 */
555
556
/*
557
 *   Portable binary LENGTH indicator.  This is used to store length
558
 *   prefixes for strings, lists, and similar objects.  We use a UINT2
559
 *   (16-bit unsigned integer) for this type of value.  
560
 */
561
const size_t VMB_LEN = 2;
562
inline void vmb_put_len(char *buf, size_t len) { oswp2(buf, len); }
563
inline size_t vmb_get_len(const char *buf) { return osrp2(buf); }
564
565
/*
566
 *   Portable binary unsigned 2-byte integer 
567
 */
568
const size_t VMB_UINT2 = 2;
569
inline void vmb_put_uint2(char *buf, uint16 i) { oswp2(buf, i); }
570
inline uint16 vmb_get_uint2(const char *buf) { return osrp2(buf); }
571
572
/*
573
 *   Portable binary object ID. 
574
 */
575
const size_t VMB_OBJECT_ID = 4;
576
inline void vmb_put_objid(char *buf, vm_obj_id_t obj) { oswp4(buf, obj); }
577
inline vm_obj_id_t vmb_get_objid(const char *buf) { return t3rp4u(buf); }
578
579
/*
580
 *   Portable binary property ID 
581
 */
582
const size_t VMB_PROP_ID = 2;
583
inline void vmb_put_propid(char *buf, vm_obj_id_t prop) { oswp2(buf, prop); }
584
inline vm_prop_id_t vmb_get_propid(const char *buf) { return osrp2(buf); }
585
586
/*
587
 *   Portable data holder.  This is used to store varying-type data items;
588
 *   for example, this is used to store an element in a list, or the value
589
 *   of a property in an object.  This type of value stores a one-byte
590
 *   prefix indicating the type of the value, and a four-byte area in
591
 *   which the value is stored.  The actual use of the four-byte value
592
 *   area depends on the type.  
593
 */
594
const size_t VMB_DATAHOLDER = 5;
595
596
/* offset from a portable data holder pointer to the data value */
597
const size_t VMB_DH_DATAOFS = 1;
598
599
/* store a portable dataholder from a vm_val_t */
600
void vmb_put_dh(char *buf, const vm_val_t *val);
601
602
/* store a nil value in a portable dataholder */
603
inline void vmb_put_dh_nil(char *buf) { buf[0] = VM_NIL; }
604
605
/* store an object value in a portable dataholder */
606
inline void vmb_put_dh_obj(char *buf, vm_obj_id_t obj)
607
    { buf[0] = VM_OBJ; vmb_put_objid(buf + 1, obj); }
608
609
/* store a property value in a portable dataholder */
610
inline void vmb_put_dh_prop(char *buf, vm_prop_id_t prop)
611
    { buf[0] = VM_PROP; vmb_put_propid(buf + 1, prop); }
612
613
/* get the value portion of a vm_val_t from a portable dataholder */
614
void vmb_get_dh_val(const char *buf, vm_val_t *val);
615
616
/* get the type from a portable dataholder */
617
inline vm_datatype_t vmb_get_dh_type(const char *buf)
618
    { return (vm_datatype_t)buf[0]; }
619
620
/* get a vm_val_t from a portable dataholder */
621
inline void vmb_get_dh(const char *buf, vm_val_t *val)
622
    { val->typ = vmb_get_dh_type(buf); vmb_get_dh_val(buf, val); }
623
624
/* get an object value from a portable dataholder */
625
inline vm_obj_id_t vmb_get_dh_obj(const char *buf)
626
    { return (vm_obj_id_t)t3rp4u(buf+1); }
627
628
/* get an integer value from a portable dataholder */
629
inline int32 vmb_get_dh_int(const char *buf)
630
    { return (int32)osrp4(buf+1); }
631
632
/* get a property ID value from a portable dataholder */
633
inline vm_prop_id_t vmb_get_dh_prop(const char *buf)
634
    { return (vm_prop_id_t)osrp2(buf+1); }
635
636
/* get a constant offset value from a portable dataholder */
637
inline pool_ofs_t vmb_get_dh_ofs(const char *buf)
638
    { return (pool_ofs_t)t3rp4u(buf+1); }
639
640
641
#endif /* VMTYPE_H */
642