#ifdef RCSID
static char RCSid[] =
"$Header: d:/cvsroot/tads/TADS2/OBJCOMP.C,v 1.3 1999/07/11 00:46:30 MJRoberts Exp $";
#endif

/* 
 *   Copyright (c) 1992, 2002 Michael J. Roberts.  All Rights Reserved.
 *   
 *   Please see the accompanying license file, LICENSE.TXT, for information
 *   on using and copying this software.  
 */
/*
Name
  objcomp.c - object manipulation routines for compiler
Function
  Provides routines used only by the compiler
Notes
  Split off from obj main module to make run-time smaller
Modified
  12/18/92 MJRoberts     - creation
*/

#include "os.h"
#include "std.h"
#include "err.h"
#include "mcm.h"
#include "obj.h"

/*
 *   Set up for emitting code into an object.  No undo information is
 *   kept for this type of operation, as it is presumed that the object is
 *   being compiled rather than being manipulated at run-time.  Any
 *   previous value for the property is deleted, a property header is set
 *   up, and the offset of the next free byte in the object is returned.
 */
uint objemt(mcmcxdef *ctx, objnum objn, prpnum prop, dattyp typ)
{
    objdef *objptr;
    prpdef *p;
    
    objptr = (objdef *)mcmlck(ctx, (mcmon)objn);
    
    ERRBEGIN(ctx->mcmcxgl->mcmcxerr)

    objdelp(ctx, objn, prop, FALSE);   /* delete old property value, if any */
    p = objpfre(objptr);                        /* get top of property area */
    
    if ((char *)p - (char *)objptr + PRPHDRSIZ >=
        mcmobjsiz(ctx, (mcmon)objn))
    {
        ushort newsiz = 64 + ((objfree(objptr) + PRPHDRSIZ) -
                              mcmobjsiz(ctx, (mcmon)objn));
        objptr = objexp(ctx, objn, &newsiz);
        p = objpfre(objptr);                       /* object may have moved */
    }
    
    /* set up property header as much as we can (don't know size yet) */
    prpsetprop(p, prop);
    prptype(p) = typ;
    prpflg(p) = 0;
    objsnp(objptr, objnprop(objptr) + 1);              /* one more property */

    ERRCLEAN(ctx->mcmcxgl->mcmcxerr)
        mcmunlck(ctx, (mcmon)objn);
    ERRENDCLN(ctx->mcmcxgl->mcmcxerr)

    /* dirty the cache object and release the lock before return */
    mcmtch(ctx, objn);
    mcmunlck(ctx, (mcmon)objn);
    return(((uchar *)prpvalp(p)) - ((uchar *)objptr));
}

/* done emitting code into property - finish setting object information */
void objendemt(mcmcxdef *ctx, objnum objn, prpnum prop, uint endofs)
{
    objdef *objptr;
    prpdef *p;
    uint    siz;
    
    objptr = (objdef *)mcmlck(ctx, (mcmon)objn);
    p = objofsp(objptr, objgetp(ctx, objn, prop, (dattyp *)0));
    
    siz = endofs - (((uchar *)prpvalp(p)) - ((uchar *)objptr));
    
    prpsetsize(p, siz);
    objsfree(objptr, objfree(objptr) + siz + PRPHDRSIZ);
    
    /* mark the object as changed, and unlock it */
    mcmtch(ctx, (mcmon)objn);
    mcmunlck(ctx, (mcmon)objn);
}

/* add superclasses to an object */
void objaddsc(mcmcxdef *mctx, int sccnt, objnum objn)
{
    objdef *o;
    ushort  siz;
    
    /* get lock on object */
    o = (objdef *)mcmlck(mctx, objn);
    
    /* make sure there's enough space, adding space if needed */
    if (mcmobjsiz(mctx, (mcmon)objn) - objfree(o) < 2 * sccnt)
    {
        siz = 64 + ((2 * sccnt + objfree(o)) -
                    mcmobjsiz(mctx, (mcmon)objn));
        o = objexp(mctx, objn, &siz);                  /* expand the object */
    }

    /* move properties, if any, above added superclasses */
    if (objnprop(o))
        memmove(objprp(o), ((uchar *)objprp(o)) + 2 * sccnt,
                (size_t)(((uchar *)o) + objfree(o) - (uchar *)objprp(o)));
    
    /* set new free pointer */
    objsfree(o, objfree(o) + 2 * sccnt);
    
    /* mark cache object modified and unlock it */
    mcmtch(mctx, objn);
    mcmunlck(mctx, objn);
}

