ldmud-3.2.9/doc/
ldmud-3.2.9/doc/efun/
ldmud-3.2.9/mud/
ldmud-3.2.9/mud/heaven7/
ldmud-3.2.9/mud/heaven7/lib/
ldmud-3.2.9/mud/lp-245/
ldmud-3.2.9/mud/lp-245/banish/
ldmud-3.2.9/mud/lp-245/doc/
ldmud-3.2.9/mud/lp-245/doc/examples/
ldmud-3.2.9/mud/lp-245/doc/sefun/
ldmud-3.2.9/mud/lp-245/log/
ldmud-3.2.9/mud/lp-245/obj/Go/
ldmud-3.2.9/mud/lp-245/players/lars/
ldmud-3.2.9/mud/lp-245/room/death/
ldmud-3.2.9/mud/lp-245/room/maze1/
ldmud-3.2.9/mud/lp-245/room/sub/
ldmud-3.2.9/mud/lp-245/secure/
ldmud-3.2.9/mud/morgengrauen/
ldmud-3.2.9/mud/morgengrauen/lib/
ldmud-3.2.9/mud/sticklib/
ldmud-3.2.9/mud/sticklib/src/
ldmud-3.2.9/mudlib/uni-crasher/
ldmud-3.2.9/pkg/
ldmud-3.2.9/pkg/debugger/
ldmud-3.2.9/pkg/diff/
ldmud-3.2.9/pkg/misc/
ldmud-3.2.9/src/autoconf/
ldmud-3.2.9/src/bugs/
ldmud-3.2.9/src/bugs/MudCompress/
ldmud-3.2.9/src/bugs/b-020916-files/
ldmud-3.2.9/src/bugs/doomdark/
ldmud-3.2.9/src/bugs/ferrycode/ferry/
ldmud-3.2.9/src/bugs/ferrycode/obj/
ldmud-3.2.9/src/bugs/psql/
ldmud-3.2.9/src/done/
ldmud-3.2.9/src/done/order_alist/
ldmud-3.2.9/src/done/order_alist/obj/
ldmud-3.2.9/src/done/order_alist/room/
ldmud-3.2.9/src/gcc/
ldmud-3.2.9/src/gcc/2.7.0/
ldmud-3.2.9/src/gcc/2.7.1/
ldmud-3.2.9/src/hosts/
ldmud-3.2.9/src/hosts/GnuWin32/
ldmud-3.2.9/src/hosts/amiga/NetIncl/
ldmud-3.2.9/src/hosts/amiga/NetIncl/netinet/
ldmud-3.2.9/src/hosts/amiga/NetIncl/sys/
ldmud-3.2.9/src/hosts/i386/
ldmud-3.2.9/src/hosts/msdos/byacc/
ldmud-3.2.9/src/hosts/msdos/doc/
ldmud-3.2.9/src/hosts/os2/
ldmud-3.2.9/src/hosts/win32/
ldmud-3.2.9/src/util/
ldmud-3.2.9/src/util/erq/
ldmud-3.2.9/src/util/indent/hosts/next/
ldmud-3.2.9/src/util/xerq/
ldmud-3.2.9/src/util/xerq/lpc/
ldmud-3.2.9/src/util/xerq/lpc/www/
#ifndef EXEC_H__
#define EXEC_H__ 1

