Add support for debugging upvalues.

Upvalues are stored in the symbol slots structure as well, but
since they are always live, we repurpose the death_pc field to
refer to the environment index that we want to look at at runtime.
This commit is contained in:
Calvin Rose 2023-02-05 15:27:39 -06:00
parent c7fb7b4451
commit 7a1c9c7798
5 changed files with 63 additions and 14 deletions

View File

@ -2767,12 +2767,6 @@
(put nextenv :debug-level level)
(put nextenv :signal (fiber/last-value fiber))
# define variables available at breakpoint
(def frame ((debug/stack fiber) 0))
(def pc (frame :pc))
(eachp [local value] (get frame :locals)
(put nextenv local @{:value value}))
(merge-into nextenv debugger-env)
(defn debugger-chunks [buf p]
(def status (:state p :delimiters))
@ -3381,11 +3375,16 @@
(print))
(defn .frame
"Show a stack frame."
"Show a stack frame"
[&opt n]
(def stack (debug/stack (.fiber)))
(in stack (or n 0)))
(defn .locals
"Show local bindings"
[&opt n]
(get (.frame n) :locals))
(defn .fn
"Get the current function."
[&opt n]

View File

@ -928,10 +928,15 @@ static Janet janet_disasm_symbolslots(JanetFuncDef *def) {
return janet_wrap_nil();
}
JanetArray *symbolslots = janet_array(def->symbolmap_length);
Janet upvaluekw = janet_ckeywordv("upvalue");
for (int32_t i = 0; i < def->symbolmap_length; i++) {
JanetSymbolMap ss = def->symbolmap[i];
Janet *t = janet_tuple_begin(4);
t[0] = janet_wrap_integer(ss.birth_pc);
if (ss.birth_pc == UINT32_MAX) {
t[0] = upvaluekw;
} else {
t[0] = janet_wrap_integer(ss.birth_pc);
}
t[1] = janet_wrap_integer(ss.death_pc);
t[2] = janet_wrap_integer(ss.slot_index);
t[3] = janet_wrap_symbol(ss.symbol);

View File

@ -344,6 +344,7 @@ found:
}
/* non-local scope needs to expose its environment */
JanetScope *original_scope = scope;
pair->keep = 1;
while (scope && !(scope->flags & JANET_SCOPE_FUNCTION))
scope = scope->parent;
@ -365,7 +366,7 @@ found:
/* Check if scope already has env. If so, break */
len = janet_v_count(scope->envs);
for (j = 0; j < len; j++) {
if (scope->envs[j] == envindex) {
if (scope->envs[j].envindex == envindex) {
scopefound = 1;
envindex = j;
break;
@ -374,7 +375,10 @@ found:
/* Add the environment if it is not already referenced */
if (!scopefound) {
len = janet_v_count(scope->envs);
janet_v_push(scope->envs, envindex);
JanetEnvRef ref;
ref.envindex = envindex;
ref.scope = original_scope;
janet_v_push(scope->envs, ref);
envindex = len;
}
}
@ -878,7 +882,10 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
/* Copy envs */
def->environments_length = janet_v_count(scope->envs);
def->environments = janet_v_flatten(scope->envs);
def->environments = janet_malloc(sizeof(int32_t) * def->environments_length);
for (int32_t i = 0; i < def->environments_length; i++) {
def->environments[i] = scope->envs[i].envindex;
}
def->constants_length = janet_v_count(scope->consts);
def->constants = janet_v_flatten(scope->consts);
@ -935,6 +942,30 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
/* Capture symbol to local mapping */
JanetSymbolMap *locals = NULL;
/* Symbol -> upvalue mapping */
JanetScope *top = c->scope;
while (top->parent) top = top->parent;
for (JanetScope *s = top; s != NULL; s = s->child) {
for (int32_t j = 0; j < janet_v_count(scope->envs); j++) {
JanetEnvRef ref = scope->envs[j];
JanetScope *upscope = ref.scope;
if (upscope != s) continue;
for (int32_t i = 0; i < janet_v_count(upscope->syms); i++) {
SymPair pair = upscope->syms[i];
if (pair.sym2) {
JanetSymbolMap jsm;
jsm.birth_pc = UINT32_MAX;
jsm.death_pc = j;
jsm.slot_index = pair.slot.index;
jsm.symbol = pair.sym2;
janet_v_push(locals, jsm);
}
}
}
}
/* Symbol -> slot mapping */
for (int32_t i = 0; i < janet_v_count(scope->syms); i++) {
SymPair pair = scope->syms[i];
if (pair.sym2) {

View File

@ -117,6 +117,11 @@ typedef struct SymPair {
uint32_t death_pc;
} SymPair;
typedef struct JanetEnvRef {
int32_t envindex;
JanetScope *scope;
} JanetEnvRef;
/* A lexical scope during compilation */
struct JanetScope {
@ -145,7 +150,7 @@ struct JanetScope {
/* Referenced closure environments. The values at each index correspond
* to which index to get the environment from in the parent. The environment
* that corresponds to the direct parent's stack will always have value 0. */
int32_t *envs;
JanetEnvRef *envs;
int32_t bytecode_start;
int flags;

View File

@ -334,10 +334,19 @@ static Janet doframe(JanetStackFrame *frame) {
JanetTable *local_bindings = janet_table(0);
for (int32_t i = def->symbolmap_length - 1; i >= 0; i--) {
JanetSymbolMap jsm = def->symbolmap[i];
Janet value = janet_wrap_nil();
uint32_t pc = (uint32_t)(frame->pc - def->bytecode);
if (pc >= jsm.birth_pc && pc < jsm.death_pc) {
janet_table_put(local_bindings, janet_wrap_symbol(jsm.symbol), stack[jsm.slot_index]);
if (jsm.birth_pc == UINT32_MAX) {
JanetFuncEnv *env = frame->func->envs[jsm.death_pc];
if (env->offset > 0) {
value = env->as.fiber->data[env->offset + jsm.slot_index];
} else {
value = env->as.values[jsm.slot_index];
}
} else if (pc >= jsm.birth_pc && pc < jsm.death_pc) {
value = stack[jsm.slot_index];
}
janet_table_put(local_bindings, janet_wrap_symbol(jsm.symbol), value);
}
janet_table_put(t, janet_ckeywordv("locals"), janet_wrap_table(local_bindings));
}