From 7a1c9c7798061298c4b7dbd9d3903c00f193c67d Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sun, 5 Feb 2023 15:27:39 -0600 Subject: [PATCH] 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. --- src/boot/boot.janet | 13 ++++++------- src/core/asm.c | 7 ++++++- src/core/compile.c | 37 ++++++++++++++++++++++++++++++++++--- src/core/compile.h | 7 ++++++- src/core/debug.c | 13 +++++++++++-- 5 files changed, 63 insertions(+), 14 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index e016b52b..ae909686 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -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] diff --git a/src/core/asm.c b/src/core/asm.c index 0f3f0fed..41afe409 100644 --- a/src/core/asm.c +++ b/src/core/asm.c @@ -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); diff --git a/src/core/compile.c b/src/core/compile.c index 1eb32e6f..3c31049f 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -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) { diff --git a/src/core/compile.h b/src/core/compile.h index 6e3b595c..dc6ae912 100644 --- a/src/core/compile.h +++ b/src/core/compile.h @@ -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; diff --git a/src/core/debug.c b/src/core/debug.c index 0faf68b9..685543bb 100644 --- a/src/core/debug.c +++ b/src/core/debug.c @@ -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)); }