/*---------------------------------------------------------------------------
 * Types and Macros used to describe and store compiled programs.
 *
 *---------------------------------------------------------------------------
 * Basics
 * ------
 *
 * LPMud distinguished between objects and their programs. Objects hold
 * the live data which is unique to each of them. Programs otoh are shared
 * among objects through inheritance or cloning and hold the program code,
 * variable specification and similar information. Due to the separation
 * programs maintain their own reference count, and it is this separation
 * which allows the separate swapping of programs and variables.
 *
 * A compiled program consists of several data blocks which are allocated
 * together in one memory block. Not only this is advantageous for the
 * memory locality, but also simplifies swapping as all data for a program
 * can be read or written in one go, with a simple relocation computation
 * on the pointers involved.
 *
 * The blocks of a program in correct allocation order are these:
 *
 *   struct program_s: The basic structure of the program, with pointers to
 *       the other data blocks.
 *
 *   bytecode program[]: the actual bytecode for the program.
 *
 *   unsigned short function_names[]: Lookup table of function name index
 *       to the offset of the function within the functions[] table.
 *       function_names[] is allocated together with and right after the
 *       bytecode. If a function redefines an inherited function, this table
 *       points to the redefinition.
 *       Read 'function_name' as 'function_index'.
 *
 *   uint32 functions[]: One word for each function, giving the type
 *       and the offset within the program[] codearray. Additional information
 *       for each function is embedded into the program code.
 *       If a program uses undefined functions, the compiler generates
 *       dummy functions.
 *       Through inheritance and cross-definition these functions can
 *       be resolved on a higher level.
 *
 *   char *strings[]: An array of pointers to the string literals (stored
 *       as shared strings) used by the program. This way the program can
 *       use the strings simply by an (easily swappable) index. The compiler
 *       makes sure that each string is unique.
 *
 *       When a program is swapped, the reference counts to these strings are
 *       not removed so that the string literals stay in memory all the time.
 *       This saves time on swap-in, and the string sharing saves more memory
 *       than swapping might gain.
 *
 *   struct variable_s variable_names[]: an array describing all variables
 *       with type and name, inherited or own. When a program is swapped, the
 *       reference counts to these strings are not removed so that the
 *       string literals stay in memory all the time.
 *
 *   struct inherit inherit[]: an array describing all inherited programs.
 *
 *   vartype_t argument_types[]: (only with #pragma save_types)
 *       The types of all function arguments of the program in the
 *       order they were encountered.
 *
 *   unsigned short type_start[]: (only with #pragma save_types)
 *       Lookup table [.num_function_names] function index
 *       -> index in .argument_types[], which gives the index of
 *       the first argument type. If this index is INDEX_START_NONE,
 *       the function has no type information.
 *
 * The linenumber information is allocated separately from the main program
 * block so that it can be swapped separately more easily. The struct
 * linenumbers_s is allocated to size and holds the line number information
 * as an array of bytecodes:
 *
 *   include_t includes[]: an array listing all include files used
 *       to compile the program, in the order they were encountered.
 *       When a program is swapped, the reference counts to these strings
 *       are not removed so that the string literals stay in memory all
 *       the time.
 *
 *   bytecode_t line_numbers[]: the line number information,
 *       encoded in a kind of delta compression. When a program
 *       is swapped in, the line numbers are allocated separately
 *       and not swapped in until needed.
 *
 * TODO: If the program_s is allocated separately from the rest,
 * TODO:: we could swap even if a program is used by clones.
 * TODO:: However, find out how often that would be useful.
 *
 *
 * Bytecode
 * --------
 *
 * As the name conveys, the LPC programs are compiled into a bytecode,
 * assuming 8-Bit-Bytes. Since we have more than 256 opcodes, the less
 * often used instructions are encoded in two-byte opcodes: the highbyte
 * is (instruction / 128), the low byte (instruction % 128). The resulting
 * values for the highbyte are 'coincidentally' the values of the prefix
 * opcodes F_ESCAPE, F_TEFUN and F_VEFUN. The divisor 128 is symbolically
 * defined by F_ESCAPE_BITS below.
 *
 * To achieve platform independance, the driver does not operate directly
 * with 'char's and 'char *'s, but instead with 'bytecode_t' and
 * 'bytecode_p's. Combined with some macros this allows the implementation
 * even on platforms with CHAR_BITs != 8.
 * TODO: This support is far from complete/comprehensive, and some values
 * TODO:: in the bytecode are stored in host-sizes and -layouts.
 *
 * The bytecode itself is divided into functions: the code for every
 * function is (except for absolute jumps) selfcontained and prepended
 * by a header holding the name of the function, and the number and types
 * of expected arguments. The advantage is that for a given function
 * the driver does not need to know if it is part of a program or a
 * lambda closure compiled at runtime: in both cases all necessary
 * information about the function is right where its code is.
 *
 * The maximum size of a program is limited by the biggest offset
 * that can be stored in the 'functions' array, currently 1 MByte.
 * A lot of internal offset counters are shorts even, though so far
 * this never caused a problem.
 *
 *
 * Inheritance
 * -----------
 *
 * Inheritance is handled such that all the variable and function
 * information of the inherited programs are appended to the programs
 * own variables and functions. The inherit entries then give away
 * where in the programs tables the inherited information can be found.
 *
 * Imagine: A and B inherit nothing, C inherits A, D inherits C and B.
 * Then the function and variable blocks are set up like this ('-funs' are
 * real function entries, '-desc' are pointers to inherit descriptors):
 *
 *     A-fblock: A-funs  (B similar)
 *     A-vblock: A-vars  (B similar)
 *
 *     C-fblock: C-funs A-desc
 *     C-vblock: C-vars A-vars
 *
 *     D-fblock: D-funs (C-desc A-desc) B-desc
 *     D-vblock: D-vars  C-vars A-vars  B-vars
 *       and fblock has the twist that (C-desc A-desc) together are
 *       considered being 'the' C-desc block.
 *
 * If a program is inherited virtually several times, each virtual inherit
 * gets its own struct inherit; the variable block however is reserved
 * just once. To mark the virtual inherit, the variable types receive the
 * modifier TYPE_MOD_VIRTUAL. During the inheritance of the compiler, functions
 * from non-virtual inherits temporarily receive the flag
 * NON_VIRTUAL_OFFSET_TAG in their variable indices, too.
 *
 * However, accesses to virtually inherited variables only use the first
 * inherit instance - if the variable index in the child's program code indexes
 * the variable block associated with a later instance, the driver finds
 * the first instance by comparing the .prog entries and uses the 'real'
 * variable associated with this first instance.
 *
 * The compiler places virtual variables always at the begin of the variable
 * block and limits the number to 256.
 *
 * TODO: Find a way to avoid the virtual var searching - either by encoding the
 * TODO:: inherit index in the code, or by adding a ref to the 'first
 * TODO:: instance' to this structure. See interpret.c:find_virtual_value().
 *
 * Old Comments:
 * -------------
 * When an object is compiled with type testing (#pragma strict_types), all
 * types are saved of the arguments for that function during compilation.
 * If the #pragma save_types is specified, then the types are saved even
 * after compilation, to be used when the object is inherited.
 *---------------------------------------------------------------------------
 */

