/* $Header:runtime.c 12.0$ */
/* $ACIS:runtime.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ucb/dbx/RCS/runtime.c,v $ */

#ifndef lint
static char *rcsid = "$Header:runtime.c 12.0$";
#endif


/* Copyright (c) 1982 Regents of the University of California */

/*
 * Runtime organization dependent routines, mostly dealing with
 * activation records.
 */

#include "defs.h"
#include "runtime.h"
#include "frame.h"
#include "process.h"
#include "machine.h"
#include "events.h"
#include "mappings.h"
#include "symbols.h"
#include "tree.h"
#include "eval.h"
#include "operators.h"
#include "object.h"
#include <signal.h>

#ifndef public
typedef struct Frame *Frame;

#include "machine.h"
#endif

private Boolean walkingstack = false;

typedef struct {
    Node callnode;
    Node cmdnode;
    boolean isfunc;
} CallEnv;

private CallEnv endproc;

/*
 * Get the current frame information in the given Frame and store the
 * associated function in the given value-result parameter.
 */

private getcurfunc (frp, fp)
Frame frp;
Symbol *fp;
{
    getcurframe(frp);
    *fp = whatblock(savepc(frp));
}

/*
 * Return the frame associated with the next function up the call stack, or
 * nil if there is none.  The function is returned in a value-result parameter.
 * For "inline" functions the statically outer function and same frame
 * are returned.
 */

public Frame nextfunc (frp, fp)
Frame frp;
Symbol *fp;
{
    Symbol t;
    Frame nfrp;

    t = *fp;
    checkref(t);
    if (isinline(t)) {
	t = container(t);
	nfrp = frp;
    } else {
	nfrp = nextframe(frp);
	if (nfrp == nil) {
	    t = nil;
	} else {
	    t = whatblock(savepc(nfrp));
	}
    }
    *fp = t;
    return nfrp;
}

/*
 * Return the frame associated with the given function.
 * If the function is nil, return the most recently activated frame.
 *
 * Static allocation for the frame.
 */

public Frame findframe (f)
Symbol f;
{
    register Frame frp;
    static struct Frame frame;
    Symbol p;

    frp = &frame;
    getcurframe(frp);
    if (f != nil) {
	if (f == curfunc and curframe != nil) {
	    *frp = *curframe;
	} else {
	    p = whatblock(savepc(frp));
	    for (;;) {
		if (p == f) {
		    break;
		} else if (p == program) {
		    frp = nil;
		    break;
		} else {
		    frp = nextfunc(frp, &p);
		    if (frp == nil) {
			break;
		    }
		}
	    }
	}
    }
    return frp;
}

/*
 * Find the return address of the current procedure/function.
 */

public Address return_addr ()
{
    Frame frp;
    Address addr;
    struct Frame frame;

    frp = &frame;
    getcurframe(frp);
    frp = nextframe(frp);
    if (frp == nil) {
	addr = 0;
    } else {
	addr = savepc(frp);
    }
    return addr;
}

/*
 * Print a list of currently active blocks starting with most recent.
 */

public wherecmd ()
{
    walkstack(false);
}

/*
 * Print the variables in the given frame or the current one if nil.
 */

public dump (func)
Symbol func;
{
    Symbol f;
    Frame frp;

    if (func == nil) {
	f = curfunc;
	if (curframe != nil) {
	    frp = curframe;
	} else {
	    frp = findframe(f);
	}
    } else {
	f = func;
	frp = findframe(f);
    }
    showaggrs = true;
    printcallinfo(f, frp);
    dumpvars(f, frp);
}

/*
 * Dump all values.
 */

public dumpall ()
{
    walkstack(true);
}

/*
 * Walk the stack of active procedures printing information
 * about each active procedure.
 */

