1
0
mirror of https://github.com/janet-lang/janet synced 2024-11-28 11:09:54 +00:00

Make source mapping use byte offset instead of line and col

for better debugging support in repl. Add debug module for better
debugging support.
This commit is contained in:
Calvin Rose 2018-12-13 18:46:53 -05:00
parent 4b01409d2d
commit e8c0dcd14e
16 changed files with 400 additions and 187 deletions

View File

@ -710,8 +710,8 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
if (!janet_checktype(tup[1], JANET_INTEGER)) { if (!janet_checktype(tup[1], JANET_INTEGER)) {
janet_asm_error(&a, "expected integer"); janet_asm_error(&a, "expected integer");
} }
mapping.line = janet_unwrap_integer(tup[0]); mapping.start = janet_unwrap_integer(tup[0]);
mapping.column = janet_unwrap_integer(tup[1]); mapping.end = janet_unwrap_integer(tup[1]);
def->sourcemap[i] = mapping; def->sourcemap[i] = mapping;
} }
} }
@ -876,8 +876,8 @@ Janet janet_disasm(JanetFuncDef *def) {
for (i = 0; i < def->bytecode_length; i++) { for (i = 0; i < def->bytecode_length; i++) {
Janet *t = janet_tuple_begin(2); Janet *t = janet_tuple_begin(2);
JanetSourceMapping mapping = def->sourcemap[i]; JanetSourceMapping mapping = def->sourcemap[i];
t[0] = janet_wrap_integer(mapping.line); t[0] = janet_wrap_integer(mapping.start);
t[1] = janet_wrap_integer(mapping.column); t[1] = janet_wrap_integer(mapping.end);
sourcemap->data[i] = janet_wrap_tuple(janet_tuple_end(t)); sourcemap->data[i] = janet_wrap_tuple(janet_tuple_end(t));
} }
sourcemap->count = def->bytecode_length; sourcemap->count = def->bytecode_length;

View File

@ -125,10 +125,10 @@ int32_t janet_verify(JanetFuncDef *def) {
for (i = 0; i < def->bytecode_length; i++) { for (i = 0; i < def->bytecode_length; i++) {
uint32_t instr = def->bytecode[i]; uint32_t instr = def->bytecode[i];
/* Check for invalid instructions */ /* Check for invalid instructions */
if ((instr & 0xFF) >= JOP_INSTRUCTION_COUNT) { if ((instr & 0x7F) >= JOP_INSTRUCTION_COUNT) {
return 3; return 3;
} }
enum JanetInstructionType type = janet_instructions[instr & 0xFF]; enum JanetInstructionType type = janet_instructions[instr & 0x7F];
switch (type) { switch (type) {
case JINT_0: case JINT_0:
continue; continue;

View File

@ -462,9 +462,9 @@ static int macroexpand1(
if (janet_tuple_length(form) == 0) if (janet_tuple_length(form) == 0)
return 0; return 0;
/* Source map - only set when we get a tuple */ /* Source map - only set when we get a tuple */
if (janet_tuple_sm_line(form) > 0) { if (janet_tuple_sm_start(form) >= 0) {
c->current_mapping.line = janet_tuple_sm_line(form); c->current_mapping.start = janet_tuple_sm_start(form);
c->current_mapping.column = janet_tuple_sm_col(form); c->current_mapping.end = janet_tuple_sm_end(form);
} }
if (!janet_checktype(form[0], JANET_SYMBOL)) if (!janet_checktype(form[0], JANET_SYMBOL))
return 0; return 0;
@ -648,15 +648,15 @@ static void janetc_init(JanetCompiler *c, JanetTable *env, const uint8_t *where)
c->recursion_guard = JANET_RECURSION_GUARD; c->recursion_guard = JANET_RECURSION_GUARD;
c->env = env; c->env = env;
c->source = where; c->source = where;
c->current_mapping.line = 0; c->current_mapping.start = -1;
c->current_mapping.column = 0; c->current_mapping.end = -1;
/* Init result */ /* Init result */
c->result.error = NULL; c->result.error = NULL;
c->result.status = JANET_COMPILE_OK; c->result.status = JANET_COMPILE_OK;
c->result.funcdef = NULL; c->result.funcdef = NULL;
c->result.macrofiber = NULL; c->result.macrofiber = NULL;
c->result.error_mapping.line = 0; c->result.error_mapping.start = -1;
c->result.error_mapping.column = 0; c->result.error_mapping.end = -1;
} }
/* Deinitialize a compiler struct */ /* Deinitialize a compiler struct */
@ -717,8 +717,8 @@ static int cfun(JanetArgs args) {
} else { } else {
t = janet_table(4); t = janet_table(4);
janet_table_put(t, janet_csymbolv(":error"), janet_wrap_string(res.error)); janet_table_put(t, janet_csymbolv(":error"), janet_wrap_string(res.error));
janet_table_put(t, janet_csymbolv(":line"), janet_wrap_integer(res.error_mapping.line)); janet_table_put(t, janet_csymbolv(":start"), janet_wrap_integer(res.error_mapping.start));
janet_table_put(t, janet_csymbolv(":column"), janet_wrap_integer(res.error_mapping.column)); janet_table_put(t, janet_csymbolv(":end"), janet_wrap_integer(res.error_mapping.end));
if (res.macrofiber) { if (res.macrofiber) {
janet_table_put(t, janet_csymbolv(":fiber"), janet_wrap_fiber(res.macrofiber)); janet_table_put(t, janet_csymbolv(":fiber"), janet_wrap_fiber(res.macrofiber));
} }

View File

@ -1252,11 +1252,11 @@ value, one key will be ignored."
(res) (res)
(do (do
(:= good false) (:= good false)
(def {:error err :line errl :column errc :fiber errf} res) (def {:error err :start start :end end :fiber errf} res)
(onstatus (onstatus
:compile :compile
(if (< 0 errl) (if (<= 0 start)
(string err "\n in a form at line " errl ", column " errc) (string err "\n at (" start ":" end ")")
err) err)
errf errf
where)))) where))))
@ -1309,7 +1309,7 @@ value, one key will be ignored."
"\n") "\n")
(when f (when f
(loop (loop
[nf :in (reverse (fiber/lineage f)) [nf :in (reverse (debug/lineage f))
:before (file/write stderr " (fiber)\n") :before (file/write stderr " (fiber)\n")
{:function func {:function func
:tail tail :tail tail
@ -1317,8 +1317,8 @@ value, one key will be ignored."
:c c :c c
:name name :name name
:source source :source source
:line source-line :source-start start
:column source-col} :in (fiber/stack nf)] :source-end end} :in (debug/stack nf)]
(file/write stderr " in") (file/write stderr " in")
(when c (file/write stderr " cfunction")) (when c (file/write stderr " cfunction"))
(if name (if name
@ -1327,14 +1327,15 @@ value, one key will be ignored."
(if source (if source
(do (do
(file/write stderr " [" source "]") (file/write stderr " [" source "]")
(if source-line (if start
(file/write (file/write
stderr stderr
" on line " " at ("
(string source-line) (string start)
", column " ":"
(string source-col))))) (string end)
(if (and (not source-line) pc) ")"))))
(if (and (not start) pc)
(file/write stderr " (pc=" (string pc) ")")) (file/write stderr " (pc=" (string pc) ")"))
(when tail (file/write stderr " (tailcall)")) (when tail (file/write stderr " (tailcall)"))
(file/write stderr "\n")))) (file/write stderr "\n"))))
@ -1507,6 +1508,7 @@ value, one key will be ignored."
(def newenv (make-env)) (def newenv (make-env))
(default chunks (fn [buf _] (file/read stdin :line buf))) (default chunks (fn [buf _] (file/read stdin :line buf)))
(default onsignal (fn [sig x f source] (default onsignal (fn [sig x f source]
(put newenv '_fiber @{:value f})
(case sig (case sig
:dead (do :dead (do
(put newenv '_ @{:value x}) (put newenv '_ @{:value x})

View File

@ -801,6 +801,7 @@ JanetTable *janet_core_env(void) {
janet_lib_os(args); janet_lib_os(args);
janet_lib_parse(args); janet_lib_parse(args);
janet_lib_compile(args); janet_lib_compile(args);
janet_lib_debug(args);
janet_lib_string(args); janet_lib_string(args);
janet_lib_marsh(args); janet_lib_marsh(args);
#ifdef JANET_ASSEMBLER #ifdef JANET_ASSEMBLER

289
src/core/debug.c Normal file
View File

@ -0,0 +1,289 @@
/*
* Copyright (c) 2018 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <janet/janet.h>
#include "gc.h"
#include "state.h"
/* Implements functionality to build a debugger from within janet.
* The repl should also be able to serve as pretty featured debugger
* out of the box. */
/* Add a break point to a function */
int janet_debug_break(JanetFuncDef *def, int32_t pc) {
if (pc >= def->bytecode_length || pc < 0)
return 1;
def->bytecode[pc] |= 0x80;
return 0;
}
/* Remove a break point from a function */
int janet_debug_unbreak(JanetFuncDef *def, int32_t pc) {
if (pc >= def->bytecode_length || pc < 0)
return 1;
def->bytecode[pc] &= ~((uint32_t)0x80);
return 0;
}
/*
* Find a location for a breakpoint given a source file an
* location.
*/
int janet_debug_find(
JanetFuncDef **def_out, int32_t *pc_out,
const uint8_t *source, int32_t offset) {
/* Scan the heap for right func def */
JanetGCMemoryHeader *current = janet_vm_blocks;
/* Keep track of the best source mapping we have seen so far */
int32_t besti = -1;
int32_t best_range = INT32_MAX;
JanetFuncDef *best_def = NULL;
while (NULL != current) {
if ((current->flags & JANET_MEM_TYPEBITS) == JANET_MEMORY_FUNCDEF) {
JanetFuncDef *def = (JanetFuncDef *)(current + 1);
if (def->sourcemap &&
def->source &&
!janet_string_compare(source, def->source)) {
/* Correct source file, check mappings. The chosen
* pc index is the first match with the smallest range. */
int32_t i;
for (i = 0; i < def->bytecode_length; i++) {
int32_t start = def->sourcemap[i].start;
int32_t end = def->sourcemap[i].end;
if (end - start < best_range &&
start <= offset &&
end >= offset) {
best_range = end - start;
besti = i;
best_def = def;
}
}
}
}
current = current->next;
}
if (best_def) {
*def_out = best_def;
*pc_out = besti;
return 0;
} else {
return 1;
}
}
/*
* CFuns
*/
/* Helper to find funcdef and bytecode offset to insert or remove breakpoints.
* Takes a source file name and byte offset. */
static int helper_find(JanetArgs args, JanetFuncDef **def, int32_t *bytecode_offset) {
const uint8_t *source;
int32_t source_offset;
JANET_FIXARITY(args, 2);
JANET_ARG_STRING(source, args, 0);
JANET_ARG_INTEGER(source_offset, args, 1);
if (janet_debug_find(
def, bytecode_offset, source, source_offset)) {
JANET_THROW(args, "could not find breakpoint");
}
JANET_RETURN_NIL(args);
}
/* Helper to find funcdef and bytecode offset to insert or remove breakpoints.
* Takes a function and byte offset*/
static int helper_find_fun(JanetArgs args, JanetFuncDef **def, int32_t *bytecode_offset) {
JanetFunction *func;
int32_t offset = 0;
JANET_MINARITY(args, 1);
JANET_MAXARITY(args, 2);
JANET_ARG_FUNCTION(func, args, 0);
if (args.n == 2) {
JANET_ARG_INTEGER(offset, args, 1);
}
*def = func->def;
*bytecode_offset = offset;
JANET_RETURN_NIL(args);
}
static int cfun_break(JanetArgs args) {
JanetFuncDef *def;
int32_t offset;
int status = helper_find(args, &def, &offset);
if (status == 0) janet_debug_break(def, offset);
return status;
}
static int cfun_unbreak(JanetArgs args) {
JanetFuncDef *def;
int32_t offset;
int status = helper_find(args, &def, &offset);
if (status == 0) janet_debug_unbreak(def, offset);
return status;
}
static int cfun_fbreak(JanetArgs args) {
JanetFuncDef *def;
int32_t offset;
int status = helper_find_fun(args, &def, &offset);
if (status == 0) {
if (janet_debug_break(def, offset)) {
JANET_THROW(args, "could not find breakpoint");
}
}
return status;
}
static int cfun_unfbreak(JanetArgs args) {
JanetFuncDef *def;
int32_t offset;
int status = helper_find_fun(args, &def, &offset);
if (status == 0) {
if (janet_debug_unbreak(def, offset)) {
JANET_THROW(args, "could not find breakpoint");
}
}
return status;
}
static int cfun_lineage(JanetArgs args) {
JanetFiber *fiber;
JanetArray *array;
JANET_FIXARITY(args, 1);
JANET_ARG_FIBER(fiber, args, 0);
array = janet_array(0);
while (fiber) {
janet_array_push(array, janet_wrap_fiber(fiber));
fiber = fiber->child;
}
JANET_RETURN_ARRAY(args, array);
}
/* Extract info from one stack frame */
static Janet doframe(JanetStackFrame *frame) {
int32_t off;
JanetTable *t = janet_table(3);
JanetFuncDef *def = NULL;
if (frame->func) {
janet_table_put(t, janet_csymbolv(":function"), janet_wrap_function(frame->func));
def = frame->func->def;
if (def->name) {
janet_table_put(t, janet_csymbolv(":name"), janet_wrap_string(def->name));
}
} else {
JanetCFunction cfun = (JanetCFunction)(frame->pc);
if (cfun) {
Janet name = janet_table_get(janet_vm_registry, janet_wrap_cfunction(cfun));
if (!janet_checktype(name, JANET_NIL)) {
janet_table_put(t, janet_csymbolv(":name"), name);
}
}
janet_table_put(t, janet_csymbolv(":c"), janet_wrap_true());
}
if (frame->flags & JANET_STACKFRAME_TAILCALL) {
janet_table_put(t, janet_csymbolv(":tail"), janet_wrap_true());
}
if (frame->func && frame->pc) {
off = (int32_t) (frame->pc - def->bytecode);
janet_table_put(t, janet_csymbolv(":pc"), janet_wrap_integer(off));
if (def->sourcemap) {
JanetSourceMapping mapping = def->sourcemap[off];
janet_table_put(t, janet_csymbolv(":source-start"), janet_wrap_integer(mapping.start));
janet_table_put(t, janet_csymbolv(":source-end"), janet_wrap_integer(mapping.end));
}
if (def->source) {
janet_table_put(t, janet_csymbolv(":source"), janet_wrap_string(def->source));
}
}
return janet_wrap_table(t);
}
static int cfun_stack(JanetArgs args) {
JanetFiber *fiber;
JanetArray *array;
JANET_FIXARITY(args, 1);
JANET_ARG_FIBER(fiber, args, 0);
array = janet_array(0);
{
int32_t i = fiber->frame;
JanetStackFrame *frame;
while (i > 0) {
frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
janet_array_push(array, doframe(frame));
i = frame->prevframe;
}
}
JANET_RETURN_ARRAY(args, array);
}
static const JanetReg cfuns[] = {
{"debug/break", cfun_break,
"(debug/break source byte-offset)\n\n"
"Sets a breakpoint with source a key at a given byte offset. An offset "
"of 0 is the first byte in a file. Will throw an error if the breakpoint location "
"cannot be found. For example\n\n"
"\t(debug/break \"core.janet\" 1000)\n\n"
"wil set a breakpoint at the 1000th byte of the file core.janet."},
{"debug/unbreak", cfun_unbreak,
"(debug/unbreak source byte-offset)\n\n"
"Remove a breakpoint with a source key at a given byte offset. An offset "
"of 0 is the first byte in a file. Will throw an error if the breakpoint "
"cannot be found."},
{"debug/fbreak", cfun_fbreak,
"(debug/fbreak fun [,pc=0])\n\n"
"Set a breakpoint in a given function. pc is an optional offset, which "
"is in bytecode instructions. fun is a function value. Will throw an error "
"if the offset is too large or negative."},
{"debug/funbreak", cfun_unfbreak,
"(debug/fbreak fun [,pc=0])\n\n"
"Unset a breakpoint set with debug/fbreak."},
{"debug/stack", cfun_stack,
"(debug/stack fib)\n\n"
"Gets information about the stack as an array of tables. Each table "
"in the array contains information about a stack frame. The top most, current "
"stack frame is the first table in the array, and the bottom most stack frame "
"is the last value. Each stack frame contains some of the following attributes:\n\n"
"\t:c - true if the stack frame is a c function invocation\n"
"\t:column - the current source column of the stack frame\n"
"\t:function - the function that the stack frame represents\n"
"\t:line - the current source line of the stack frame\n"
"\t:name - the human friendly name of the function\n"
"\t:pc - integer indicating the location of the program counter\n"
"\t:source - string with filename or other identifier for the source code\n"
"\t:tail - boolean indicating a tail call"
},
{"debug/lineage", cfun_lineage,
"(debug/lineage fib)\n\n"
"Returns an array of all child fibers from a root fiber. This function "
"is useful when a fiber signals or errors to an ancestor fiber. Using this function, "
"the fiber handling the error can see which fiber raised the signal. This function should "
"be used mostly for debugging purposes."
},
{NULL, NULL, NULL}
};
/* Module entry point */
int janet_lib_debug(JanetArgs args) {
JanetTable *env = janet_env(args);
janet_cfuns(env, NULL, cfuns);
return 0;
}

View File

@ -349,88 +349,11 @@ static int cfun_new(JanetArgs args) {
static int cfun_status(JanetArgs args) { static int cfun_status(JanetArgs args) {
JanetFiber *fiber; JanetFiber *fiber;
const char *status = "";
JANET_FIXARITY(args, 1); JANET_FIXARITY(args, 1);
JANET_ARG_FIBER(fiber, args, 0); JANET_ARG_FIBER(fiber, args, 0);
uint32_t s = (fiber->flags & JANET_FIBER_STATUS_MASK) >> uint32_t s = (fiber->flags & JANET_FIBER_STATUS_MASK) >>
JANET_FIBER_STATUS_OFFSET; JANET_FIBER_STATUS_OFFSET;
switch (s) { JANET_RETURN_CSYMBOL(args, janet_status_names[s]);
case JANET_STATUS_DEAD: status = ":dead"; break;
case JANET_STATUS_ERROR: status = ":error"; break;
case JANET_STATUS_DEBUG: status = ":debug"; break;
case JANET_STATUS_PENDING: status = ":pending"; break;
case JANET_STATUS_USER0: status = ":user0"; break;
case JANET_STATUS_USER1: status = ":user1"; break;
case JANET_STATUS_USER2: status = ":user2"; break;
case JANET_STATUS_USER3: status = ":user3"; break;
case JANET_STATUS_USER4: status = ":user4"; break;
case JANET_STATUS_USER5: status = ":user5"; break;
case JANET_STATUS_USER6: status = ":user6"; break;
case JANET_STATUS_USER7: status = ":user7"; break;
case JANET_STATUS_USER8: status = ":user8"; break;
case JANET_STATUS_USER9: status = ":user9"; break;
case JANET_STATUS_NEW: status = ":new"; break;
default:
case JANET_STATUS_ALIVE: status = ":alive"; break;
}
JANET_RETURN_CSYMBOL(args, status);
}
/* Extract info from one stack frame */
static Janet doframe(JanetStackFrame *frame) {
int32_t off;
JanetTable *t = janet_table(3);
JanetFuncDef *def = NULL;
if (frame->func) {
janet_table_put(t, janet_csymbolv(":function"), janet_wrap_function(frame->func));
def = frame->func->def;
if (def->name) {
janet_table_put(t, janet_csymbolv(":name"), janet_wrap_string(def->name));
}
} else {
JanetCFunction cfun = (JanetCFunction)(frame->pc);
if (cfun) {
Janet name = janet_table_get(janet_vm_registry, janet_wrap_cfunction(cfun));
if (!janet_checktype(name, JANET_NIL)) {
janet_table_put(t, janet_csymbolv(":name"), name);
}
}
janet_table_put(t, janet_csymbolv(":c"), janet_wrap_true());
}
if (frame->flags & JANET_STACKFRAME_TAILCALL) {
janet_table_put(t, janet_csymbolv(":tail"), janet_wrap_true());
}
if (frame->func && frame->pc) {
off = (int32_t) (frame->pc - def->bytecode);
janet_table_put(t, janet_csymbolv(":pc"), janet_wrap_integer(off));
if (def->sourcemap) {
JanetSourceMapping mapping = def->sourcemap[off];
janet_table_put(t, janet_csymbolv(":line"), janet_wrap_integer(mapping.line));
janet_table_put(t, janet_csymbolv(":column"), janet_wrap_integer(mapping.column));
}
if (def->source) {
janet_table_put(t, janet_csymbolv(":source"), janet_wrap_string(def->source));
}
}
return janet_wrap_table(t);
}
static int cfun_stack(JanetArgs args) {
JanetFiber *fiber;
JanetArray *array;
JANET_FIXARITY(args, 1);
JANET_ARG_FIBER(fiber, args, 0);
array = janet_array(0);
{
int32_t i = fiber->frame;
JanetStackFrame *frame;
while (i > 0) {
frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
janet_array_push(array, doframe(frame));
i = frame->prevframe;
}
}
JANET_RETURN_ARRAY(args, array);
} }
static int cfun_current(JanetArgs args) { static int cfun_current(JanetArgs args) {
@ -438,19 +361,6 @@ static int cfun_current(JanetArgs args) {
JANET_RETURN_FIBER(args, janet_vm_fiber); JANET_RETURN_FIBER(args, janet_vm_fiber);
} }
static int cfun_lineage(JanetArgs args) {
JanetFiber *fiber;
JanetArray *array;
JANET_FIXARITY(args, 1);
JANET_ARG_FIBER(fiber, args, 0);
array = janet_array(0);
while (fiber) {
janet_array_push(array, janet_wrap_fiber(fiber));
fiber = fiber->child;
}
JANET_RETURN_ARRAY(args, array);
}
static int cfun_maxstack(JanetArgs args) { static int cfun_maxstack(JanetArgs args) {
JanetFiber *fiber; JanetFiber *fiber;
JANET_FIXARITY(args, 1); JANET_FIXARITY(args, 1);
@ -500,32 +410,10 @@ static const JanetReg cfuns[] = {
"\t:alive - the fiber is currently running and cannot be resumed\n" "\t:alive - the fiber is currently running and cannot be resumed\n"
"\t:new - the fiber has just been created and not yet run" "\t:new - the fiber has just been created and not yet run"
}, },
{"fiber/stack", cfun_stack,
"(fiber/stack fib)\n\n"
"Gets information about the stack as an array of tables. Each table "
"in the array contains information about a stack frame. The top most, current "
"stack frame is the first table in the array, and the bottom most stack frame "
"is the last value. Each stack frame contains some of the following attributes:\n\n"
"\t:c - true if the stack frame is a c function invokation\n"
"\t:column - the current source column of the stack frame\n"
"\t:function - the function that the stack frame represents\n"
"\t:line - the current source line of the stack frame\n"
"\t:name - the human friendly name of the function\n"
"\t:pc - integer indicating the location of the program counter\n"
"\t:source - string with filename or other identifier for the source code\n"
"\t:tail - boolean indicating a tail call"
},
{"fiber/current", cfun_current, {"fiber/current", cfun_current,
"(fiber/current)\n\n" "(fiber/current)\n\n"
"Returns the currently running fiber." "Returns the currently running fiber."
}, },
{"fiber/lineage", cfun_lineage,
"(fiber/lineage fib)\n\n"
"Returns an array of all child fibers from a root fiber. This function "
"is useful when a fiber signals or errors to an ancestor fiber. Using this function, "
"the fiber handling the error can see which fiber raised the signal. This function should "
"be used mostly for debugging purposes."
},
{"fiber/maxstack", cfun_maxstack, {"fiber/maxstack", cfun_maxstack,
"(fiber/maxstack fib)\n\n" "(fiber/maxstack fib)\n\n"
"Gets the maximum stack size in janet values allowed for a fiber. While memory for " "Gets the maximum stack size in janet values allowed for a fiber. While memory for "

View File

@ -234,8 +234,8 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCEMAP) { if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCEMAP) {
for (int32_t i = 0; i < def->bytecode_length; i++) { for (int32_t i = 0; i < def->bytecode_length; i++) {
JanetSourceMapping map = def->sourcemap[i]; JanetSourceMapping map = def->sourcemap[i];
pushint(st, map.line); pushint(st, map.start);
pushint(st, map.column); pushint(st, map.end);
} }
} }
} }
@ -740,8 +740,8 @@ static const uint8_t *unmarshal_one_def(
JANET_OUT_OF_MEMORY; JANET_OUT_OF_MEMORY;
} }
for (int32_t i = 0; i < bytecode_length; i++) { for (int32_t i = 0; i < bytecode_length; i++) {
def->sourcemap[i].line = readint(st, &data); def->sourcemap[i].start = readint(st, &data);
def->sourcemap[i].column = readint(st, &data); def->sourcemap[i].end = readint(st, &data);
} }
} else { } else {
def->sourcemap = NULL; def->sourcemap = NULL;

View File

@ -102,8 +102,7 @@ struct JanetParseState {
int32_t counter; int32_t counter;
int32_t argn; int32_t argn;
int flags; int flags;
size_t start_line; size_t start;
size_t start_col;
Consumer consumer; Consumer consumer;
}; };
@ -147,8 +146,7 @@ static void pushstate(JanetParser *p, Consumer consumer, int flags) {
s.argn = 0; s.argn = 0;
s.flags = flags; s.flags = flags;
s.consumer = consumer; s.consumer = consumer;
s.start_line = p->line; s.start = p->offset;
s.start_col = p->col;
_pushstate(p, s); _pushstate(p, s);
} }
@ -159,8 +157,8 @@ static void popstate(JanetParser *p, Janet val) {
if (newtop->flags & PFLAG_CONTAINER) { if (newtop->flags & PFLAG_CONTAINER) {
/* Source mapping info */ /* Source mapping info */
if (janet_checktype(val, JANET_TUPLE)) { if (janet_checktype(val, JANET_TUPLE)) {
janet_tuple_sm_line(janet_unwrap_tuple(val)) = (int32_t) top.start_line; janet_tuple_sm_start(janet_unwrap_tuple(val)) = (int32_t) top.start;
janet_tuple_sm_col(janet_unwrap_tuple(val)) = (int32_t) top.start_col; janet_tuple_sm_end(janet_unwrap_tuple(val)) = (int32_t) p->offset;
} }
newtop->argn++; newtop->argn++;
push_arg(p, val); push_arg(p, val);
@ -176,8 +174,8 @@ static void popstate(JanetParser *p, Janet val) {
t[0] = janet_csymbolv(which); t[0] = janet_csymbolv(which);
t[1] = val; t[1] = val;
/* Quote source mapping info */ /* Quote source mapping info */
janet_tuple_sm_line(t) = (int32_t) newtop->start_line; janet_tuple_sm_start(t) = (int32_t) newtop->start;
janet_tuple_sm_col(t) = (int32_t) newtop->start_col; janet_tuple_sm_end(t) = (int32_t) p->offset;
val = janet_wrap_tuple(janet_tuple_end(t)); val = janet_wrap_tuple(janet_tuple_end(t));
} else { } else {
return; return;
@ -522,16 +520,11 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
int janet_parser_consume(JanetParser *parser, uint8_t c) { int janet_parser_consume(JanetParser *parser, uint8_t c) {
int consumed = 0; int consumed = 0;
if (parser->error) return 0; if (parser->error) return 0;
if (c == '\n') {
parser->line++;
parser->col = 0;
} else if (c != '\r') {
parser->col++;
}
while (!consumed && !parser->error) { while (!consumed && !parser->error) {
JanetParseState *state = parser->states + parser->statecount - 1; JanetParseState *state = parser->states + parser->statecount - 1;
consumed = state->consumer(parser, state, c); consumed = state->consumer(parser, state, c);
} }
parser->offset++;
parser->lookback = c; parser->lookback = c;
return 1; return 1;
} }
@ -584,9 +577,8 @@ void janet_parser_init(JanetParser *parser) {
parser->statecount = 0; parser->statecount = 0;
parser->statecap = 0; parser->statecap = 0;
parser->error = NULL; parser->error = NULL;
parser->line = 1;
parser->col = 0;
parser->lookback = -1; parser->lookback = -1;
parser->offset = 0;
pushstate(parser, root, PFLAG_CONTAINER); pushstate(parser, root, PFLAG_CONTAINER);
} }
@ -742,10 +734,7 @@ static int cfun_where(JanetArgs args) {
JANET_FIXARITY(args, 1); JANET_FIXARITY(args, 1);
JANET_CHECKABSTRACT(args, 0, &janet_parse_parsertype); JANET_CHECKABSTRACT(args, 0, &janet_parse_parsertype);
p = (JanetParser *) janet_unwrap_abstract(args.v[0]); p = (JanetParser *) janet_unwrap_abstract(args.v[0]);
Janet *tup = janet_tuple_begin(2); JANET_RETURN_INTEGER(args, p->offset);
tup[0] = janet_wrap_integer((int32_t)p->line);
tup[1] = janet_wrap_integer((int32_t)p->col);
JANET_RETURN_TUPLE(args, janet_tuple_end(tup));
} }
static int cfun_state(JanetArgs args) { static int cfun_state(JanetArgs args) {

View File

@ -67,7 +67,7 @@ void janet_stacktrace(JanetFiber *fiber, const char *errtype, Janet err) {
int32_t off = (int32_t) (frame->pc - def->bytecode); int32_t off = (int32_t) (frame->pc - def->bytecode);
if (def->sourcemap) { if (def->sourcemap) {
JanetSourceMapping mapping = def->sourcemap[off]; JanetSourceMapping mapping = def->sourcemap[off];
fprintf(stderr, " on line %d, column %d", mapping.line, mapping.column); fprintf(stderr, " at (%d:%d)", mapping.start, mapping.end);
} else { } else {
fprintf(stderr, " pc=%d", off); fprintf(stderr, " pc=%d", off);
} }
@ -75,6 +75,8 @@ void janet_stacktrace(JanetFiber *fiber, const char *errtype, Janet err) {
fprintf(stderr, "\n"); fprintf(stderr, "\n");
} }
} }
janet_v_free(fibers);
} }
/* Run a string */ /* Run a string */

View File

@ -32,8 +32,8 @@ Janet *janet_tuple_begin(int32_t length) {
char *data = janet_gcalloc(JANET_MEMORY_TUPLE, 4 * sizeof(int32_t) + length * sizeof(Janet)); char *data = janet_gcalloc(JANET_MEMORY_TUPLE, 4 * sizeof(int32_t) + length * sizeof(Janet));
Janet *tuple = (Janet *)(data + (4 * sizeof(int32_t))); Janet *tuple = (Janet *)(data + (4 * sizeof(int32_t)));
janet_tuple_length(tuple) = length; janet_tuple_length(tuple) = length;
janet_tuple_sm_line(tuple) = 0; janet_tuple_sm_start(tuple) = -1;
janet_tuple_sm_col(tuple) = 0; janet_tuple_sm_end(tuple) = -1;
return tuple; return tuple;
} }

View File

@ -53,6 +53,42 @@ const char *const janet_type_names[16] = {
":abstract" ":abstract"
}; };
const char *const janet_signal_names[14] = {
":ok",
":error",
":debug",
":yield",
":user0",
":user1",
":user2",
":user3",
":user4",
":user5",
":user6",
":user7",
":user8",
":user9"
};
const char *const janet_status_names[16] = {
":dead",
":error",
":debug",
":pending",
":user0",
":user1",
":user2",
":user3",
":user4",
":user5",
":user6",
":user7",
":user8",
":user9",
":new",
":alive"
};
/* Calculate hash for string */ /* Calculate hash for string */
int32_t janet_string_calchash(const uint8_t *str, int32_t len) { int32_t janet_string_calchash(const uint8_t *str, int32_t len) {

View File

@ -57,5 +57,6 @@ int janet_lib_parse(JanetArgs args);
int janet_lib_asm(JanetArgs args); int janet_lib_asm(JanetArgs args);
#endif #endif
int janet_lib_compile(JanetArgs args); int janet_lib_compile(JanetArgs args);
int janet_lib_debug(JanetArgs args);
#endif #endif

View File

@ -54,6 +54,8 @@ JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out) {
/* Expected types on type error */ /* Expected types on type error */
uint16_t expected_types; uint16_t expected_types;
uint8_t first_opcode;
/* Signal to return when done */ /* Signal to return when done */
JanetSignal signal = JANET_SIGNAL_OK; JanetSignal signal = JANET_SIGNAL_OK;
@ -92,16 +94,24 @@ JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out) {
* instruction. */ * instruction. */
retreg = in; retreg = in;
goto vm_resume_child; goto vm_resume_child;
} else if (startstatus != JANET_STATUS_NEW) { } else if (startstatus != JANET_STATUS_NEW &&
((*pc & 0xFF) == JOP_SIGNAL)) {
/* Only should be hit if child is waiting on a SIGNAL instruction */ /* Only should be hit if child is waiting on a SIGNAL instruction */
/* If waiting for response to signal, use input and increment pc */ /* If waiting for response to signal, use input and increment pc */
stack[oparg(1, 0xFF)] = in; stack[oparg(1, 0xFF)] = in;
pc++; pc++;
} }
/* The first opcode to execute. If the first opcode has
* the breakpoint bit set and we were in the debug state, skip
* that first breakpoint. */
first_opcode = (startstatus == JANET_STATUS_DEBUG)
? (*pc & 0x7F)
: (*pc & 0xFF);
/* Use computed gotos for GCC and clang, otherwise use switch */ /* Use computed gotos for GCC and clang, otherwise use switch */
#ifdef __GNUC__ #ifdef ____GNUC__
#define VM_START() {vm_next(); #define VM_START() { goto *op_lookup[first_opcode];
#define VM_END() } #define VM_END() }
#define VM_OP(op) label_##op : #define VM_OP(op) label_##op :
#define VM_DEFAULT() label_unknown_op: #define VM_DEFAULT() label_unknown_op:
@ -193,11 +203,11 @@ static void *op_lookup[255] = {
&&label_unknown_op &&label_unknown_op
}; };
#else #else
#define VM_START() for(;;){switch(*pc & 0xFF){ #define VM_START() uint8_t opcode = first_opcode; for (;;) {switch(opcode) {
#define VM_END() }} #define VM_END() }}
#define VM_OP(op) case op : #define VM_OP(op) case op :
#define VM_DEFAULT() default: #define VM_DEFAULT() default:
#define vm_next() continue #define vm_next() opcode = *pc & 0xFF; continue
#endif #endif
#define vm_checkgc_next() janet_maybe_collect(); vm_next() #define vm_checkgc_next() janet_maybe_collect(); vm_next()
@ -279,6 +289,7 @@ static void *op_lookup[255] = {
VM_START(); VM_START();
VM_DEFAULT(); VM_DEFAULT();
signal = JANET_SIGNAL_DEBUG;
retreg = janet_wrap_nil(); retreg = janet_wrap_nil();
goto vm_exit; goto vm_exit;
@ -535,7 +546,6 @@ static void *op_lookup[255] = {
pc++; pc++;
vm_next(); vm_next();
/* Candidate */
VM_OP(JOP_GREATER_THAN_INTEGER) VM_OP(JOP_GREATER_THAN_INTEGER)
stack[oparg(1, 0xFF)] = janet_wrap_boolean( stack[oparg(1, 0xFF)] = janet_wrap_boolean(
janet_unwrap_integer(stack[oparg(2, 0xFF)]) > janet_unwrap_integer(stack[oparg(2, 0xFF)]) >
@ -543,7 +553,6 @@ static void *op_lookup[255] = {
pc++; pc++;
vm_next(); vm_next();
/* Candidate */
VM_OP(JOP_GREATER_THAN_IMMEDIATE) VM_OP(JOP_GREATER_THAN_IMMEDIATE)
stack[oparg(1, 0xFF)] = janet_wrap_boolean( stack[oparg(1, 0xFF)] = janet_wrap_boolean(
janet_unwrap_integer(stack[oparg(2, 0xFF)]) > ((*(int32_t *)pc) >> 24) janet_unwrap_integer(stack[oparg(2, 0xFF)]) > ((*(int32_t *)pc) >> 24)
@ -551,7 +560,6 @@ static void *op_lookup[255] = {
pc++; pc++;
vm_next(); vm_next();
/* Candidate */
VM_OP(JOP_GREATER_THAN_REAL) VM_OP(JOP_GREATER_THAN_REAL)
stack[oparg(1, 0xFF)] = janet_wrap_boolean( stack[oparg(1, 0xFF)] = janet_wrap_boolean(
janet_unwrap_real(stack[oparg(2, 0xFF)]) > janet_unwrap_real(stack[oparg(2, 0xFF)]) >
@ -559,7 +567,6 @@ static void *op_lookup[255] = {
pc++; pc++;
vm_next(); vm_next();
/* Candidate */
VM_OP(JOP_GREATER_THAN_EQUAL_REAL) VM_OP(JOP_GREATER_THAN_EQUAL_REAL)
stack[oparg(1, 0xFF)] = janet_wrap_boolean( stack[oparg(1, 0xFF)] = janet_wrap_boolean(
janet_unwrap_real(stack[oparg(2, 0xFF)]) >= janet_unwrap_real(stack[oparg(2, 0xFF)]) >=
@ -575,7 +582,6 @@ static void *op_lookup[255] = {
pc++; pc++;
vm_next(); vm_next();
/* Candidate */
VM_OP(JOP_EQUALS_INTEGER) VM_OP(JOP_EQUALS_INTEGER)
stack[oparg(1, 0xFF)] = janet_wrap_boolean( stack[oparg(1, 0xFF)] = janet_wrap_boolean(
janet_unwrap_integer(stack[oparg(2, 0xFF)]) == janet_unwrap_integer(stack[oparg(2, 0xFF)]) ==
@ -584,7 +590,6 @@ static void *op_lookup[255] = {
pc++; pc++;
vm_next(); vm_next();
/* Candidate */
VM_OP(JOP_EQUALS_REAL) VM_OP(JOP_EQUALS_REAL)
stack[oparg(1, 0xFF)] = janet_wrap_boolean( stack[oparg(1, 0xFF)] = janet_wrap_boolean(
janet_unwrap_real(stack[oparg(2, 0xFF)]) == janet_unwrap_real(stack[oparg(2, 0xFF)]) ==
@ -593,7 +598,6 @@ static void *op_lookup[255] = {
pc++; pc++;
vm_next(); vm_next();
/* Candidate */
VM_OP(JOP_EQUALS_IMMEDIATE) VM_OP(JOP_EQUALS_IMMEDIATE)
stack[oparg(1, 0xFF)] = janet_wrap_boolean( stack[oparg(1, 0xFF)] = janet_wrap_boolean(
janet_unwrap_integer(stack[oparg(2, 0xFF)]) == ((*(int32_t *)pc) >> 24) janet_unwrap_integer(stack[oparg(2, 0xFF)]) == ((*(int32_t *)pc) >> 24)

View File

@ -204,6 +204,8 @@ extern "C" {
/* Names of all of the types */ /* Names of all of the types */
extern const char *const janet_type_names[16]; extern const char *const janet_type_names[16];
extern const char *const janet_signal_names[14];
extern const char *const janet_status_names[16];
/* Fiber signals */ /* Fiber signals */
typedef enum { typedef enum {
@ -667,8 +669,8 @@ struct JanetKV {
/* Source mapping structure for a bytecode instruction */ /* Source mapping structure for a bytecode instruction */
struct JanetSourceMapping { struct JanetSourceMapping {
int32_t line; int32_t start;
int32_t column; int32_t end;
}; };
/* A function definition. Contains information needed to instantiate closures. */ /* A function definition. Contains information needed to instantiate closures. */
@ -731,8 +733,7 @@ struct JanetParser {
size_t statecap; size_t statecap;
size_t bufcount; size_t bufcount;
size_t bufcap; size_t bufcap;
size_t line; size_t offset;
size_t col;
int lookback; int lookback;
}; };
@ -967,8 +968,8 @@ JANET_API int janet_buffer_push_u64(JanetBuffer *buffer, uint64_t x);
#define janet_tuple_raw(t) ((int32_t *)(t) - 4) #define janet_tuple_raw(t) ((int32_t *)(t) - 4)
#define janet_tuple_length(t) (janet_tuple_raw(t)[0]) #define janet_tuple_length(t) (janet_tuple_raw(t)[0])
#define janet_tuple_hash(t) ((janet_tuple_raw(t)[1])) #define janet_tuple_hash(t) ((janet_tuple_raw(t)[1]))
#define janet_tuple_sm_line(t) ((janet_tuple_raw(t)[2])) #define janet_tuple_sm_start(t) ((janet_tuple_raw(t)[2]))
#define janet_tuple_sm_col(t) ((janet_tuple_raw(t)[3])) #define janet_tuple_sm_end(t) ((janet_tuple_raw(t)[3]))
JANET_API Janet *janet_tuple_begin(int32_t length); JANET_API Janet *janet_tuple_begin(int32_t length);
JANET_API const Janet *janet_tuple_end(Janet *tuple); JANET_API const Janet *janet_tuple_end(Janet *tuple);
JANET_API const Janet *janet_tuple_n(const Janet *values, int32_t n); JANET_API const Janet *janet_tuple_n(const Janet *values, int32_t n);

View File

@ -55,6 +55,6 @@
(do (do
(print (string "Janet " janet/version "-" janet/build " Copyright (C) 2017-2018 Calvin Rose")) (print (string "Janet " janet/version "-" janet/build " Copyright (C) 2017-2018 Calvin Rose"))
(repl (fn [buf p] (repl (fn [buf p]
(def [line] (parser/where p)) (def offset (parser/where p))
(def prompt (string "janet:" line ":" (parser/state p) "> ")) (def prompt (string "janet:" offset ":" (parser/state p) "> "))
(getline prompt buf))))))) (getline prompt buf)))))))