#include "driver.h"
#include "typedefs.h"


/* --- Type Constants ---
 *
 * These constants and types are used to encode types and visibility
 * of variables and functions. They are used by both the compiler and
 * the interpreter.
 *
 * You'll find the "TYPE_MOD_" visibility constants below with the
 * function flags.
 */

typedef unsigned short  vartype_t;   /* Basic: just the datatype */
typedef uint32          fulltype_t;  /* Full: type and visibility */


/* Basic type values */

#define TYPE_UNKNOWN        0   /* This type must be casted */
#define TYPE_NUMBER         1
#define TYPE_STRING         2
#define TYPE_VOID           3
#define TYPE_OBJECT         4
#define TYPE_MAPPING        5
#define TYPE_FLOAT          6
#define TYPE_ANY            7   /* Will match any type */
#define TYPE_CLOSURE        8
#define TYPE_SYMBOL         9
#define TYPE_QUOTED_ARRAY  10

#define TYPEMAP_SIZE       11   /* Number of types */

/* Flags, or'ed on top of the basic type */

#define TYPE_MOD_POINTER    0x0040  /* Pointer to a basic type */
#define TYPE_MOD_REFERENCE  0x0080  /* Reference to a type */

#define TYPE_MOD_MASK   0x000000ff
  /* Mask for basic type and flags.
   */

#define PRIMARY_TYPE_MASK (TYPE_MOD_MASK & ~TYPE_MOD_REFERENCE & ~TYPE_MOD_POINTER)
  /* Mask to retriefe the basic type value.
   */

#define TYPE_MOD_RMASK  (TYPE_MOD_MASK & ~TYPE_MOD_REFERENCE)
  /* Mask to delete TYPE_MOD_REFERENCE and the visibility mods from
   * a type value.
   */

#define VIRTUAL_VAR_TAG 0x4000
  /* Flag set in virtual variables, also interpreted as offset
   * in the variable index for virtual variables.
   */


/* --- struct instr_s: description of stackmachine instructions ---
 *
 * Stackmachine instructions are both 'internal' codes with no external
 * representation, as well as efuns.
 *
 * The information about all instructions is collected in the table
 * instrs[] which is indexed by the bytecode of the instructions.
 *
 * The table is declared in instrs.h and defined in the file efun_defs.c
 * both of which are created by make_func from the func_spec file.
 * The table is compiled into the lexer module and exported from there.
 */

struct instr_s
{
    short max_arg;    /* Maximum number of arguments, -1 for '...' */
    short min_arg;
      /* Minimum number of arguments.
       * The number 0 marks incallable closures: instructions which
       * are used as syntactic markers in lambda closures, but by
       * themselves can't be executed.
       */
    char  type[2];    /* Types of arguments 1 and 2, using the svalue codes */
    short Default;
      /* An efun to use as default value for last argument.
       * > 0: index into instrs[] to the efun to use.
       *   0: no default value.
       *  -1: this whole entry describes an internal stackmachine code,
       *      not a normal efun (an 'operator' in closure lingo).
       */
    vartype_t ret_type;  /* The return type used by the compiler. */
    short arg_index;     /* Indexes the efun_arg_types[] array (see make_func). */
    char *name;          /* The printable name of the instruction. */
    char *deprecated;    /* Usually NULL, for deprecated efuns this is
                          * the warning message to print.
                          */
};