private walkstack (dumpvariables)
Boolean dumpvariables;
{
    Frame frp;
    boolean save;
    Symbol f;
    struct Frame frame;

    if (notstarted(process) or isfinished(process)) {
	error("program is not active");
    } else {
	save = walkingstack;
	walkingstack = true;
	showaggrs = dumpvariables;
	frp = &frame;
	getcurfunc(frp, &f);
	for (;;) {
	    printcallinfo(f, frp);
	    if (dumpvariables) {
		dumpvars(f, frp);
		putchar('\n');
	    }
	    frp = nextfunc(frp, &f);
	    if (frp == nil or f == program) {
		break;
	    }
	}
	if (dumpvariables) {
	    printf("in \"%s\":\n", symname(program));
	    dumpvars(program, nil);
	    putchar('\n');
	}
	walkingstack = save;
    }
}

/*
 * Print out the information about a call, i.e.,
 * routine name, parameter values, and source location.
 */

private printcallinfo (f, frp)
Symbol f;
Frame frp;
{
    Lineno line;
    Address caller;

    caller = savepc(frp);
    if (caller != reg(PROGCTR)) {
	/*
	 * Unless we are in the current procedure, we should use pc-1
	 * for the traceback so that we see the call as the current location
	 * in the caller.  The pc often points to the line following the call.
	 */
	caller -= 1;
    }
    printname(stdout, f);
    if (not isinline(f)) {
	printparams(f, frp);
    }
    line = srcline(caller);
    if (line != 0) {
	printf(", line %d", line);
	printf(" in \"%s\"\n", srcfilename(caller));
    } else {
	printf(" at 0x%x\n", caller);
    }
}

/*
 * Set the current function to the given symbol.
 * We must adjust "curframe" so that subsequent operations are
 * not confused; for simplicity we simply clear it.
 */

public setcurfunc (f)
Symbol f;
{
    curfunc = f;
    curframe = nil;
}

/*
 * Return the frame for the current function.
 * The space for the frame is allocated statically.
 */

public Frame curfuncframe ()
{
    static struct Frame frame;
    Frame frp;

    if (curframe == nil) {
	frp = findframe(curfunc);
	curframe = &curframerec;
	*curframe = *frp;
    } else {
	frp = &frame;
	*frp = *curframe;
    }
    return frp;
}

/*
 * Execute up to a source line, skipping over intermediate procedure calls.
 * The tricky part here is recursion; we might single step to the next line,
 * but be within a recursive call.  So we compare frame pointers to make sure
 * that execution is at the same or outer level.
 */

public donext ()
{
    Address oldfrp, newfrp;

    if (isprolog(pc)) {
	dostep(true);
	pc = reg(PROGCTR);
    } else {
	oldfrp = reg(FRP);
	do {
	    dostep(true);
	    pc = reg(PROGCTR);
	    newfrp = reg(FRP);
	} while (newfrp < oldfrp and newfrp != 0);
    }
}

/*
 * Set curfunc to be N up/down the stack from its current value.
 */

public up (n)
integer n;
{
    integer i;
    Symbol f;
    Frame frp;
    boolean done;

    if (not isactive(program)) {
	error("program is not active");
    } else if (curfunc == nil) {
	error("no current function");
    } else {
	i = 0;
	f = curfunc;
	frp = curfuncframe();
	done = false;
	do {
	    if (frp == nil) {
		done = true;
		error("not that many levels");
	    } else if (i >= n) {
		done = true;
		curfunc = f;
		curframe = &curframerec;
		*curframe = *frp;
		showaggrs = false;
		printcallinfo(curfunc, curframe);
	    } else if (f == program) {
		done = true;
		error("not that many levels");
	    } else {
		frp = nextfunc(frp, &f);
	    }
	    ++i;
	} while (not done);
    }
}

