#ifndef SVALUE_H__ #define SVALUE_H__ 1 /*--------------------------------------------------------------------------- * Stack-value datatypes used by the virtual machine. * *--------------------------------------------------------------------------- * This file refers to, but does not define 'basic' types like mappings, * arrays or lambdas. */ #include "driver.h" #include "typedefs.h" /* --- Types --- */ /* --- union u: the hold-all type --- * * This union is used to hold the data referenced by a svalue. * It contains no type information, which must be provided by the * containing svalue. * * Lots of code assumes that '.number' is big enough to contain the whole * union regardless of its actual content, therefore it is of type 'p_int'. */ union u { string_t *str; /* T_STRING: pointer to the string. * T_SYMBOL: pointer to the (shared) symbol string. * T_(PROTECTED_)STRING_RANGE_LVALUE: the target string holding * the range. */ char *charp; /* T_(PROTECTED_)CHAR_LVALUE: pointer to the referenced character * (referenced from a T_LVALUE). */ p_int number; /* T_NUMBER: the number. */ object_t *ob; /* T_OBJECT: pointer to the object structure. * T_CLOSURE: efun-, simul_efun-, operator closures: the object * the closure is bound to. */ vector_t *vec; /* T_POINTER, T_QUOTED_ARRAY: pointer to the vector structure. * T_(PROTECTED_)POINTER_RANGE_LVALUE: the target vector holding * the range. */ #ifdef USE_STRUCTS struct_t *strct; /* T_STRUCT: pointer to the structure instance. */ #endif mapping_t *map; /* T_MAPPING: pointer to the mapping structure. * T_PROTECTOR_MAPPING: TODO: ??? */ lambda_t *lambda; /* T_CLOSURE: allocated closures: the closure structure. */ p_int mantissa; /* T_FLOAT: The mantissa (or at least one half of the float bitpattern). */ callback_t *cb; /* T_CALLBACK: A callback structure referenced from the stack * to allow proper cleanup during error recoveries. The interpreter * knows how to free it, but that's all. */ void *generic; /* Use read-only, this member is used to read the svalue generically * as "a pointer". It is mostly used in comparisons. */ svalue_t *lvalue; /* T_LVALUE: pointer to a (usually 'normal') svalue which * this lvalue references. Also, lvalues may be chained through * this pointer so that only the last lvalue points to the real * svalue, and the others point to the next lvalue in the chain. * This is necessary when lvalues are passed around as lfun args. * * For T_LVALUE this may point to one of the protector * structures (see interpret.c) which just 'happen' to have a svalue * as first element. The actual type of the protected svalue is given * by the .type of the referenced protector structure. * * When creating such a protected T_LVALUE, this .lvalue field is set * by assigning one of the following aliases: */ struct protected_lvalue *protected_lvalue; struct protected_char_lvalue *protected_char_lvalue; struct protected_range_lvalue *protected_range_lvalue; /* The following fields are used only in svalues referenced by * T_LVALUE svalues: */ void (*error_handler) (svalue_t *); /* T_ERROR_HANDLER: this function is * executed on a free_svalue(), receiving the T_ERROR_HANDLER svalue* * as argument. This allows the transparent implemention of cleanup * functions which are called even after runtime errors. In order * to pass additional information to the error_handler(), embed * the T_ERROR_HANDLER svalue into a larger structure (possible since * it has to be referenced by pointer) and let the error_handler() * execute the appropriate casts. */ }; /* --- struct svalue_s: the LPC data structure --- * * svalues ('stack values)' are used throughout the driver to hold LPC * values. One svalue consists of an instance of union u to hold the * actual value, and a type field describing the type of the value. * * Some values need specific, additional information (e.g. string values * distinguish shared from allocated strings), which is stored in the * union 'x'. * * The T_LVALUE type family is special in that the svalue instance does * not contain the actual value, but instead a reference to another * svalue. lvalues are necessary whenever existing svalue instance * are to be assigned. A special case are protected lvalues where the * svalue referenced by the T_LVALUE.u.lvalue is not the target svalue, * but instead a T_PROTECTED_xxx_LVALUE which then points to the target * and its protector. Similar, indexed and range-indexed lvalues don't * point to the data itself, but instead to one of the dedicated T_xxx_LVALUE * types which contain the necessary information to perform the indexing * operation; effectively limiting the access to the target value to * the view specified by the index/range bounds. * * T_LVALUEs are also used to reference meta data, like T_ERROR_HANDLER * svalues. TODO: Maybe they should get the T_META type. */ struct svalue_s { ph_int type; /* Primary type information */ union { /* Secondary type information */ ph_int exponent; /* Exponent of a T_FLOAT */ ph_int closure_type; /* Type of a T_CLOSURE */ ph_int quotes; /* Number of quotes of a quoted array or symbol */ ph_int num_arg; /* used by call_out.c to for vararg callouts */ ph_int extern_args; /* Callbacks: true if the argument memory was * allocated externally */ ph_int generic; /* For types without secondary type information, this is set to * a fixed value, usually (u.number << 1). * Also, this field is also used as generic 'secondary type field' * handle, e.g. when comparing svalues. */ } x; union u u; /* The value */ }; #define SVALUE_FULLTYPE(svp) ((p_int *)(svp)) /* Return a pointer to an integer with the primary and secondary type * information. */ /* TODO: Sanity test: sizeof struct { ph_int, ph_int } <= sizeof p_int */ /* struct svalue_s.type: Primary types. * There must be no more than 16 different primary types, as some * type tables (prolang.y::hook_type_map[] for example) store * them as bitflags in shorts. * TODO: Create these constants, plus the names in * TODO:: interpret.c::svalue_typename[] from a descriptive _spec file. */ #define T_INVALID 0x0 /* empty svalue */ #define T_LVALUE 0x1 /* a lvalue */ #define T_NUMBER 0x2 /* an integer number */ #define T_STRING 0x3 /* a string */ #define T_POINTER 0x4 /* a vector */ #define T_OBJECT 0x5 /* an object */ #define T_MAPPING 0x6 /* a mapping */ #define T_FLOAT 0x7 /* a float number */ #define T_CLOSURE 0x8 /* a closure */ #define T_SYMBOL 0x9 /* a symbol */ #define T_QUOTED_ARRAY 0xa /* a quoted array */ #ifdef USE_STRUCTS #define T_STRUCT 0xb /* a struct */ #endif /* USE_STRUCTS */ #define T_CHAR_LVALUE 0xc /* .u.string points to the referenced character in a string */ /* The following types must be used only in svalues referenced * by a T_LVALUE svalue. */ #define T_STRING_RANGE_LVALUE 0x0d /* TODO: ??? */ #define T_POINTER_RANGE_LVALUE 0x0e /* TODO: ??? */ #define T_PROTECTED_CHAR_LVALUE 0x0f /* A protected character lvalue */ #define T_PROTECTED_STRING_RANGE_LVALUE 0x10 /* A protected string range lvalue */ #define T_PROTECTED_POINTER_RANGE_LVALUE 0x11 /* A protected pointer/mapping range lvalue */ #define T_PROTECTED_LVALUE 0x12 /* A protected lvalue */ #define T_PROTECTOR_MAPPING 0x13 /* TODO: ??? */ #define T_CALLBACK 0x14 /* A callback structure referenced from the stack to allow * proper cleanup during error recoveries. The interpreter * knows how to free it, but that's all. */ #define T_ERROR_HANDLER 0x15 /* Not an actual value, this is used internally for cleanup * operations. See the description of the error_handler() member * for details. */ #define T_NULL 0x16 /* Not an actual type, this is used in the efun_lpc_types[] table * to encode the acceptance of '0' instead of the real datatype. */ #define T_MOD_SWAPPED 0x80 /* This flag is |-ed to the swapped-out type value if the value * data has been swapped out. */ /* T_CLOSURE secondary information. */ /* For closures of operators (internal machine codes), efuns and * simul_efuns, the x.closure_type is a negative number defining * which operator/efun/simul_efun to call. * The values given here are just the limits of the usable number * ranges. * The relations are: * Operator-closure index = CLOSURE_OPERATOR_OFFS + instruction index * Efun-closure index = CLOSURE_EFUN_OFFS + instruction index * Simul_efun index = CLOSURE_SIMUL_EFUN_OFFS + function index * Yes, the operator range can be overlaid onto the efun range without * collision, distinguishing them by the struct instr[].Default fields * (the 'closure' instruction does that to save space), but this way they * are easier to distinguish. * TODO: Note: Some code interprets these values as unsigned shorts. */ #define CLOSURE_OPERATOR (-0x1800) /* == 0xe800 */ #define CLOSURE_EFUN (-0x1000) /* == 0xf000 */ #define CLOSURE_SIMUL_EFUN (-0x0800) /* == 0xf800 */ #define CLOSURE_OPERATOR_OFFS (CLOSURE_OPERATOR & 0xffff) #define CLOSURE_EFUN_OFFS (CLOSURE_EFUN & 0xffff) #define CLOSURE_SIMUL_EFUN_OFFS (CLOSURE_SIMUL_EFUN & 0xffff) /* The other closure types are created from actual objects and lambdas, * the detailed information is stored in the u.lambda field. * The first types are 'just' references to existing lfuns and variables, * the others actually point to code. */ #define CLOSURE_LFUN 0 /* lfun in an object */ /* Code 1 was used for ALIEN_LFUNs - some mudlibs however rely on the * old code values. */ #define CLOSURE_IDENTIFIER 2 /* variable in this object */ #define CLOSURE_PRELIMINARY 3 /* Efun closure used in a static initialization */ #define CLOSURE_BOUND_LAMBDA 4 /* Bound unbound-lambda closure */ #define CLOSURE_LAMBDA 5 /* normal lambda closure */ #define CLOSURE_UNBOUND_LAMBDA 6 /* unbound lambda closure. */ #define CLOSURE_IDENTIFIER_OFFS 0xe800 /* When creating lfun/variable closures, the lfun/variable to bind * is given as unsigned number: * number < C_I_OFFS: number is index into function table * number >= C_I_OFFS: (number-C_I_OFFS) is index into variable table. */ /* Predicates operating on T_CLOSURE secondary information */ #define CLOSURE_MALLOCED(c) ((c) >= 0) /* TRUE if the closure was created at LPC runtime, ie is not an operator, * efun or simul_efun. */ #define CLOSURE_IS_LFUN(c) ((c) == CLOSURE_LFUN) /* TRUE if the closure is of the #' type. */ #define CLOSURE_REFERENCES_CODE(c) ((c) > CLOSURE_PRELIMINARY) /* TRUE if the closure may have or reference code. */ #define CLOSURE_HAS_CODE(c) ((c) > CLOSURE_BOUND_LAMBDA) /* TRUE if the closure points to actual code. */ #define CLOSURE_CALLABLE(c) ((c) >= CLOSURE_EFUN && (c) <= CLOSURE_LAMBDA) /* TRUE if the closure is callable. */ /* --- The primary types in bit-flag encoding --- * * This flag encoding is used for the runtime typetests. * Not all svalue types have a corresponding bitflag. */ #define TF_INVALID (1 << T_INVALID) #define TF_LVALUE (1 << T_LVALUE) #define TF_NUMBER (1 << T_NUMBER) #define TF_STRING (1 << T_STRING) #define TF_POINTER (1 << T_POINTER) #define TF_OBJECT (1 << T_OBJECT) #define TF_MAPPING (1 << T_MAPPING) #define TF_FLOAT (1 << T_FLOAT) #define TF_CLOSURE (1 << T_CLOSURE) #define TF_SYMBOL (1 << T_SYMBOL) #define TF_QUOTED_ARRAY (1 << T_QUOTED_ARRAY) #define TF_NULL (1 << T_NULL) #ifdef USE_STRUCTS #define TF_STRUCT (1 << T_STRUCT) #endif /* USE_STRUCTS */ #define TF_ANYTYPE (~0) /* This is used in the efun_lpc_types[] * table to encode the acceptance of any type. */ /* --- Float Support --- */ /* The driver uses a 48-Bit floating point format with a 32 bit mantissa * and a 16 bit exponent, stored in u.mantissa and x.exponent. This should * be well below all existing host floating point formats so that we get * the same accuracy on all platforms. * * The functions to encode/decode float numbers exist in two version, one * fast one using internal knowledge about how the compiler stores its * numbers, and a second portable one. To keep the implementation * transparent, the following macros/functions are defined: * * int FLOAT_FORMAT: * 0 for the portable format, 1 for the fast format. * Additional numbers may be defined for more formats. * * double READ_DOUBLE(struct svalue * sp) * Return the floating point number stored in *sp. * * long SPLIT_DOUBLE (double d, int * p) * Store the bytes 4..5 of to the address given in