/* --- Byte code ---
 *
 * The program code is stored as byte code. The following definitions
 * and macros allow its implementation even on platforms with more than
 * 8 bits per character.
 * TODO: This portability is far from complete, and not used everywhere,
 * TODO:: not even in the compiler.
 *
 *   bytecode_t: an integral type holding the numbers 0..255.
 *   bytecode_p: an integral type addressing a bytecode. This need not
 *               be a pointer.
 *
 *   bytecode_t GET_CODE(bytecode_p p)
 *   bytecode_t LOAD_CODE(bytecode_p p)
 *     Return the bytecode from *p, the LOAD_ variant then increments p.
 *
 *   void PUT_CODE(bytecode_p p, bytecode_t c)
 *   void STORE_CODE(bytecode_p p, bytecode_t c)
 *   void RSTORE_CODE(bytecode_p p, bytecode_t c)
 *     Store the bytecode c in *p, the STORE_ variant then increments p.
 *     The RSTORE_ variant pre-decrements p.
 *
 *    char GET_INT8(p) , LOAD_INT8(p)
 *   uchar GET_UINT8(p), LOAD_UINT8(p)
 *     Return the 8-Bit (unsigned) int stored at <p>, the LOAD_ variants
 *     then increment <p>.
 *
 *   void PUT_INT8(p, char c),   STORE_INT8(p, char c)
 *   void PUT_UINT8(p, uchar c), STORE_UINT8(p, uchar c)
 *     Store the 8-Bit (unsigned) int <c> into <p>, the STORE_ variants
 *     then increment <p>.
 *
 *   void GET_SHORT ([unsigned] short d, bytecode_p p)
 *   void LOAD_SHORT([unsigned] short d, bytecode_p p)
 *     Load the (unsigned) short 'd' stored at <p>, the LOAD_ variant
 *     then increments <p>.
 *
 *   void PUT_SHORT (bytecode_p p, [unsigned] short d)
 *   void STORE_SHORT(bytecode_p p, [unsigned] short d)
 *   void RSTORE_SHORT(bytecode_p p, [unsigned] short d)
 *     Store the (unsigned) short <d> into <p>, the STORE_ variant
 *     then increments <p>. The RSTORE_ variant pre-decrements <p>.
 *
 *   void GET_INT16 ([unsigned] int16 d, bytecode_p p)
 *   void LOAD_INT16([unsigned] int16 d, bytecode_p p)
 *     Load the (unsigned) int16 'd' stored at <p>, the LOAD_ variant
 *     then increments <p>.
 *
 *   void LOAD_INT32([unsigned] int32 d, bytecode_p p)
 *     Load the (unsigned) in32 'd' stored at <p>, then increment <p>.
 */

#define F_ESCAPE_BITS 7
   /* The number of bits the lowbyte of a dual-byte-opcode can hold
    */

#if CHAR_BIT == 8
typedef unsigned char   bytecode_t;
typedef bytecode_t    * bytecode_p;

#define GET_CODE(p)      (*(p))
#define LOAD_CODE(p)     (*(p)++)

#define PUT_CODE(p,c)    (*(p) = (c))
#define STORE_CODE(p,c)  (*(p)++ = (c))
#define RSTORE_CODE(p,c)  (*--(p) = (c))

/* TODO: all these casts yield rvalues, so they shouldn't compile
 * TODO:: (and on xlc on AIX some of them indeed don't).
 */
#define GET_UINT8(p)     (*((unsigned char *)(p)))
#define GET_INT8(p)      (*((signed char *)(p)))
#define LOAD_UINT8(p)    (*((unsigned char *)(p)++))
#define LOAD_INT8(p)     (*((signed char *)(p)++))

#define PUT_UINT8(p,c)   (*((unsigned char *)(p)) = (c))
#define PUT_INT8(p,c)    (*((signed char *)(p)) = (c))
#define STORE_UINT8(p,c) (*((unsigned char *)(p)++) = (c))
#define STORE_INT8(p,c)  (*((signed char *)(p)++) = (c))

/* TODO: These generic mem-macros should go into a macros.h. See also
 * TODO:: how mudos does it.
 * Note: the lowlevel BYTE macros can't use MACRO(), since this is
 * needed on the next abstraction level, and a macro can't be nested
 * into itself.
 */
#define LOAD_2BYTE(d,p) ( ((char *)&(d))[0] = *(char *)(p)++, \
                          ((char *)&(d))[1] = *(char *)(p)++)

#define GET_2BYTE(d,p)  ( ((char *)&(d))[0] = ((char *)(p))[0], \
                          ((char *)&(d))[1] = ((char *)(p))[1] )

#define STORE_2BYTE(p,d) do {\
                             unsigned char * _q, ** _qq; \
                             _q = (unsigned char *)(p); \
                             _qq = (unsigned char **)&(p); \
                             _q[0] = ((unsigned char *)&(d))[0]; \
                             _q[1] = ((unsigned char *)&(d))[1]; \
                             *_qq += 2; \
                         } while(0)

#define RSTORE_2BYTE(p,d) do {\
                             unsigned char * _q, ** _qq; \
                             _q = (unsigned char *)(p); \
                             _qq = (unsigned char **)&(p); \
                             _q[-2] = ((unsigned char *)&(d))[0]; \
                             _q[-1] = ((unsigned char *)&(d))[1]; \
                             *_qq -= 2; \
                         } while(0)

#define PUT_2BYTE(p,d)  ( ((char *)(p))[0] = ((char *)&(d))[0], \
                          ((char *)(p))[1] = ((char *)&(d))[1] )

