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