/* ctar - stuff to manage compile-time activation records.
 * This generates code for the run time activation records,
 * which are pushed onto a stack at the start of each function
 * and popped on the end. This lets us clean up local vars when 
 * there's an exception, and also allows us to print a nice
 * stack trace. */

#include "common.h"
#include "hash.h"
#include "dystring.h"
#include "pfType.h"
#include "pfScope.h"
#include "pfToken.h"
#include "pfCompile.h"
#include "pfParse.h"
#include "recodedType.h"
#include "codedType.h"
#include "cCoder.h"
#include "ctar.h"

void ctarFree(struct ctar **pCtar)
/* Free up memory associated with activation record. */
{
struct ctar *ctar = *pCtar;
if (ctar != NULL)
    {
    slFreeList(&ctar->varRefList);
    freez(pCtar);
    }
}

static void rAddVars(struct ctar *ctar, struct pfParse *pp, 
	struct hash *aliasHash, int *pAliasNum)
/* Add variables in parse tree to activation record. */
{
switch (pp->type)
    {
    case pptVarInit:
	{
	struct pfVar *var = pp->var;
	if (var->ty->access != paStatic)
	    {
	    char *name = var->cName;

	    /* Make sure C name is unique.  It might not be if they reuse
	     * a name in an inner block. */
	    if (hashLookup(aliasHash, name))
		{
		int aliasSize = strlen(name) + 8 + 1;   /* Allow up to 8 digits of alias */
		char *alias = needMem(aliasSize);	
		for (;;)
		    {
		    *pAliasNum += 1;
		    safef(alias, aliasSize, "%s%d", name, *pAliasNum);
		    if (!hashLookup(aliasHash, alias))
			break;
		    }
		var->cName = alias;
		}
	    hashAdd(aliasHash, var->cName, NULL);
	    refAdd(&ctar->varRefList, var);
	    }
        break;
	}
    }
for (pp = pp->children; pp != NULL; pp = pp->next)
    rAddVars(ctar, pp, aliasHash, pAliasNum);
}


static char *cNameForFunction(struct pfParse *funcDec)
/* Return mangled C name for function */
{
struct dyString *dy = dyStringNew(0);
char *result;
struct pfBaseType *class = funcDec->var->scope->class;
if (class)
    {
    dyStringPrintf(dy, "_pf_cm%d_%s_%s", class->scope->id,
    	class->name, funcDec->name);
    }
else
    {
    dyStringPrintf(dy, "pf_%s", funcDec->name);
    }
result = cloneString(dy->string);
dyStringFree(&dy);
return result;
}

struct ctar *ctarOnFunction(struct pfParse *funcDec)
/* Create a compile time activation record for function. Hangs it on pp->var->ctar
 * and returns it. */
{
struct ctar *ctar;
struct hash *aliasHash = hashNew(8);
int aliasNum = 1;
static int id=0;
struct pfBaseType *class = funcDec->var->scope->class;
struct pfParse *namePp = funcDec->children;
struct pfParse *inTuple = namePp->next;
struct pfParse *outTuple = inTuple->next;

AllocVar(ctar);
funcDec->var->ctar = ctar;
ctar->id = ++id;
ctar->name = funcDec->name;
ctar->cName = cNameForFunction(funcDec);
ctar->pp = funcDec;
ctar->inCount = slCount(inTuple->children);
ctar->outCount = slCount(outTuple->children);
rAddVars(ctar, funcDec, aliasHash, &aliasNum);
ctar->localCount = slCount(ctar->varRefList) - ctar->inCount - ctar->outCount;
if (class != NULL)
    {
    struct pfVar *selfVar = pfScopeFindVar(funcDec->scope, "self");
    assert(selfVar != NULL);
    refAdd(&ctar->varRefList, selfVar);
    ctar->selfVar = selfVar;
    if (pfBaseIsDerivedClass(class))
        {
	struct pfVar *parentVar = pfScopeFindVar(funcDec->scope, "parent");
	assert(parentVar != NULL);
	refAdd(&ctar->varRefList, parentVar);
	ctar->parentVar = parentVar;
	}
    }
slReverse(&ctar->varRefList);

/* Fill in in, out, and local vars */
ctar->inRefList = ctar->varRefList;
ctar->outRefList = slElementFromIx(ctar->inRefList, ctar->inCount);
ctar->localRefList = slElementFromIx(ctar->outRefList, ctar->outCount);

/* Clean up and go home. */
hashFree(&aliasHash);
return ctar;
}