#define LOAD_4BYTE(d,p) ( ((char *)&(d))[0] = *(char *)(p)++, \
                          ((char *)&(d))[1] = *(char *)(p)++, \
                          ((char *)&(d))[2] = *(char *)(p)++, \
                          ((char *)&(d))[3] = *(char *)(p)++ )

#define GET_4BYTE(d,p)  ( ((char *)&(d))[0] = ((char *)(p))[0], \
                          ((char *)&(d))[1] = ((char *)(p))[1], \
                          ((char *)&(d))[2] = ((char *)(p))[2], \
                          ((char *)&(d))[3] = ((char *)(p))[3] )

#define STORE_4BYTE(p,d) ( *(unsigned char *)(p)++ = ((char *)&(d))[0], \
                           *(unsigned char *)(p)++ = ((char *)&(d))[1], \
                           *(unsigned char *)(p)++ = ((char *)&(d))[2], \
                           *(unsigned char *)(p)++ = ((char *)&(d))[3] )

#define PUT_4BYTE(p,d)  ( ((char *)(p))[0] = ((char *)&(d))[0], \
                          ((char *)(p))[1] = ((char *)&(d))[1], \
                          ((char *)(p))[2] = ((char *)&(d))[2], \
                          ((char *)(p))[3] = ((char *)&(d))[3] )


#define GET_SHORT(d,p)    GET_2BYTE(d,p)
#define LOAD_SHORT(d,p)   LOAD_2BYTE(d,p)
#define PUT_SHORT(p,d)    MACRO(unsigned short _us = (unsigned short)d; \
                                PUT_2BYTE(p,_us);)
#define STORE_SHORT(p,d)  MACRO(unsigned short _us = (unsigned short)d; \
                               STORE_2BYTE(p,_us);)
#define RSTORE_SHORT(p,d) MACRO(unsigned short _us = (unsigned short)d; \
                               RSTORE_2BYTE(p,_us);)
  /* TODO: This assumes sizeof(short) == 2. */

#define LOAD_INT16(d,p) LOAD_2BYTE(d,p)
#define LOAD_INT32(d,p) LOAD_2BYTE(d,p)
#define GET_INT32(d,p)  GET_4BYTE(d,p)
#define PUT_INT32(p,d)  PUT_4BYTE(p,d)

#endif

#ifndef GET_CODE
#  error "No bytecode type defined."
#endif


/* --- Function flags ---
 *
 * Programs know about their functions from an array of uint32s which hold
 * the flags for the function and, in one field in the low bits, the
 * address of the code.
 *
 * Some flags (TYPE_MOD_*) are also used for variable types.
 *
 * Using a bitfield is tempting, but generates inefficient code due to the
 * lack of a real 'bool' datatype.
 */

typedef fulltype_t funflag_t;  /* Function flags */

#define NAME_INHERITED      0x80000000  /* defined by inheritance */
#define TYPE_MOD_STATIC     0x40000000  /* Static function or variable    */
#define TYPE_MOD_NO_MASK    0x20000000  /* The nomask => not redefineable */
#define TYPE_MOD_PRIVATE    0x10000000  /* Can't be inherited             */
#define TYPE_MOD_PUBLIC     0x08000000  /* Force inherit through private  */
#define TYPE_MOD_VARARGS    0x04000000  /* Used for type checking         */
#define NAME_INITIALIZED    0x04000000  /* only used for variables        */
#define TYPE_MOD_VIRTUAL    0x02000000  /* can be re- and cross- defined  */
#define TYPE_MOD_PROTECTED  0x01000000  /* cannot be called externally    */
#define TYPE_MOD_XVARARGS   0x00800000  /* accepts optional arguments     */
#define TYPE_MOD_NOSAVE     0x00400000  /* vars: can't be saved           */

#define FUNSTART_MASK       0x000fffff
  /* Function not inherited: unsigned address of the function code relative
   * to the begin of the program block (struct program_s->program).
   */

#define NAME_CROSS_DEFINED  0x00080000
  /* Two functions with the same name inherited from A and B into C.
   * The compiler usually prefers A, and the value 'flags & INHERIT_MASK'
   * (in bias-0x20000 representation) stored as B.offset.func is the
   * difference between to the real functions index (also see below).
   * A special use is A uses a function from B. The function is marked
   * in A as undefined, but the compiler will use cross-defining in C
   * to resolve the function calls in A to call the function in B.
   */

#define INHERIT_MASK        0x0003ffff
  /* Function inherited: unsigned index of the parent program descriptor
   * in the struct program_s->inherit[] array. In the parent program, this
   * function may again be inherited.
   * Function crossdefined: signed difference (in bias-0x20000 notation)
   * from the current function index to the real function index
   * (real = this + offset).
   */


#define NAME_HIDDEN         0x00000800 /* Not visible for inheritance    */
#define NAME_PROTOTYPE      0x00000400 /* Defined by a prototype only    */
#define NAME_UNDEFINED      0x00000200 /* Not defined yet                */
#define NAME_TYPES_LOST     0x00000100 /* inherited, no save_types       */

