/* "debug.c" procedures for displaying and debugging code.
* Copyright (C) 2001 Free Software Foundation, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program. If not, see
* .
*/
/* Authors: Radey Shouman & Aubrey Jaffer. */
#include "scm.h"
#include "setjump.h"
long tc16_codeptr; /* Type code for non-executable code
wrappers */
static SCM prinhead P((SCM x, SCM port, int writing));
static void prinbindings P((SCM names, SCM inits, SCM init_env,
SCM steps, SCM step_env, SCM port, int writing));
SCM scm_env_rlookup(addr, stenv, what)
SCM addr, stenv;
const char *what;
{
SCM env, fr;
int icdrp;
unsigned int idist, iframe;
if (IMP(addr)) {
if (!ILOCP(addr)) return BOOL_F;
iframe = IFRAME(addr);
idist = IDIST(addr);
icdrp = ICDRP(addr) && 1;
}
else {
if (!ISYMP(CAR(addr))) return BOOL_F;
icdrp = 0;
switch (ISYMNUM(CAR(addr))) {
default: return BOOL_F;
case (ISYMNUM(IM_FARLOC_CDR)):
icdrp = 1;
case (ISYMNUM(IM_FARLOC_CAR)):
fr = CDR(addr);
iframe = INUM(CAR(fr));
idist = INUM(CDR(fr));
break;
}
}
for (env = stenv; NIMP(env); env = CDR(env)) {
fr = CAR(env);
if (INUMP(fr)) {
ASRTER(NIMP(env) && CONSP(env), stenv, s_badenv, what);
env = CDR(env);
continue;
}
if (SCM_LINUMP(fr)) continue;
if (NIMP(fr) && CONSP(fr) && IMP(CAR(fr))) continue;
if (0==iframe--) {
while (idist--) {
if (IMP(fr) || NCONSP(fr)) return BOOL_F;
fr = CDR(fr);
}
if (!icdrp) {
if (NIMP(fr) && CONSP(fr))
fr = CAR(fr);
else
fr = BOOL_F;
}
if (NIMP(fr) && IDENTP(fr)) return fr;
return BOOL_F;
}
}
return BOOL_F;
}
SCM scm_env_addprop(prop, val, env)
SCM prop, val, env;
{
if (UNBNDP(prop)) return env;
return cons2(prop, val, env);
}
SCM scm_env_getprop(prop, env)
SCM prop, env;
{
SCM e = env;
if (!UNBNDP(prop)) {
while (NIMP(e)) {
if (INUMP(CAR(e))) {
if (CAR(e)==prop) return CDR(e);
e = CDR(e);
ASRTER(NIMP(e), env, s_badenv, "env_getprop");
}
e = CDR(e);
}
}
return BOOL_F;
}
static SCM prinhead(x, port, writing)
SCM x, port;
int writing;
{
lputc('(', port);
scm_iprin1(CAR(x), port, writing);
lputc(' ', port);
return CDR(x);
}
static void prinbindings(names, inits, init_env,
steps, step_env, port, writing)
SCM names, inits, init_env, steps, step_env, port;
int writing;
{
lputc('(', port);
while (NIMP(names) && NIMP(inits)) {
lputc('(', port);
scm_iprin1(CAR(names), port, writing);
lputc(' ', port);
scm_princode(CAR(inits), init_env, port, writing);
if (NIMP(steps)) {
lputc(' ', port);
scm_princode(CAR(steps), step_env, port, writing);
steps = CDR(steps);
}
lputc(')', port);
names = CDR(names);
inits = CDR(inits);
if (NIMP(names)) lputc(' ', port);
}
lputs(") ", port);
}
void scm_princode(code, env, port, writing)
SCM code, env, port;
int writing;
{
SCM oenv = env, name, init, bdgs, x = code;
if (UNBNDP(env)) {
scm_iprin1(code, port, writing);
return;
}
tail:
if (IMP(x)) {
if (ILOCP(x)) {
local:
name = scm_env_rlookup(x, env, "princode");
if (FALSEP(name)) goto gencase;
lputs("#@", port);
scm_iprin1(name, port, writing);
return;
}
else
goto gencase;
}
if (SCM_GLOCP(x)) {
scm_iprin1(x, port, writing);
return;
}
switch (TYP7(x)) {
default: gencase:
scm_iprin1(x, port, writing);
return;
gencode:
case tcs_cons_gloc:
case tcs_cons_nimcar:
case tcs_cons_iloc:
case (127 & IM_OR): case (127 & IM_AND): case (127 & IM_BEGIN):
case (127 & IM_SET): case (127 & IM_COND): case (127 & IM_CASE):
case (127 & IM_IF):
lputc('(', port);
scm_princode(CAR(x), env, port, writing);
body:
x = CDR(x);
no_cdr:
for (; NNULLP(x); x = CDR(x)) {
if (IMP(x) || NECONSP(x)) {
lputs(" . ", port);
scm_iprin1(x, port, writing);
break;
}
lputc(' ', port);
scm_princode(CAR(x), env, port, writing);
}
lputc(')', port);
return;
case (127 & IM_LAMBDA):
x = prinhead(x, port, writing);
env = CAR(x);
bdgs = SCM_ENV_FORMALS(env);
if (IMP(bdgs) || NECONSP(bdgs))
scm_iprin1(bdgs, port, writing);
else {
lputc('(', port);
while (!0) {
if (NECONSP(bdgs)) break;
scm_iprin1(CAR(bdgs), port, writing);
if (NIMP(bdgs = CDR(bdgs)))
lputc(' ', port);
else break;
}
if (NIMP(bdgs)) {
lputs(". ", port);
scm_iprin1(bdgs, port, writing);
}
lputc(')', port);
}
goto body;
case (127 & IM_LETREC):
case (127 & IM_LET):
x = prinhead(x, port, writing);
env = CAR(x);
prinbindings(SCM_ENV_FORMALS(env),
CAR(CDR(x)), (TYP7(x)==(127 & IM_LET) ? oenv: env),
UNDEFINED, UNDEFINED, port, writing);
x = CDR(x);
goto body;
case (127 & IM_LETSTAR):
x = prinhead(x, port, writing);
lputc('(', port);
if (NIMP(bdgs = CAR(x))) {
oenv = CAR(bdgs);
bdgs = CDR(bdgs);
while (!0) {
init = CAR(bdgs);
bdgs = CDR(bdgs);
env = CAR(bdgs);
lputc('(', port);
scm_iprin1(SCM_ENV_FORMALS(env), port, writing);
lputc(' ', port);
scm_princode(init, oenv, port, writing);
oenv = env;
lputc(')', port);
if (IMP(bdgs = CDR(bdgs)))
break;
lputc(' ', port);
}
}
lputs(") ", port);
goto body;
case (127 & IM_DO):
{
/* (#@do (env (init ...) (test ...) (body ...) step ...)) */
SCM test, steps;
x = prinhead(x, port, writing);
env = CAR(x);
x = CDR(x);
init = CAR(x);
x = CDR(x);
test = CAR(x);
x = CDR(x);
steps = CDR(x);
x = CAR(x);
prinbindings(SCM_ENV_FORMALS(env), init, oenv, steps, env,
port, writing);
scm_princode(test, env, port, writing);
lputc(' ', port);
goto no_cdr;
}
case (127 & IM_FUNCALL):
lputc('(', port);
x = CDR(x);
scm_princode(CAR(x), env, port, writing);
goto body;
case (127 & MAKISYM(0)):
if (!ISYMP(CAR(x))) goto gencode;
switch (ISYMNUM(CAR(x))) {
default:
goto gencode;
case ISYMNUM(IM_LINUM):
x = CDR(x);
goto tail;
case ISYMNUM(IM_FARLOC_CAR):
case ISYMNUM(IM_FARLOC_CDR):
goto local;
}
}
}
void scm_princlosure(proc, port, writing)
SCM proc, port;
int writing;
{
SCM env, linum = UNDEFINED;
proc = CODE(proc);
lputs("# ", port);
env = CAR(proc);
if (NIMP(env=scm_env_getprop(SCM_ENV_FILENAME, env)))
scm_line_msg(CAR(env), linum, port);
#endif
env = CAR(proc);
scm_iprin1(SCM_ENV_FORMALS(env), port, writing);
if (writing) {
for (proc = CDR(proc); NIMP(proc); proc = CDR(proc)) {
lputc(' ', port);
scm_princode(CAR(proc), env, port, writing);
}
}
lputc('>', port);
}
static char s_int2linum[] = "integer->line-number";
SCM scm_int2linum(n)
SCM n;
{
int i = INUM(n);
ASRTER(INUMP(n) && i >= 0, n, ARG1, s_int2linum);
return SCM_MAKE_LINUM(i);
}
static char s_linum2int[] = "line-number->integer";
SCM scm_linum2int(linum)
SCM linum;
{
ASRTER(SCM_LINUMP(linum), linum, ARG1, s_linum2int);
return MAKINUM(SCM_LINUM(linum));
}
SCM scm_linump(obj)
SCM obj;
{
return SCM_LINUMP(obj) ? BOOL_T : BOOL_F;
}
static char s_remove_linums[] = "remove-line-numbers!";
SCM scm_remove_linums(x)
SCM x;
{
SCM ret = x;
SCM *px = &ret;
tail:
x = *px;
if (IMP(x)) return ret;
if (CONSP(x)) {
if (SCM_LINUMP(CAR(x))) {
*px = CDR(x);
px = &CDR(x);
goto tail;
}
if (NIMP(CAR(x)))
CAR(x) = scm_remove_linums(CAR(x));
px = &CDR(x);
goto tail;
}
else if (VECTORP(x)) {
SCM *ve = VELTS(x);
sizet i = LENGTH(x);
while (i--) {
if (NIMP(ve[i]))
ve[i] = scm_remove_linums(ve[i]);
}
return ret;
}
else
return ret;
}
#ifdef CAUTIOUS
long num_frames(estk, i)
SCM estk;
int i;
{
long n = 0;
while NIMP(estk) {
n += (i - SCM_ESTK_BASE)/SCM_ESTK_FRLEN;
i = INUM(SCM_ESTK_PARENT_INDEX(estk));
estk = SCM_ESTK_PARENT(estk);
}
return n;
}
SCM *estk_frame(estk, i, nf)
SCM estk;
int i, nf;
{
int n;
/* Make this 1-based, because continuations have an extra frame at
the top of the estk. */
nf -= 1;
while NIMP(estk) {
n = (i - SCM_ESTK_BASE)/SCM_ESTK_FRLEN;
if (nf <= n) return &(VELTS(estk)[i - nf*SCM_ESTK_FRLEN]);
nf -= n;
i = INUM(SCM_ESTK_PARENT_INDEX(estk));
estk = SCM_ESTK_PARENT(estk);
}
return (SCM *)0;
}
SCM stacktrace1(estk, i)
SCM estk;
int i;
{
SCM *frame, env, ste, lste = UNDEFINED;
int n, nf = num_frames(estk, i);
int nbrk1 = 7, nbrk2 = nf - 6;
if (nf <= 0) return BOOL_F;
lputs("\n;STACK TRACE", cur_errp);
for (n = 1; n <= nf; n++) {
if ((0 <= nbrk1--) || n >= nbrk2) {
if (!(frame = estk_frame(estk, i, n))) continue;
if (BOOL_F==(ste = frame[3])) continue;
env = frame[2];
if (ste != lste) {
lste = ste;
if (reset_safeport(sys_safep, 65, cur_errp)) {
/* The usual C setjmp, not SCM's setjump. */
if (0==setjmp(SAFEP_JMPBUF(sys_safep))) {
lputc('\n', cur_errp);
scm_intprint((long)n, -10, sys_safep);
lputs("; ", sys_safep);
scm_princode(ste, env, sys_safep, 1);
}
}
}
else {
lputs("\n...", cur_errp);
break;
}
}
}
lputc('\n', cur_errp);
return BOOL_T;
}
SCM *cont_frame(contin, nf)
SCM contin;
int nf;
{
CONTINUATION *cont = CONT(contin);
SCM estk = cont->other.estk;
int i = LENGTH(estk) - SCM_ESTK_FRLEN;
if (0 == nf) return cont->other.stkframe;
return estk_frame(estk, i, nf);
}
static char s_stack_trace[] = "stack-trace";
SCM scm_stack_trace(contin)
SCM contin;
{
SCM estk;
int i;
if (UNBNDP(contin)) {
estk = scm_estk;
i = (scm_estk_ptr - VELTS(scm_estk));
}
else {
CONTINUATION *cont;
ASRTER(NIMP(contin) && (tc7_contin==TYP7(contin)), contin, ARG1,
s_stack_trace);
cont = CONT(contin);
estk = cont->other.estk;
i = LENGTH(estk) - SCM_ESTK_FRLEN;
}
return stacktrace1(estk, i);
}
static char s_frame_trace[] = "frame-trace";
SCM scm_frame_trace(contin, nf)
SCM contin, nf;
{
SCM *stkframe, code, env;
ASRTER(NIMP(contin) && tc7_contin==TYP7(contin), contin, ARG1,
s_frame_trace);
ASRTER(INUMP(nf) && INUM(nf) >= 0, nf, ARG2, s_frame_trace);
if (!(stkframe = cont_frame(contin, INUM(nf))))
return BOOL_F;
env = stkframe[2];
code = stkframe[3];
scm_princode(code, env, cur_errp, 1);
scm_scope_trace(env);
return UNSPECIFIED;
}
static char s_frame2env[] = "frame->environment";
SCM scm_frame2env(contin, nf)
SCM contin, nf;
{
SCM *stkframe;
ASRTER(NIMP(contin) && tc7_contin==TYP7(contin), contin, ARG1,
s_frame2env);
ASRTER(INUMP(nf) && INUM(nf) >= 0, nf, ARG2, s_frame2env);
if (!(stkframe = cont_frame(contin, INUM(nf))))
return BOOL_F;
return stkframe[2];
}
static char s_frame_eval[] = "frame-eval";
SCM scm_frame_eval(contin, nf, expr)
SCM contin, nf, expr;
{
SCM res, env, *stkframe;
ASRTER(NIMP(contin) && tc7_contin==TYP7(contin), contin, ARG1,
s_frame_eval);
ASRTER(INUMP(nf) && INUM(nf) >= 0, nf, ARG2, s_frame_eval);
if (!(stkframe = cont_frame(contin, INUM(nf))))
return BOOL_F;
env = stkframe[2];
if (IMP(expr)) return expr;
DEFER_INTS_EGC;
res = ceval(expr, env, stkframe[0]);
ALLOW_INTS_EGC;
return res;
}
#endif
static char s_scope_trace[] = "scope-trace";
SCM scm_scope_trace(env)
SCM env;
{
SCM ef, file = UNDEFINED;
int fprinted = 0;
if (UNBNDP(env))
env = scm_current_env();
else if (NIMP(env) && CLOSUREP(env))
env = CAR(CODE(env));
if (scm_nullenv_p(env))
lputs("\n; in top level environment.", cur_errp);
else
lputs("\n; in scope:", cur_errp);
#ifdef CAUTIOUS
if (NIMP(ef=scm_env_getprop(SCM_ENV_FILENAME, env))) {
file = CAR(ef);
}
#endif
for (; NIMP(env); env = CDR(env)) {
if (NCONSP(env)) {
badenv:
lputs("\n; corrupted environment ", cur_errp);
scm_iprin1(env, cur_errp, 1);
return UNSPECIFIED;
}
ef = CAR(env);
if (SCM_LINUMP(ef)) {
lputs("\n; ", cur_errp);
scm_line_msg(file, ef, cur_errp);
fprinted++;
}
else if (INUMP(ef)) {
ASRTGO(NIMP(env) && CONSP(env), badenv);
env = CDR(env);
#ifdef CAUTIOUS
switch (ef) {
default: break;
case SCM_ENV_PROCNAME:
lputs(" procedure ", cur_errp);
scm_iprin1(CAR(env), cur_errp, 1);
break;
}
#endif
}
else if (NIMP(ef) && CONSP(ef) && NIMP(CAR(ef)) && CONSP(CAR(ef))) {
lputs("\n; ", cur_errp);
scm_iprin1(CAR(ef), cur_errp, 1);
lputs(" syntax bindings", cur_errp);
}
else {
lputs("\n; ", cur_errp);
scm_iprin1(ef, cur_errp, 1);
}
}
#ifdef CAUTIOUS
if (NIMP(file) && !fprinted) {
lputs("\n; defined by ", cur_errp);
if (NIMP(file) && STRINGP(file))
lputs("load: ", cur_errp);
scm_iprin1(file, cur_errp, 1);
lputc('\n', cur_errp);
}
#endif
return UNSPECIFIED;
}
static char s_env_annotation[] = "environment-annotation";
SCM scm_env_annotation(var, stenv)
SCM var, stenv;
{
SCM s, frame, env = stenv;
#ifdef MACRO
SCM mark = IDENT_ENV(var);
if (NIMP(mark)) mark = CAR(mark);
#endif
for (; NIMP(env); env = CDR(env)) {
frame = CAR(env);
#ifdef MACRO
if (frame==mark) {
var = IDENT_PARENT(var);
mark = IDENT_ENV(var);
if (NIMP(mark)) mark = CAR(mark);
}
#endif
if (IMP(frame)) {
if (INUMP(frame)) {
#ifndef RECKLESS
if (!(NIMP(env) && CONSP(env))) {
badenv: wta(stenv, s_badenv, s_env_annotation);
}
#endif
env = CDR(env);
}
continue;
}
#ifdef MACRO
if (NIMP(frame) && CONSP(frame) && BOOL_F==CAR(frame)) {
/* syntax binding */
s = assq(var, CDR(frame));
if (NIMP(s)) goto local_out;
continue;
}
#endif
for (; NIMP(frame); frame = CDR(frame)) {
if (NCONSP(frame)) {
if (var==frame)
goto local_out;
break;
}
if (CAR(frame)==var) {
local_out:
env = CDR(env);
if (IMP(env)) return BOOL_T;
if (SCM_ENV_ANNOTATION != CAR(env)) return BOOL_T;
env = CDR(env);
ASRTGO(NIMP(env), badenv);
s = assq(var, CAR(env));
if (NIMP(s)) return s;
return BOOL_T;
}
ASRTGO(CONSP(frame), badenv);
}
}
ASRTGO(NULLP(env), badenv);
return BOOL_F;
}
/* This is to be used for code backpointers to go into environments,
allowing run-time reporting of error line numbers. */
SCM scm_wrapcode(code, env)
SCM code, env;
{
SCM z, x = cons(env, code);
NEWCELL(z);
CDR(z) = x;
CAR(z) = tc16_codeptr;
return z;
}
static int princodeptr(exp, port, writing)
SCM exp;
SCM port;
int writing;
{
SCM env = CAR(CDR(exp));
lputs("#', port);
return !0;
}
static smobfuns codesmob = {markcdr, free0, princodeptr};
static iproc subr1os[] = {
{s_scope_trace, scm_scope_trace},
#ifdef CAUTIOUS
{s_stack_trace, scm_stack_trace},
#endif
{0, 0}};
static iproc subr1s[] = {
{s_int2linum, scm_int2linum},
{"line-number?", scm_linump},
{s_linum2int, scm_linum2int},
{s_remove_linums, scm_remove_linums},
{0, 0}};
static iproc subr2s[] = {
{s_env_annotation, scm_env_annotation},
#ifdef CAUTIOUS
{s_frame_trace, scm_frame_trace},
{s_frame2env, scm_frame2env},
#endif
{0, 0}};
void init_debug()
{
tc16_codeptr = newsmob(&codesmob);
init_iprocs(subr1os, tc7_subr_1o);
init_iprocs(subr1s, tc7_subr_1);
init_iprocs(subr2s, tc7_subr_2);
#ifdef CAUTIOUS
make_subr(s_frame_eval, tc7_subr_3, scm_frame_eval);
#endif
}