static void ctarFixedCode(struct ctar *ctar, struct pfCompile *pfc, FILE *f)
/* Write out fixed part of activation record for this function. */
{
struct pfParse *funcDec = ctar->pp;
struct pfBaseType *class = funcDec->var->scope->class;
int id = ctar->id;
int localCount = slCount(ctar->varRefList);
fprintf(f, "/* local var info for %s */\n",  ctar->cName);
if (localCount > 0)
    {
    struct slRef *ref;
    struct pfVar *var;
    fprintf(f, "static struct _pf_localVarInfo _pf_rtar%d_vars[%d] = {\n", id, localCount);
    for (ref = ctar->varRefList; ref != NULL; ref = ref->next)
        {
	var = ref->val;
	fprintf(f, " {\"%s\", ", var->name);
	fprintf(f, "%d, ", recodedTypeId(pfc, var->ty));
	fprintf(f, "0},\n");
	}
    fprintf(f, "};\n");
    }
fprintf(f, "static struct _pf_functionFixedInfo _pf_rtar%d_fixed = {", id);
if (class)
    {
    struct pfType *ty = pfTypeNew(class);
    fprintf(f, "%d, ", recodedTypeId(pfc, ty));
    freeMem(ty);
    }
else
    fprintf(f, "-1, ");

fprintf(f, "  \"%s\", %d, %d, ", ctar->name, 
	recodedTypeId(pfc, funcDec->var->ty), localCount);
if (localCount > 0)
    fprintf(f, "_pf_rtar%d_vars,", id);
else
    fprintf(f, "0,");
fprintf(f, "};\n\n");
}

void ctarCodeFixedParts(struct ctar *ctarList, struct pfCompile *pfc, FILE *f)
/* Write parts of function activation record that don't change from invocation
 * to invocation of function. */
{
struct ctar *ctar;
for (ctar = ctarList; ctar != NULL; ctar = ctar->next)
    ctarFixedCode(ctar, pfc, f);
fprintf(f, "static struct _pf_functionFixedInfo *_pf_rtar_fixed[] = {\n");
for (ctar = ctarList; ctar != NULL; ctar = ctar->next)
     fprintf(f, " &_pf_rtar%d_fixed,\n", ctar->id);
fprintf(f, "};\n\n");
}

void ctarCodeStartupCall(struct ctar *ctarList, struct pfCompile *pfc, FILE *f)
/* Code call to initialize types and offsets of fixed run time activation records. */
{
fprintf(f, "_pf_rtar_init_tables(_pf_rtar_fixed, %d, _pf_lti);\n", slCount(ctarList));
}

void ctarCodeLocalStruct(struct ctar *ctar, struct pfCompile *pfc, FILE *f,
	char *refName)
/* Write out a structure that has all of the local variables for a function. 
 * If refName is zero then make a pointer of this type equal to refName,
 * otherwise make an actual instand initialized to zero. */
{
struct slRef *ref;
if (ctar->varRefList)
    {
    fprintf(f, "struct _pf_l {\n");
    for (ref = ctar->varRefList; ref != NULL; ref = ref->next)
	{
	fprintf(f, " ");
	struct pfVar *var = ref->val;
	codeBaseType(pfc, f, var->ty->base);
	fprintf(f, " %s;\n", var->cName);
	}
    fprintf(f, "}");
    if (refName)
        fprintf(f, "*_pf_l = %s;\n", refName);
    else
	{
	fprintf(f, "_pf_l;\n");
	fprintf(f, "memset(&_pf_l, 0, sizeof(_pf_l));\n");
	}
    }
}

void ctarCodePush(struct ctar *ctar, struct pfCompile *pfc, FILE *f)
/* Write out code to define run-time activation structure and push
 * it on activation stack. */
{
fprintf(f, "struct _pf_activation _pf_act;\n");
fprintf(f, "_pf_act.parent = _pf_activation_stack;\n");
fprintf(f, "_pf_act.fixed = &_pf_rtar%d_fixed;\n", ctar->id);
if (ctar->varRefList == NULL)
    fprintf(f, "_pf_act.data = 0;\n");
else
    fprintf(f, "_pf_act.data = &_pf_l;\n");
fprintf(f, "_pf_activation_stack = &_pf_act;\n");
}

void ctarCodePop(struct ctar *ctar, struct pfCompile *pfc, FILE *f)
/* Write out code to pop activation structure off of stack. */
{
fprintf(f, "_pf_activation_stack = _pf_activation_stack->parent;\n");
}