#define CROSSDEF_NAME_OFFSET(flags) \
     (((flags) & INHERIT_MASK) - ((INHERIT_MASK + 1) >> 1))

  /* For a given NAME_CROSS_DEFINED function, extract the difference to
   * the real functions index from the flags.
   */

#define GET_CROSSDEF_OFFSET(value) \
     ((value) - ((INHERIT_MASK + 1) >> 1))

  /* The index difference <value> in bias-notation is converted back into
   * the real number. The difference to CROSSDEF_NAME_OFFSET is that <value>
   * is supposed to be the plain number, not a real function flags word.
   */

#define MAKE_CROSSDEF_OFFSET(value) \
     ((value) + ((INHERIT_MASK + 1) >> 1))

  /* Convert the index difference <value> into the bias-notation for storage
   * in the function flags.
   */

#define MAKE_CROSSDEF_ROFFSET(value) \
     ((value) - ((INHERIT_MASK + 1) >> 1))

  /* Convert the reverse index difference <value> into the bias-notation for
   * storage in the function flags.
   * TODO: What is this exactly?
   */

/* --- Function header ---
 *
 * The bytecode for every function is preceeded by a header with the name
 * and information about arguments and types:
 *
 * struct fun_hdr {
 *     shared char * name_of_function; (4 Bytes)
 *     byte          return_type;        (1 Byte)
 * --> byte          number_formal_args; (1 Byte)
 *         Bit 7: set if the function has a 'varargs' argument
 * TODO: some code makes use of the fact that this makes the number negative
 *         Bit 6..0: the number of arguments
 *     byte          number_local_vars;  (1 Byte)
 *         This includes the svalues needed for the break stack for
 *         switch() statements.
 *     bytecode_t    opcode[...]
 * }
 *
 * The function address given in the program's function block points to
 * .number_formal_args.
 *
 * Since structs introduce uncontrollable padding, access of all fields
 * is implemented using macros taking the 'function address', typedef'd
 * as fun_hdr_p, as argument and evaluate to the desired value.
 *
 * WARNING: if ALIGN_FUNCTIONS is not defined, the name_of_function pointer
 *   is not aligned properly to be directly used as a pointer!
 *
 * Note: Changes here can affect the struct lambda layout and associated
 *       constants.
 * TODO: the other fields should have proper types, too.
 * TODO: the whole information should be in a table, and not in the
 * TODO:: bytecode. See struct program_s.
 */

typedef bytecode_p fun_hdr_p;
  /* TODO: Make this a void* for now? */

#define SIMUL_EFUN_FUNSTART ((bytecode_p) -1)
  /* Special value used for inter_pc and funstart to mark simul_efuns
   * for dump_trace().
   * TODO: Invent something similar for efun/operator closures?
   */

#define EFUN_FUNSTART ((bytecode_p) -2)
  /* Special value used for funstart to mark efuns for dump_trace.
   */


#define FUNCTION_NAMEP(p)     ((void*)((char *)p - sizeof(char) - sizeof(char *)))
#define FUNCTION_TYPE(p)      (*((unsigned char *)((char *)p - sizeof(char))))
#define FUNCTION_NUM_ARGS(p)  EXTRACT_SCHAR((char *)p)
#define FUNCTION_NUM_VARS(p)  (*((unsigned char *)((char *)p + sizeof(char))))
#define FUNCTION_CODE(p)      ((bytecode_p)((unsigned char *)p + 2* sizeof(char)))
#define FUNCTION_FROM_CODE(p) ((fun_hdr_p)((unsigned char *)p - 2* sizeof(char)))

#define FUNCTION_HDR_SIZE     (sizeof(char*) + 3)


/* --- struct variable_s: description of one variable
 *
 * This structure describes one variable, inherited or own.
 * The type part of the .flags is used just by the compiler.
 */

struct variable_s
{
    char       *name;   /* Name of the variable (shared string) */
    fulltype_t  flags;
      /* Flags and type of the variable.
       * If a variable is inherited virtually, the function flag
       * TYPE_MOD_VIRTUAL is or'ed on this.
       */
};


/* --- struct inherit: description of one inherited program
 *
 * The information about inherited programs ("objects") for a given
 * program is stored in an array of these structure, and the inherited
 * programs are accessed from the childs' program code by indexing this array.
 */

