/* $Header$ */

/* 
 *   Copyright (c) 2010 Michael J. Roberts.  All Rights Reserved.
 *   
 *   Please see the accompanying license file, LICENSE.TXT, for information
 *   on using and copying this software.  
 */
/*
Name
  vmdynfunc.h - Code Object
Function
  A Code Object is an intrinsic class object that contains a block of byte
  code.  This can be used for things like debugger expressions and run-time
  evaluation and code creation.

  A Code Object is immutable, like a String or List.  It contains the
  original source code text, as a UTF-8 string, and the corresponding
  compiled byte code.
Notes
  
Modified
  12/13/09 MJRoberts  - Creation
*/

#ifndef VMDYNFUNC_H
#define VMDYNFUNC_H

#include "t3std.h"
#include "vmtype.h"
#include "tcprstyp.h"
#include "vmglob.h"
#include "vmobj.h"
#include "vmundo.h"


/* ------------------------------------------------------------------------ */
/*
 *   The image file data block is arranged as follows:
 *   
 *.  UINT2 code_length
 *.  UINT2 obj_ref_cnt
 *.  DATAHOLDER src_string
 *.  BYTE[code_length] bytecode
 *.  UINT2[ref_cnt] obj_refs
 *   
 *   code_length is the size of the bytecode stream, in bytes.
 *   
 *   obj_ref_cnt is the number of object references.  These are stored
 *   immediately after the bytecode stream.  Each reference array entry is a
 *   UINT2 giving the byte offset in the bytecode array of the reference,
 *   which is a UINT4 stored in portable format giving the object ID.
 */


/* ------------------------------------------------------------------------ */
/*
 *   Our in-memory extension data structure, which mimics the image file
 *   structure but uses native types.  
 */

/* prefix header size */
const size_t VMCO_PREFIX_LENGTH = VMB_OBJECT_ID;

/* extension structure */
struct vm_dynfunc_ext
{
    /* allocate the structure */
    static vm_dynfunc_ext *alloc_ext(VMG_ class CVmDynamicFunc *self,
                                     size_t bytecode_len, int obj_ref_cnt);

    /* the source string object */
    vm_val_t src;

    /* 
     *   Method context object.  For a function that's compiled with a local
     *   stack frame context, this is the 'self' object or the complete
     *   method context object (suitable for the LOADCTX opcode), as
     *   applicable, for the enclosing method.  This allows the function to
     *   establish the dynamic enclosing method context at entry.  
     */
    vm_val_t method_ctx;

    /* the size in bytes of the byte code data */
    size_t bytecode_len;

    /* number of object references in the fixup list */
    int obj_ref_cnt;

    /* 
     *   Object references.  The structure is allocated with enough memory
     *   for 'obj_ref_cnt' entries in this array. 
     */
    uint obj_refs[1];

    /*
     *   The structure is allocated with memory following the 'obj_refs'
     *   array for the following dynamic elements:
     *   
     *.   char dynamic_code_header[VMB_OBJECT_ID]
     *.   char bytecode[bytecode_len]
     *   
     *   Explanation of the dynamic object header:
     *   
     *   The VM internally refers to executing code by a pointer directly
     *   into the code's physical memory.  It always keeps an Entry Pointer
     *   value giving a pointer to the first byte of the method; this makes
     *   it easy to get metadata about any active code block by serving as an
     *   identifier for the code.
     *   
     *   Regular code is stored at compile-time in the Code Pool, which is a
     *   special block of read-only memory especially for code.  The FUNCPTR
     *   and CODEPTR primitive datatypes contain offsets into the Code Pool.
     *   A function generated by the compiler is represented by a FUNCPTR
     *   value.  To get a FUNCPTR value given an Entry Pointer, we simply
     *   translate the physical memory pointer back into a Code Pool offset,
     *   and wrap the result in a FUNCPTR value.
     *   
     *   Dynamic code objects are not in the Code Pool, however, since that's
     *   read-only memory that we can't add to at run-time.  Dynamic code
     *   objects are instead allocated in the garbage-collected heap like
     *   other objects.  So we need some way to translate from an Entry
     *   Pointer that points into a dynamic object back into a vm_val_t.  The
     *   vm_val_t representation of a dynamic object is a reference to the
     *   object - i.e., an OBJ value containing the code object's ID.
     *   There's no generic way to look up an object ID given the extension
     *   pointer, which is what the Entry Pointer for a dynamic code object
     *   basically is.  (The Entry Pointer doesn't actually point to the
     *   extension - it points to the start of the 'buf' element of the
     *   extension, because that's where our method header starts.)  So, we
     *   need our own way of working from the Entry Point back to our object
     *   ID.  To do this, we simply add our object ID just ahead of the
     *   method header.  To generate a vm_val_t from an Entry Pointer value,
     *   then, we first look to see if the pointer is a Code Pool element; if
     *   so, we use a FUNCPTR value; if not, it must be a dynamic code
     *   object, so we retrieve the OBJECT_ID value at (Entry Pointer -
     *   VMB_OBJECT_ID) as an object ID, and create an OBJ value with that
     *   ID.  
     */