public down (n)
integer n;
{
    integer i, depth;
    Frame frp, curfrp;
    Symbol f;
    struct Frame frame;

    if (not isactive(program)) {
	error("program is not active");
    } else if (curfunc == nil) {
	error("no current function");
    } else {
	depth = 0;
	frp = &frame;
	getcurfunc(frp, &f);
	if (curframe == nil) {
	    curfrp = findframe(curfunc);
	    curframe = &curframerec;
	    *curframe = *curfrp;
	}
	while ((f != curfunc or !frameeq(frp, curframe)) and f != nil) {
	    frp = nextfunc(frp, &f);
	    ++depth;
	}
	if (f == nil or n > depth) {
	    error("not that many levels");
	} else {
	    depth -= n;
	    frp = &frame;
	    getcurfunc(frp, &f);
	    for (i = 0; i < depth; i++) {
		frp = nextfunc(frp, &f);
		assert(frp != nil);
	    }
	    curfunc = f;
	    *curframe = *frp;
	    showaggrs = false;
	    printcallinfo(curfunc, curframe);
	}
    }
}

/*
 * Return the address corresponding to the first line in a function.
 */

public Address firstline (f)
Symbol f;
{
    Address addr;

    addr = codeloc(f);
    while (linelookup(addr) == 0 and addr < objsize) {
	++addr;
    }
    if (addr == objsize) {
	addr = -1;
    }
    return addr;
}

/*
 * Catcher drops strike three ...
 */

public runtofirst ()
{
    Address addr, endaddr;

    addr = pc;
    endaddr = objsize + CODESTART;
    while (linelookup(addr) == 0 and addr < endaddr) {
	++addr;
    }
    if (addr < endaddr) {
	stepto(addr);
    }
}

/*
 * Return the address corresponding to the end of the program.
 *
 * We look for the entry to "_exit".
 */

public Address lastaddr ()
{
    Symbol s;

    s = lookup(identname("_exit", true));
    if (s == nil) {
	warning("can't find _exit");
	return CODESTART;
    }
    return codeloc(s);
}

/*
 * Decide if the given function is currently active.
 *
 * We avoid calls to "findframe" during a stack trace for efficiency.
 * Presumably information evaluated while walking the stack is active.
 */

public Boolean isactive (f)
Symbol f;
{
    Boolean b;

    if (isfinished(process)) {
	b = false;
    } else {
	if (walkingstack or f == program or f == nil or
	  (ismodule(f) and isactive(container(f)))) {
	    b = true;
	} else {
	    b = (Boolean) (findframe(f) != nil);
	}
    }
    return b;
}

/*
 * Evaluate a call to a procedure.
 */

public callproc (exprnode, isfunc)
Node exprnode;
boolean isfunc;
{
    Node procnode, arglist;
    Symbol proc;
    integer argc;

    procnode = exprnode->value.arg[0];
    arglist = exprnode->value.arg[1];
    if (procnode->op != O_SYM) {
	beginerrmsg();
	fprintf(stderr, "can't call \"");
	prtree(stderr, procnode);
	fprintf(stderr, "\"");
	enderrmsg();
    }
    assert(procnode->op == O_SYM);
    proc = procnode->value.sym;
    if (not isblock(proc)) {
	error("\"%s\" is not a procedure or function", symname(proc));
    }
    endproc.isfunc = isfunc;
    endproc.callnode = exprnode;
    endproc.cmdnode = topnode;
    pushenv();
    pc = codeloc(proc);
    argc = pushargs(proc, arglist);
    beginproc(proc, argc);
    event_once(
	build(O_EQ, build(O_SYM, pcsym), build(O_SYM, retaddrsym)),
	buildcmdlist(build(O_PROCRTN, proc))
    );
    isstopped = false;
    if (not bpact()) {
	isstopped = true;
	cont(0);
    }
    /*
     * bpact() won't return true, it will call printstatus() and go back
     * to command input if a breakpoint is found.
     */
    /* NOTREACHED */
}

/*
 * Check to see if an expression is correct for a given parameter.
 * If the given parameter is false, don't worry about type inconsistencies.
 *
 * Return whether or not it is ok.
 */