, and * return the bytes 0..3 of as a long. * Used by the compiler to generate F_FLOAT instructions. * TODO: This code makes heave assumptions about data sizes and layout * TODO:: of integral types. * * unknown STORE_DOUBLE (struct svalue * dest, double d) * Store the float into the svalue *dest. * * STORE_DOUBLE_USED * Declaration of a local variable which STORE_DOUBLE needs. */ /* --- The fast format */ #if (defined(AMIGA) && defined(_DCC)) #define FLOAT_FORMAT_1 /* Faster routines, using inline and knowlegde about double format. * The exponent isn't actually in 'exponent', but that doesn't really matter * as long as the accesses are consistent. * * The DICE compiler for the Amiga lacks the ldexp() and frexp() functions, * therefore these functions here are the only way to get things done. * * STORE_DOUBLE doesn't do any rounding, but truncates off the least * significant bits of the mantissa that won't fit together with the exponent * into 48 bits. To compensate for this, we initialise the unknown bits of * the mantissa with 0x7fff in READ_DOUBLE . This keeps the maximum precision * loss of a store/read pair to the same value as rounding, while being faster * and being more stable. */ static INLINE double READ_DOUBLE(struct svalue *svalue_pnt) { double tmp; (*(long*)&tmp) = svalue_pnt->u.mantissa; ((short*)&tmp)[2] = svalue_pnt->x.exponent; ((short*)&tmp)[3] = 0x7fff; return tmp; } #define SPLIT_DOUBLE(double_value, int_pnt) (\ (*(int_pnt) = ((short*)&double_value)[2]),\ *((long*)&double_value)\ ) #define STORE_DOUBLE_USED #define STORE_DOUBLE(dest, double_value) (\ (dest)->u.mantissa = *((long*)&double_value),\ (dest)->x.exponent = ((short*)&double_value)[2]\ ) #endif /* --- The portable format, used if no other format is defined */ #ifndef STORE_DOUBLE #define FLOAT_FORMAT_0 #define READ_DOUBLE(svalue_pnt) ( ldexp( (double)((svalue_pnt)->u.mantissa) , \ (svalue_pnt)->x.exponent-31 ) ) /* if your machine doesn't use the exponent to designate powers of two, the use of ldexp in SPLIT_DOUBLE won't work; you'll have to mulitply with 32768. in this case */ #define SPLIT_DOUBLE(double_value, int_pnt) \ ( (long)ldexp( frexp( (double_value), (int_pnt) ), 31) ) #define STORE_DOUBLE_USED int __store_double_int_; #define STORE_DOUBLE(dest, double_value) (\ ((dest)->u.mantissa = SPLIT_DOUBLE((double_value), &__store_double_int_)),\ (dest)->x.exponent = (short)__store_double_int_\ ) #endif /* ifndef STORE_DOUBLE */ /* --- svalue macros --- */ #define addref_closure(sp, from) \ MACRO( svalue_t * p = sp; \ if (CLOSURE_MALLOCED(p->x.closure_type)) \ p->u.lambda->ref++; \ else \ (void)ref_object(p->u.ob, from); \ ) /* void addref_closure(sp, from): Add one ref to the closure svalue * in the function . */ /* void put_(sp, value): Initialise svalue *sp with value of . * 'sp' is evaluated several times and must point to an otherwise * empty svalue. If is a refcounted value, its refcount is * NOT incremented. * * void put_ref_(sp, value): Initialise svalue *sp with value * of . 'sp' is evaluated several times and must point to an * otherwise empty svalue. must be a refcounted value, and * its refcount is incremented. * * void push_(), void push_ref_() * As the put_() macros, but is incremented once first. * * As a rule, all arguments must not have sideeffects! * * The psh_ macros are not in use (yet) - they have been renamed from push_ * so that the first use stands out. * * TODO: Add push_xxx() macros, see MudOS:interpret.h. In general, get * TODO:: rid of the local sp/pc copies since they make error handling * TODO:: very difficult. * TODO: Also add 'adopt_()' and 'pop_()'macros, e.g. * TODO:: 'adopt_object(sp)', which * TODO:: retrieve the type from the *sp and set sp->type to T_INVALID. */ #define put_number(sp,num) \ ( (sp)->type = T_NUMBER, (sp)->u.number = (num) ) #define put_float(sp,fnum) \ do { STORE_DOUBLE_USED; (sp)->type = T_FLOAT; STORE_DOUBLE((sp), fnum); }while(0) #define put_ref_array(sp,arr) \ ( (sp)->type = T_POINTER, (sp)->u.vec = ref_array(arr) ) #define put_array(sp,arr) \ ( (sp)->type = T_POINTER, (sp)->u.vec = arr ) #ifdef USE_STRUCTS #define put_ref_struct(sp,st) \ ( (sp)->type = T_STRUCT, (sp)->u.strct = ref_struct(st) ) #define put_struct(sp,st) \ ( (sp)->type = T_STRUCT, (sp)->u.strct = st ) #endif /* USE_STRUCTS */ #define put_ref_mapping(sp,val) \ ( (sp)->type = T_MAPPING, (sp)->u.map = ref_mapping(val) ) #define put_mapping(sp,val) \ ( (sp)->type = T_MAPPING, (sp)->u.map = val ) #define put_ref_object(sp,val,from) \ ( (sp)->type = T_OBJECT, (sp)->u.ob = ref_object(val, from) ) #define put_object(sp,val) \ ( (sp)->type = T_OBJECT, (sp)->u.ob = val ) #define put_ref_valid_object(sp,val,from) \ ( ((val) && !((val)->flags & O_DESTRUCTED)) \ ? (put_ref_object(sp,val,from), 0) : put_number(sp, 0)) #define put_valid_object(sp,val) \ ( ((val) && !((val)->flags & O_DESTRUCTED)) \ ? (put_object(sp,val), 0) : put_number(sp, 0)) #define put_ref_string(sp,val) \ ( (sp)->type = T_STRING, (sp)->u.str = ref_mstring(val) ) #define put_string(sp,val) \ ( (sp)->type = T_STRING, (sp)->u.str = val ) #define put_callback(sp,val) \ ( (sp)->type = T_CALLBACK, (sp)->u.cb = val ) #define push_number(sp,num) \ ( (sp)++, put_number(sp, num) ) #define push_float(sp,fnum) \ ( (sp)++, put_float(sp, fnum) ) #define push_ref_array(sp,arr) \ ( (sp)++, put_ref_array(sp, arr) ) #define push_array(sp,arr) \ ( (sp)++, put_array(sp, arr) ) #ifdef USE_STRUCTS #define push_ref_struct(sp,st) \ ( (sp)++, put_ref_struct(sp, st) ) #define push_struct(sp,st) \ ( (sp)++, put_struct(sp, st) ) #endif /* USE_STRUCTS */ #define psh_ref_mapping(sp,val) \ ( (sp)++, put_ref_mapping(sp,val) ) #define push_mapping(sp,val) \ ( (sp)++, put_mapping(sp,val) ) #define push_ref_object(sp,val,from) \ ( (sp)++, put_ref_object(sp,val,from) ) #define push_object(sp,val) \ ( (sp)++, put_object(sp,val) ) #define push_ref_valid_object(sp,val,from) \ ( (sp)++, put_ref_valid_object(sp,val,from) ) #define push_valid_object(sp,val) \ ( (sp)++, put_valid_object(sp,val) ) #define push_ref_string(sp,val) \ ( (sp)++, put_ref_string(sp,val) ) #define push_string(sp,val) \ ( (sp)++, put_string(sp,val) ) #define push_c_string(sp,txt) \ ( (sp)++, put_c_string(sp, txt) ) #define push_c_n_string(sp,txt,len) \ ( (sp)++, put_c_n_string(sp, txt, len) ) #define push_callback(sp,val) \ ( (sp)++, put_callback(sp,val) ) /* The following macros implement the dynamic cost evaluations: * * DYN_STRING_COST(l): increase eval_cost depending on string length . * DYN_ARRAY_COST(l): increase eval_cost depending on array length . * DYN_MAPPING_COST(l): increase eval_cost depending on mapping length . * * Covered so far are: * F_ALLOCATE, F_ADD, F_ADD_EQ, F_VOID_ADD_EQ, F_MULTIPLY, * F_MULT_EQ of strings * * TODO: Expand this to all datatypes and sizes. */ #if defined(DYNAMIC_COSTS) #define DYN_STRING_COST(l) eval_cost += (l) / 1000; #define DYN_ARRAY_COST(l) eval_cost += (l) / 1000; #define DYN_MAPPING_COST(l) eval_cost += (l) / 1000; #else #define DYN_STRING_COST(l) #define DYN_ARRAY_COST(l) #define DYN_MAPPING_COST(l) #endif /* --- Prototypes (in interpret.c) --- */ extern void free_string_svalue(svalue_t *v); extern void free_object_svalue(svalue_t *v); extern void zero_object_svalue(svalue_t *v); extern void free_svalue(svalue_t *v); extern void assign_svalue_no_free(svalue_t *to, svalue_t *from); extern void assign_svalue(svalue_t *dest, svalue_t *v); extern void transfer_svalue_no_free(svalue_t *dest, svalue_t *v); extern void transfer_svalue(svalue_t *dest, svalue_t *v); extern svalue_t *pop_n_elems (int n, svalue_t *sp); #endif /* SVALUE_H__ */