struct inherit_s
{
    program_t *prog;  /* Pointer to the inherited program */
    unsigned short function_index_offset;
      /* Offset of the inherited program's function block within the
       * inheriting program's function block.
       */
    unsigned short variable_index_offset;
      /* Offset of the inherited program's variables block within the
       * inheriting program's variable block.
       * The NON_VIRTUAL_OFFSET_TAG marks the variables of non-virtual
       * inherits temporarily during compiles.
       */
    unsigned short inherit_type;            /* Type of inherit */

#   define INHERIT_TYPE_NORMAL      0x0000  /* Type: Normal inherit */
#   define INHERIT_TYPE_VIRTUAL     0x0001  /* Type: Virtual inherit */
#   define INHERIT_TYPE_EXTRA       0x0002  /* Type: Extra inherit added by
                                             * copy_variables()
                                             */
#   define INHERIT_TYPE_DUPLICATE   0x0004  /* Flag: Duplicate virtual inherit */
    unsigned short inherit_depth;           /* Depth of inherit */
};


/* --- struct include_s: description of one include file
 *
 * This structure describes one include file used to compile the
 * program.
 */

struct include_s
{
    char *name;
      /* Name (shared string) as it was found in the program. First and last
       * character are the delimiters - either "" or <>.
       */

    char *filename;
      /* Actual filename (shared string) of the include file, in compat mode
       * without leading slash.
       */
    int   depth;
      /* The absolute value is the include depth, counting from 1 upwards.
       * If the include did not generate code, the value is stored negative.
       */
};


/* --- struct program_s: the program head structure
 *
 * This structure is actually just the head of the memory block
 * with all the programs data.
 */

/* TODO: We seem to need a datatype for program offsets (right now: unsigned short).
 * TODO:: It shows up in quite a lot of places.
 * TODO: Replace the on program_s structure with a header/data couple. The
 * TODO:: header keeps vars likes refs, blueprint, swap_num, *data; and the
 * TODO:: data keeps the static bytecodes and similar. This way we can swap
 * TODO:: the program even for clones.
 */

struct program_s
{
    p_int           ref;           /* Reference count */
    p_int           total_size;
      /* Total size of the memory block allocated for this program.
       */
#ifdef DEBUG
    p_int           extra_ref;     /* Used to verify ref count */
#endif
    bytecode_p      program;       /* The binary instructions */
    char           *name;
      /* Name of file that defined prog (allocated, no leading '/',
       * but a trailing '.c')
       */
    object_t       *blueprint;
      /* Counted pointer to the (possibly destructed) blueprint object,
       * or NULL if the blueprint has been destructed in a previous cycle.
       */
    int32           id_number;
      /* The id-number is unique among all programs and used to store
       * information for this program without actually pointing to
       * the structure.
       */
    mp_int          load_time;     /* When has it been compiled ? */
    linenumbers_t  *line_numbers;
      /* Line number information, NULL when not swapped in.
       * If swapped out, the data is stored in the swap file at
       * .swapnum+.total_size .
       */
    unsigned short *function_names;
#define PROGRAM_END(program) ((bytecode_p)(program).function_names)
      /* Lookup table [.num_function_names] function-index -> offset of
       * the function within the functions[] table. function_names[] is
       * stored right after the the bytecode within the same allocation
       * unit.
       * The table is sorted in descending order of the pointers(!)
       * of the shared function name strings. If the program contains
       * redefinitions of inherited functions, the entry here points
       * to the redefinition, the inherited function can then be
       * found from there.
       */
    funflag_t *functions;
      /* Array [.num_functions] with the flags and addresses of all
       * functions, inherited and own.
       * Nameless functions (those without an entry in function_names[])
       * are collected at the end of the table.
       * TODO: Instead of hiding the function information in the bytecode
       * TODO:: it should be tabled here.
       */
    char **strings;
      /* Array [.num_strings] of the shared strings used by the program.
       * Stored in reverse order at the end of the array are the pointers
       * to the names of all included files which generated code - these
       * are used when retrieving line numbers.
       */
    variable_t *variable_names;
      /* Array [.num_variables] with the flags, types and names of all
       * variables.
       */
    include_t *includes;
      /* Array [.num_includes] of descriptors for included files.
       */
    inherit_t *inherit;
      /* Array [.num_inherited] of descriptors for (directly) inherited programs.
       */
    unsigned short flags;
      /* Flags for the program: */

#   define P_REPLACE_ACTIVE   0x0001
      /* This program will be or has been replaced at least once.
       * As this flag is never reset, the caller must check the
       * obj_list_replace if his object is affected or not.
       */
#   define P_NO_INHERIT       0x0002 /* Program must not be inherited */
#   define P_NO_CLONE         0x0004 /* No clones allowed */
#   define P_NO_SHADOW        0x0008 /* No shadows allowed */

    short heart_beat;
      /* Index of the heart beat function. -1 means no heart beat
       */

    /* The types of all function arguments are saved in the
     * .argument_types[]. To look up the arguments types for
     * function <n>, retrieve the start index from the .type_start[]
     * as .type_start[n]. If this index is INDEX_START_NONE, the function
     * has no type information.
     *
     * Both arrays will only be allocated if '#pragma save_types' has
     * been specified.
     */
    vartype_t *argument_types;
    unsigned short *type_start;
      /* TODO: Some code relies on this being unsigned short */
#   define INDEX_START_NONE                65535