private boolean chkparam (actual, formal, chk)
Node actual;
Symbol formal;
boolean chk;
{
    boolean b;

    b = true;
    if (chk) {
	if (formal == nil) {
	    beginerrmsg();
	    fprintf(stderr, "too many parameters");
	    b = false;
	} else if (not compatible(formal->type, actual->nodetype)) {
	    beginerrmsg();
	    fprintf(stderr, "type mismatch for %s", symname(formal));
	    b = false;
	}
    }
    if (b and formal != nil and
	isvarparam(formal) and not isopenarray(formal->type) and
	not (
	    actual->op == O_RVAL or actual->nodetype == t_addr or
	    (
		actual->op == O_TYPERENAME and
		(
		    actual->value.arg[0]->op == O_RVAL or
		    actual->value.arg[0]->nodetype == t_addr
		)
	    )
	)
    ) {
	beginerrmsg();
	fprintf(stderr, "expected variable, found \"");
	prtree(stderr, actual);
	fprintf(stderr, "\"");
	b = false;
    }
    return b;
}

/*
 * Evaluate an argument list left-to-right.
 */

public integer evalargs (proc, arglist)
Symbol proc;
Node arglist;
{
    Node p, actual;
    Symbol formal;
    Stack *savesp;
    integer count;
    boolean chk;

    savesp = sp;
    count = 0;
    formal = proc->chain;
    chk = (boolean) (not nosource(proc));
    for (p = arglist; p != nil; p = p->value.arg[1]) {
	assert(p->op == O_COMMA);
	actual = p->value.arg[0];
	if (not chkparam(actual, formal, chk)) {
	    fprintf(stderr, " in call to %s", symname(proc));
	    sp = savesp;
	    popenv();
	    enderrmsg();
	}
	passparam(actual, formal);
	if (formal != nil) {
	    formal = formal->chain;
	}
	++count;
    }
    if (chk) {
	if (formal != nil) {
	    sp = savesp;
	    popenv();
	    error("not enough parameters to %s", symname(proc));
	}
    }
    return count;
}

/*
 * Evaluate an argument list without any type checking.
 * This is only useful for procedures with a varying number of
 * arguments that are compiled -g.
 */

public integer unsafe_evalargs (proc, arglist)
Symbol proc;
Node arglist;
{
    Node p;
    integer count;

    count = 0;
    for (p = arglist; p != nil; p = p->value.arg[1]) {
	assert(p->op == O_COMMA);
	eval(p->value.arg[0]);
	++count;
    }
    return count;
}

public procreturn (f)
Symbol f;
{
    int r;
    Node n;
    char *data;

    popenv();
    if (endproc.isfunc) {
	r = size(f->type);
	if (r > sizeof(long)) {
	    pushretval(r, true);
	    data = newarr(char, r);
	    popn(r, data);
	    n = build(O_SCON, data);
	} else {
	    pushretval(r, false);
	    n = build(O_LCON, (long) popsmall(f->type));
	}
	flushoutput();
	n->nodetype = f->type;
	tfree(endproc.callnode);
	*endproc.callnode = *n;
	dispose(n);
	eval(endproc.cmdnode);
    } else {
	flushoutput();
	putchar('\n');
	printname(stdout, f);
	printf(" returns successfully\n", symname(f));
    }
    erecover();
}

/*
 * Push the current environment.
 */

public pushenv ()
{
    push(Address, pc);
    push(Lineno, curline);
    push(String, cursource);
    push(int, (int) isstopped);
    push(Symbol, curfunc);
    push(Frame, curframe);
    push(struct Frame, curframerec);
    push(CallEnv, endproc);
    push(Word, reg(PROGCTR));
    push(Word, reg(STKP));
    push(Word, reg(FRP));
}

/*
 * Pop back to the real world.
 */

public popenv ()
{
    String filename;

    setreg(FRP, pop(Word));
    setreg(STKP, pop(Word));
    setreg(PROGCTR, pop(Word));
    endproc = pop(CallEnv);
    curframerec = pop(struct Frame);
    curframe = pop(Frame);
    curfunc = pop(Symbol);
    isstopped = (Boolean) pop(int);
    filename = pop(String);
    curline = pop(Lineno);
    pc = pop(Address);
    setsource(filename);
}
