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 */
set_emptyvm_val_t217     void set_empty() { typ = VM_EMPTY; }
set_nilvm_val_t218     void set_nil() { typ = VM_NIL; }
set_truevm_val_t219     void set_true() { typ = VM_TRUE; }
set_stackvm_val_t220     void set_stack(void *ptr) { typ = VM_STACK; val.ptr = ptr; }
set_codeptrvm_val_t221     void set_codeptr(void *ptr) { typ = VM_CODEPTR; val.ptr = ptr; }
set_objvm_val_t222     void set_obj(vm_obj_id_t obj) { typ = VM_OBJ; val.obj = obj; }
set_nil_objvm_val_t223     void set_nil_obj() { typ = VM_NIL; val.obj = VM_INVALID_OBJ; }
set_propidvm_val_t224     void set_propid(vm_prop_id_t prop) { typ = VM_PROP; val.prop = prop; }
set_intvm_val_t225     void set_int(int32 intval) { typ = VM_INT; val.intval = intval; }
set_enumvm_val_t226     void set_enum(uint32 enumval) { typ = VM_ENUM; val.enumval = enumval; }
set_sstringvm_val_t227     void set_sstring(pool_ofs_t ofs) { typ = VM_SSTRING; val.ofs = ofs; }
set_dstringvm_val_t228     void set_dstring(pool_ofs_t ofs) { typ = VM_DSTRING; val.ofs = ofs; }
set_listvm_val_t229     void set_list(pool_ofs_t ofs) { typ = VM_LIST; val.ofs = ofs; }
set_codeofsvm_val_t230     void set_codeofs(pool_ofs_t ofs) { typ = VM_CODEOFS; val.ofs = ofs; }
set_fnptrvm_val_t231     void set_fnptr(pool_ofs_t ofs) { typ = VM_FUNCPTR; val.ofs = ofs; }
set_nativevm_val_t232     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      */
set_obj_or_nilvm_val_t239     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 */
set_logicalvm_val_t254     void set_logical(int val) { typ = (val != 0 ? VM_TRUE : VM_NIL); }
255 
256     /* determine if the value is logical (nil or true) */
is_logicalvm_val_t257     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      */
get_logicalvm_val_t264     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      */
num_to_logicalvm_val_t299     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 */
is_numericvm_val_t315     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      */
num_to_intvm_val_t321     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 doesn't know we'll never get here */
338             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      */
num_is_zerovm_val_t346     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             /* the compiler doesn't know we'll never get here */
360             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      */
equalsvm_val_t380     int equals(VMG_ const vm_val_t *val) const { return equals(vmg_ val, 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      */
calc_hashvm_val_t388     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      */
compare_tovm_val_t403     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 */
is_gtvm_val_t417     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 */
is_gevm_val_t426     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 */
is_ltvm_val_t435     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 */
is_levm_val_t444     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 */
CVmNativeCodeDesc(int argc)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) */
CVmNativeCodeDesc(int min_argc,int opt_argc)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 */
CVmNativeCodeDesc(int min_argc,int opt_argc,int 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 */
args_ok(int argc)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
522  *   these if we're compiling interface routines for the HTML TADS
523  *   environment, since HTML TADS has its own similar definitions for
524  *   these, and we don't need these for interface code.
525  */
526 #ifndef T3_COMPILING_FOR_HTML
527 
get_strlen(const textchar_t * str)528 inline size_t get_strlen(const textchar_t *str) { return strlen(str); }
do_strcpy(textchar_t * dst,const textchar_t * src)529 inline void do_strcpy(textchar_t *dst, const textchar_t *src)
530     { strcpy(dst, src); }
531 
532 #endif /* T3_COMPILING_FOR_HTML */
533 
534 /* ------------------------------------------------------------------------ */
535 /*
536  *   Portable Binary Representations.  When we store certain types of
537  *   information in memory, we store it in a format that is identical to
538  *   the format we use in portable binary files; using this format allows
539  *   us to read and write binary files as byte images, without any
540  *   interpretation, which greatly improves I/O performance in many cases.
541  */
542 
543 /*
544  *   Portable binary LENGTH indicator.  This is used to store length
545  *   prefixes for strings, lists, and similar objects.  We use a UINT2
546  *   (16-bit unsigned integer) for this type of value.
547  */
548 const size_t VMB_LEN = 2;
vmb_put_len(char * buf,size_t len)549 inline void vmb_put_len(char *buf, size_t len) { oswp2(buf, len); }
vmb_get_len(const char * buf)550 inline size_t vmb_get_len(const char *buf) { return osrp2(buf); }
551 
552 /*
553  *   Portable binary unsigned 2-byte integer
554  */
555 const size_t VMB_UINT2 = 2;
vmb_put_uint2(char * buf,uint16 i)556 inline void vmb_put_uint2(char *buf, uint16 i) { oswp2(buf, i); }
vmb_get_uint2(const char * buf)557 inline uint16 vmb_get_uint2(const char *buf) { return osrp2(buf); }
558 
559 /*
560  *   Portable binary object ID.
561  */
562 const size_t VMB_OBJECT_ID = 4;
vmb_put_objid(char * buf,vm_obj_id_t obj)563 inline void vmb_put_objid(char *buf, vm_obj_id_t obj) { oswp4(buf, obj); }
vmb_get_objid(const char * buf)564 inline vm_obj_id_t vmb_get_objid(const char *buf) { return osrp4(buf); }
565 
566 /*
567  *   Portable binary property ID
568  */
569 const size_t VMB_PROP_ID = 2;
vmb_put_propid(char * buf,vm_obj_id_t prop)570 inline void vmb_put_propid(char *buf, vm_obj_id_t prop) { oswp2(buf, prop); }
vmb_get_propid(const char * buf)571 inline vm_prop_id_t vmb_get_propid(const char *buf) { return osrp2(buf); }
572 
573 /*
574  *   Portable data holder.  This is used to store varying-type data items;
575  *   for example, this is used to store an element in a list, or the value
576  *   of a property in an object.  This type of value stores a one-byte
577  *   prefix indicating the type of the value, and a four-byte area in
578  *   which the value is stored.  The actual use of the four-byte value
579  *   area depends on the type.
580  */
581 const size_t VMB_DATAHOLDER = 5;
582 
583 /* offset from a portable data holder pointer to the data value */
584 const size_t VMB_DH_DATAOFS = 1;
585 
586 /* store a portable dataholder from a vm_val_t */
587 void vmb_put_dh(char *buf, const vm_val_t *val);
588 
589 /* store a nil value in a portable dataholder */
vmb_put_dh_nil(char * buf)590 inline void vmb_put_dh_nil(char *buf) { buf[0] = VM_NIL; }
591 
592 /* store an object value in a portable dataholder */
vmb_put_dh_obj(char * buf,vm_obj_id_t obj)593 inline void vmb_put_dh_obj(char *buf, vm_obj_id_t obj)
594     { buf[0] = VM_OBJ; vmb_put_objid(buf + 1, obj); }
595 
596 /* store a property value in a portable dataholder */
vmb_put_dh_prop(char * buf,vm_prop_id_t prop)597 inline void vmb_put_dh_prop(char *buf, vm_prop_id_t prop)
598     { buf[0] = VM_PROP; vmb_put_propid(buf + 1, prop); }
599 
600 /* get the value portion of a vm_val_t from a portable dataholder */
601 void vmb_get_dh_val(const char *buf, vm_val_t *val);
602 
603 /* get the type from a portable dataholder */
vmb_get_dh_type(const char * buf)604 inline vm_datatype_t vmb_get_dh_type(const char *buf)
605     { return (vm_datatype_t)buf[0]; }
606 
607 /* get a vm_val_t from a portable dataholder */
vmb_get_dh(const char * buf,vm_val_t * val)608 inline void vmb_get_dh(const char *buf, vm_val_t *val)
609     { val->typ = vmb_get_dh_type(buf); vmb_get_dh_val(buf, val); }
610 
611 /* get an object value from a portable dataholder */
vmb_get_dh_obj(const char * buf)612 inline vm_obj_id_t vmb_get_dh_obj(const char *buf)
613     { return (vm_obj_id_t)osrp4(buf+1); }
614 
615 /* get an integer value from a portable dataholder */
vmb_get_dh_int(const char * buf)616 inline int32 vmb_get_dh_int(const char *buf)
617     { return (int32)osrp4(buf+1); }
618 
619 /* get a property ID value from a portable dataholder */
vmb_get_dh_prop(const char * buf)620 inline vm_prop_id_t vmb_get_dh_prop(const char *buf)
621     { return (vm_prop_id_t)osrp2(buf+1); }
622 
623 /* get a constant offset value from a portable dataholder */
vmb_get_dh_ofs(const char * buf)624 inline pool_ofs_t vmb_get_dh_ofs(const char *buf)
625     { return (pool_ofs_t)osrp4(buf+1); }
626 
627 
628 #endif /* VMTYPE_H */
629 
630