    p_int swap_num;
      /* The swap number (swap file offset) for an unswapped program
       * It is set to -1 if it hasn't been swapped yet.
       */

    /*
     * And now some general size information.
     */
    unsigned short num_function_names;
      /* Number of function names listed in the lookup table.
       * This number is <= .num_functions as the redefinition of
       * of inherited functions does not need an additional name
       * entry. Also, private functions have no visible name.
       */
    unsigned short num_functions;
      /* Number of functions (inherited and own) of this program */
    unsigned short num_strings;
      /* Number of shared strings (including filenames) used by the program */
    unsigned short num_includes;
      /* Number of stored include filenames */
    unsigned short num_variables;
      /* Number of variables (inherited and own) of this program */
    unsigned short num_inherited;
      /* Number of (directly) inherited programs */
};


/* --- struct linenumbers_s: the linenumber head structure
 *
 * This structure is the head of the memory block with the linenumbers
 * data.
 */

struct linenumbers_s
{
    size_t     size;             /* Total allocated size of this structure */
    bytecode_t line_numbers[1];
      /* Array [.size - sizeof(.size)] with the delta-compressed
       * line number information. This is actually one byte too many, but
       * that simplifies the swapping code.
       */
};


/* --- struct function_s: Function description
 *
 * Structures of this type hold various important pieces of
 * information about a function.
 * The compiler uses this structure to collect the function information
 * of newly defined and inherited functions, of which the former will also
 * be compiled into the function header
 * The simul_efun module uses this structure to look up quickly functions.
 */

struct function_s
{
    char *name;  /* Name of function (shared string) */
    union {
        uint32 pc;       /* lfuns: Address of function header */
        uint32 inherit;  /* Inherit table index from where inherited. */
         int32 func;
           /* For cross-defined functions, this is the index offset
            * to the original function in bias-0x200000 notation.
            * Semantik: real-index = this-index + offset.
            * The offset is also stored in the function flags in
            * the program_t.functions[] array.
            *
            * simul_efun.c also uses this field as a 'next'
            * index in the simul_efun function table for
            * functions that have been discarded due to a
            * change in the number of arguments.
            */
        function_t *next;        /* used for mergesort */
    } offset;
    funflag_t     flags;      /* Function flags */
    vartype_t     type;       /* Return type of function. */
    unsigned char num_local;  /* Number of local variables */
    unsigned char num_arg;    /* Number of arguments needed. */
#   define SIMUL_EFUN_VARARGS  0xff  /* Magic num_arg value for varargs */
};


/* --- Bytecodes used to encode line numbers ---
 *
 * The line number information is a block of bytecode associating small
 * blocks of bytecode with the generating line number ranges.
 *
 * To save space, the information uses a delta compression, necessitating
 * to read all information from the beginning and counting up a line number
 * and bytecode offset counter to retrieve a specific bytecode/line number
 * association.
 *
 * The names of included files are stored in the order of appearance
 * at the end of the string table. Multiple included files are stored
 * several times.
 */

/* Bytecodes 0x00..0x3a: The amount of program bytes generated for the
 * current line; this entry is complete with that.
 */

#define LI_MAXOFFSET      0x3b
  /* The current line generated 0x3b bytes (more), but the entry
   * is not complete. This code used after the linecodes 0xC0..0xFF.
   */

#define LI_BACK           0x3c
  /* Followed by unsigned byte <off>.
   * The current line counter was set back by <off>+1.
   */

#define LI_INCLUDE        0x3d
  /* The following program bytes were generated from the next included
   * file. Reset the current line number to 0.
   * LI_INCLUDEs can be nested.
   */

#define LI_INCLUDE_END    0x3e
  /* End of the included program part.
   */

#define LI_L_RELOCATED    0x3f
  /* Followed by bytes <hi> <lo> LI_L_RELOCATED
   * The lines were relocated from a line by a delta to the current_line-2.
   * The delta, an signed int16, is encoded in <hi> and <lo>.
   */

/* Bytecodes 0x40..0x7f encode actual code/line number relations:
 *   codesize:  1..8
 *   line incr: 2..9
 * -> bytecode = (lineincr+6) << 3 | (codesize-1)
 * Each code is a complete entry.
 */

#define LI_RELOCATED      0xc0
  /* Bytecodes 0x80..0xDF: a small line relocation.
   * The lines were relocated from a line by a delta -0x20..0x1F
   * to the current_line-2.
   * The delta is encoded as delta+LI_RELOCATED.
   */

#define LI_SMALL_REL      0x20
  /* The allowed magnitude of a relocation to fit into a small
   * relocation code.
   */

#define LI_MAXEMPTY       0x20
  /* Bytecodes 0xE0..0xFF: 'use' a number of lines between 1 and 32
   * -> bytecode = (0x100 - lines)
   * The entry is not complete.
   */


/***************************************************************************/

#endif /* EXEC_H__ */