    /* get a pointer to the dynamic code object method header prefix */
    char *get_prefix_ptr()
    {
        /* the prefix starts after the obj_ref array */
        return (char *)&obj_refs[obj_ref_cnt];
    }

    /* get a pointer to the start of the byte code */
    char *get_bytecode_ptr()
    {
        /* the byte code starts after the dynamic code prefix */
        return get_prefix_ptr() + VMCO_PREFIX_LENGTH;
    }
};


/* ------------------------------------------------------------------------ */
/*
 *   DynamicFunc metaclass 
 */

class CVmDynamicFunc: public CVmObject
{
    friend class CVmMetaclassDynamicFunc;
    friend class CVmDynamicCompiler;
    
public:
    /* metaclass registration object */
    static class CVmMetaclass *metaclass_reg_;
    class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; }

    /* am I of the given metaclass? */
    virtual int is_of_metaclass(class CVmMetaclass *meta) const
    {
        /* try my own metaclass and my base class */
        return (meta == metaclass_reg_
                || CVmObject::is_of_metaclass(meta));
    }

    /* is this a DynamicFunc object? */
    static int is_dynfunc_obj(VMG_ vm_obj_id_t obj)
        { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); }

    /* 
     *   Given a pointer to a method header, retrieve the object ID of the
     *   DynamicFunc that owns the bytecode.  Returns VM_INVALID_OBJ if we
     *   can't find a valid owner.  
     */
    static vm_obj_id_t get_obj_from_prefix(VMG_ const uchar *p);

    /* create from a string, without saving the source text */
    static vm_obj_id_t create(VMG_ int in_root_set,
                              vm_obj_id_t globals, vm_obj_id_t locals,
                              vm_obj_id_t macros,
                              const char *src, size_t src_len)
    {
        vm_val_t v;
        v.set_nil();
        return create(vmg_ in_root_set, globals, locals, macros,
                      &v, src, src_len);
    }

    /* create from a string value */
    static vm_obj_id_t create(VMG_ int in_root_set,
                              vm_obj_id_t globals, vm_obj_id_t locals,
                              vm_obj_id_t macros,
                              const vm_val_t *src);

    /* create from a string, optionally saving the string value */
    static vm_obj_id_t create(VMG_ int in_root_set,
                              vm_obj_id_t symtab, vm_obj_id_t locals,
                              vm_obj_id_t macros,
                              const vm_val_t *src_val,
                              const char *src, size_t src_len);

    /* create dynamically using stack arguments */
    static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr,
                                         uint argc);

    /* notify of deletion */
    void notify_delete(VMG_ int in_root_set);

    /* 
     *   call a static property - we don't have any of our own, so simply
     *   "inherit" the base class handling 
     */
    static int call_stat_prop(VMG_ vm_val_t *result,
                              const uchar **pc_ptr, uint *argc,
                              vm_prop_id_t prop)
    {
        return CVmObject::
            call_stat_prop(vmg_ result, pc_ptr, argc, prop);
    }

    /* reserve constant data */
    virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper,
                                    vm_obj_id_t self)
    {
        /* we cannot be converted to constant data */
    }
    
    /* convert to constant data */
    virtual void convert_to_const_data(VMG_ class CVmConstMapper *,
                                       vm_obj_id_t)
    {
        /* we don't reference anything */
    }

    /* index the object */
    virtual int index_val_q(VMG_ vm_val_t *result,
                            vm_obj_id_t self,
                            const vm_val_t *index_val);

    /* set a property */
    void set_prop(VMG_ class CVmUndo *,
                  vm_obj_id_t, vm_prop_id_t, const vm_val_t *)
    {
        /* we have no settable properties */
        err_throw(VMERR_INVALID_SETPROP);
    }

    /* get a property */
    int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val,
                 vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc);

    /* invoke */
    int get_invoker(VMG_ vm_val_t *val);

    /* undo operations - we're immutable, so we can ignore these */
    void notify_new_savept() { }
    void apply_undo(VMG_ struct CVmUndoRecord *) { }
    void discard_undo(VMG_ struct CVmUndoRecord *) { }
    void mark_undo_ref(VMG_ struct CVmUndoRecord *) { }

    /* mark our references (we reference our source string) */
    void mark_refs(VMG_ uint);

    /* we don't keep any weak references */
    void remove_stale_weak_refs(VMG0_) { }
    void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { }

    /* load from an image file */
    void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz);

    /* reload from an image file */
    void reload_from_image(VMG_ vm_obj_id_t self,
                           const char *ptr, size_t siz);

    /* rebuild for image file */
    virtual ulong rebuild_image(VMG_ char *buf, ulong buflen);

    /* save to a file */
    void save_to_file(VMG_ class CVmFile *fp);

    /* restore from a file */
    void restore_from_file(VMG_ vm_obj_id_t self,
                           class CVmFile *fp, class CVmObjFixup *fixups);

    /* we're immutable, so we're definitely not changed since loading */
    int is_changed_since_load() const { return FALSE; }