/* delete an object's properties and superclasses */
void objclr(mcmcxdef *mctx, objnum objn, prpnum mindel)
{
    objdef *o;
    prpdef *p;
    int     cnt;
    prpnum  prop;
    int     indexed;
    
    /* get a lock on the object */
    o = (objdef *)mcmlck(mctx, objn);
    indexed = objflg(o) & OBJFINDEX;
    
    /* delete superclasses - move properties down over former sc array */
    if (objnprop(o))
        memmove(objsc(o), objprp(o),
                (size_t)(((uchar *)o) + objfree(o) - (uchar *)objprp(o)));
    objsnsc(o, 0);                                 /* zero superclasses now */
    
    /* delete non-"system" properties (propnum < mindel) */
    for (p = objprp(o), cnt = objnprop(o) ; cnt ; --cnt)
    {
        if ((prop = prpprop(p)) >= mindel)
        {
            prpflg(p) &= ~PRPFIGN;   /* delete even if it was marked ignore */
            objdelp(mctx, objn, prop, FALSE);  /* remove prpdef from object */
            /* p is left pointing at next prop, as it was moved down */
        }
        else
            p = objpnxt(p);                   /* advance over this property */
    }
    
    /* mark cache object modified and unlock it */
    mcmtch(mctx, objn);
    mcmunlck(mctx, objn);
    if (indexed) objindx(mctx, objn);
}

/* set up just-compiled object:  mark static part and original props */
void objcomp(mcmcxdef *mctx, objnum objn, int for_debug)
{
    objdef *objptr;
    prpdef *p;
    prpdef *nxt;
    int     cnt;
    
    /* lock object */
    objptr = (objdef *)mcmlck(mctx, (mcmon)objn);

    /* 
     *   first, go through the properties, and delete each one that's
     *   marked as ignored 
     */
    for (cnt = objnprop(objptr), p = objprp(objptr) ; cnt != 0 ;
         p = nxt, --cnt)
    {
        /* remember the next property */
        nxt = objpnxt(p);

        /* if this is marked as being ignored, delete it */
        if ((prpflg(p) & PRPFIGN) != 0)
        {
            /* 
             *   Delete the property.  If we're compiling in debug mode,
             *   don't really delete anything, but simply mark the
             *   property as deleted; this is necessary because certain
             *   debug records are not self-relative, hence are not
             *   tolerant of changes to the internal structure of the
             *   object.  If we're not compiling for debugging, we can
             *   actually delete properties, because all non-debug code is
             *   completely self-relative and thus can be moved around
             *   inside the object without any problems.  
             */
            if (for_debug)
            {
                /* simply mark the property as deleted */
                prpflg(p) |= PRPFDEL;

                /* 
                 *   clear the IGNORE flag, since we IGNORE and DELETED
                 *   are mutually exclusive 
                 */
                prpflg(p) &= ~PRPFIGN;
            }
            else
            {
                /* clear the flags so we can really delete it */
                prpflg(p) &= ~(PRPFORG | PRPFIGN | PRPFDEL);

                /* delete it */
                objdelp(mctx, objn, (prpnum)prpprop(p), FALSE);

                /* 
                 *   continue from the present location, since we moved
                 *   the next property to the current location 
                 */
                nxt = p;
            }
        }
    }
    
    /* set static entries:  free space pointer, and number of properties */
    objsetst(objptr, objnprop(objptr));
    objsetrst(objptr, objfree(objptr));

    /* go through properties, marking each as original */
    for (cnt = objnprop(objptr), p = objprp(objptr) ; cnt != 0 ;
         p = objpnxt(p), --cnt)
    {
        assert(p < objptr + mcmobjsiz(mctx, (mcmon)objn));
        prpflg(p) |= PRPFORG;             /* set ORIGINAL flag for property */
    }
    
    /* mark object changed, and unlock it */
    mcmtch(mctx, objn);
    mcmunlck(mctx, objn);
}