protected:
    /* get my extension data */
    vm_dynfunc_ext *get_ext() const { return (vm_dynfunc_ext *)ext_; }

    /* load or reload image data */
    void load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz);

    /* set the method context object */
    void set_method_ctx(const vm_val_t *val) { get_ext()->method_ctx = *val; }

    /* create a with no initial contents */
    CVmDynamicFunc() { ext_ = 0; }

    /* create with the given byte code array length */
    CVmDynamicFunc(VMG_ vm_obj_id_t self, const vm_val_t *src,
                   size_t bytecode_len, int obj_ref_cnt);

    /* property evaluator - undefined function */
    int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; }

    /* property evaluator - get my source text */
    int getp_get_source(VMG_ vm_obj_id_t, vm_val_t *, uint *);

    /* property evaluation function table */
    static int (CVmDynamicFunc::*func_table_[])(
        VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc);
};


/* ------------------------------------------------------------------------ */
/*
 *   DynamicFunc registration table object 
 */
class CVmMetaclassDynamicFunc: public CVmMetaclass
{
public:
    /* get the global name */
    const char *get_meta_name() const { return "dynamic-func/030000"; }

    /* create from image file */
    void create_for_image_load(VMG_ vm_obj_id_t id)
    {
        new (vmg_ id) CVmDynamicFunc();
        G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE);
    }

    /* create from restoring from saved state */
    void create_for_restore(VMG_ vm_obj_id_t id)
    {
        new (vmg_ id) CVmDynamicFunc();
        G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE);
    }

    /* create dynamically using stack arguments */
    vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc)
        { return CVmDynamicFunc::create_from_stack(vmg_ pc_ptr, argc); }
    
    /* call a static property */
    int call_stat_prop(VMG_ vm_val_t *result,
                       const uchar **pc_ptr, uint *argc,
                       vm_prop_id_t prop)
    {
        return CVmDynamicFunc::call_stat_prop(vmg_ result, pc_ptr, argc, prop);
    }
};

/* ------------------------------------------------------------------------ */
/*
 *   Dynamic compiler interface
 */


/* compilation modes */
enum CVmDynCompMode
{
    /* compile an expression */
    DCModeExpression,

    /* auto-sensing: 'function' syntax, 'method' syntax, or an expression */
    DCModeAuto,

    /* compile a grammar production rule (alternative) list */
    DCModeGramAlt
};

/* debugger expression context */
struct CVmDynCompDebug
{
    CVmDynCompDebug(class CTcPrsDbgSymtab *symtab,
                    tcpn_dyncomp_info &di, int self_valid)
    {
        this->symtab = symtab;
        this->di = di;
        this->self_valid = self_valid;
    }

    /* IN: local symbol table for debugger expression evaluation */
    class CTcPrsDbgSymtab *symtab;

    /* IN: debugger evaluation context settings */
    tcpn_dyncomp_info di;

    /* IN: is there a valid 'self' for this expression? */
    int self_valid;

    /* OUT: the parsed expression is an lvalue */
    int is_lval;
};

/* results structure */
struct CVmDynCompResults
{
    CVmDynCompResults()
    {
        /* clear the error code */
        err = 0;

        /* no error message */
        msgbuf = 0;
    }

    virtual ~CVmDynCompResults()
    {
        free_msgbuf();
    }

    /* Explicitly free the message buffer */
    void free_msgbuf()
    {
        if (msgbuf != 0)
        {
            t3free(msgbuf);
            msgbuf = 0;
        }
    }

    /* throw a dynamic compilation error based on the results */
    void throw_error(VMG0_);

    /* 
     *   In grammar mode (DCModeGramAlt), the compiler passes back the parsed
     *   alt list to the caller by calling this method.  Callers that don't
     *   parse grammar rules can leave this as a no-op.  'alts' is the head
     *   of the alternative list for the parsed grammar rule.  
     */
    virtual void save_grammar(VMG_ class CTcGramProdAlt * /*alts*/,
                              struct CTcGramPropArrows * /*arrows*/) { }
    
    /* compiler error code */
    int err;

    /* 
     *   Message buffer.  The compiler allocates this via t3malloc() if an
     *   error occurs. 
     */
    char *msgbuf;
};

/*
 *   dynamic compiler 
 */
class CVmDynamicCompiler
{
public:
    /* create */
    CVmDynamicCompiler(VMG0_);

    /* destroy */
    ~CVmDynamicCompiler();

    /* 
     *   Get or create the global singleton instance.  This creates an
     *   instance if it doesn't already exist, so that we don't instantiate
     *   the compiler structures until they're actually needed.  Once we
     *   create the object, we keep it around in a global.  
     */
    static CVmDynamicCompiler *get(VMG0_);

    /* 
     *   Compile source code into byte code, returning a new DynamicFunc
     *   instance containing the compiled function.  The return value is the
     *   ID of the new object if the compilation was successful, or
     *   VM_INVALID_OBJ if the compilation failed.
     *   
     *   The source code is the string given by 'src' and 'srclen'.  If
     *   'srcval' is non-null, it should be a VM_SSTR or VM_OBJ value with
     *   the VM representation of the source code string; we'll store this
     *   with the DynamicFunc for retrieval with getSource().  If 'srcval' is
     *   null, the DynamicFunc won't store the source, so getSource() will
     *   return nil.
     *   
     *   'mode' gives the compilation mode, which controls how the source
     *   string is parsed.
     *   
     *   If 'dbg' is non-null, we compile for the debugger, with the options
     *   in 'dbg'.  Debugger evaluation is slightly different from regular
     *   evaluation because it can access local variables from an enclosing
     *   scope.
     *   
     *   If 'errp' is non-null, we'll fill it in with the TCERR_xxx number
     *   for the first compiler error, if any.
     *   
     *   If 'msgbuf' is non-null, we'll fill it in with an allocated string
     *   buffer containing the compiler error message(s).  Multiple messages
     *   are separated by newline '\n' characters.  The caller must free this
     *   buffer with t3free() when done with it.  
     */
    vm_obj_id_t compile(VMG_ int in_root_set,
                        vm_obj_id_t globals, vm_obj_id_t locals,
                        vm_obj_id_t macros,
                        const vm_val_t *srcval,
                        const char *src, size_t srclen,
                        CVmDynCompMode mode, CVmDynCompDebug *dbg,
                        CVmDynCompResults *results);

protected:
    /* generate code for a code body */
    int gen_code_body(
        VMG_ class CTPNStmTop *node, const vm_val_t *srcval,
        CVmDynCompDebug *dbg);

    /* parser */
    class CTcParser *prs_;

    /* compiler host interface */
    class CTcHostIfcDynComp *hostifc_;
};



#endif /* VMDYNFUNC_H */

/*
 *   Register the class 
 */
VM_REGISTER_METACLASS(CVmDynamicFunc)
