diff --git a/src/core/asm.c b/src/core/asm.c index 050aae26..3633360f 100644 --- a/src/core/asm.c +++ b/src/core/asm.c @@ -710,8 +710,8 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int if (!janet_checktype(tup[1], JANET_INTEGER)) { janet_asm_error(&a, "expected integer"); } - mapping.line = janet_unwrap_integer(tup[0]); - mapping.column = janet_unwrap_integer(tup[1]); + mapping.start = janet_unwrap_integer(tup[0]); + mapping.end = janet_unwrap_integer(tup[1]); def->sourcemap[i] = mapping; } } @@ -876,8 +876,8 @@ Janet janet_disasm(JanetFuncDef *def) { for (i = 0; i < def->bytecode_length; i++) { Janet *t = janet_tuple_begin(2); JanetSourceMapping mapping = def->sourcemap[i]; - t[0] = janet_wrap_integer(mapping.line); - t[1] = janet_wrap_integer(mapping.column); + t[0] = janet_wrap_integer(mapping.start); + t[1] = janet_wrap_integer(mapping.end); sourcemap->data[i] = janet_wrap_tuple(janet_tuple_end(t)); } sourcemap->count = def->bytecode_length; diff --git a/src/core/bytecode.c b/src/core/bytecode.c index 83106baf..0112e605 100644 --- a/src/core/bytecode.c +++ b/src/core/bytecode.c @@ -125,10 +125,10 @@ int32_t janet_verify(JanetFuncDef *def) { for (i = 0; i < def->bytecode_length; i++) { uint32_t instr = def->bytecode[i]; /* Check for invalid instructions */ - if ((instr & 0xFF) >= JOP_INSTRUCTION_COUNT) { + if ((instr & 0x7F) >= JOP_INSTRUCTION_COUNT) { return 3; } - enum JanetInstructionType type = janet_instructions[instr & 0xFF]; + enum JanetInstructionType type = janet_instructions[instr & 0x7F]; switch (type) { case JINT_0: continue; diff --git a/src/core/compile.c b/src/core/compile.c index e67da663..1458347c 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -462,9 +462,9 @@ static int macroexpand1( if (janet_tuple_length(form) == 0) return 0; /* Source map - only set when we get a tuple */ - if (janet_tuple_sm_line(form) > 0) { - c->current_mapping.line = janet_tuple_sm_line(form); - c->current_mapping.column = janet_tuple_sm_col(form); + if (janet_tuple_sm_start(form) >= 0) { + c->current_mapping.start = janet_tuple_sm_start(form); + c->current_mapping.end = janet_tuple_sm_end(form); } if (!janet_checktype(form[0], JANET_SYMBOL)) 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->env = env; c->source = where; - c->current_mapping.line = 0; - c->current_mapping.column = 0; + c->current_mapping.start = -1; + c->current_mapping.end = -1; /* Init result */ c->result.error = NULL; c->result.status = JANET_COMPILE_OK; c->result.funcdef = NULL; c->result.macrofiber = NULL; - c->result.error_mapping.line = 0; - c->result.error_mapping.column = 0; + c->result.error_mapping.start = -1; + c->result.error_mapping.end = -1; } /* Deinitialize a compiler struct */ @@ -717,8 +717,8 @@ static int cfun(JanetArgs args) { } else { t = janet_table(4); 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(":column"), janet_wrap_integer(res.error_mapping.column)); + janet_table_put(t, janet_csymbolv(":start"), janet_wrap_integer(res.error_mapping.start)); + janet_table_put(t, janet_csymbolv(":end"), janet_wrap_integer(res.error_mapping.end)); if (res.macrofiber) { janet_table_put(t, janet_csymbolv(":fiber"), janet_wrap_fiber(res.macrofiber)); } diff --git a/src/core/core.janet b/src/core/core.janet index b2b3a1a0..72ca03f0 100644 --- a/src/core/core.janet +++ b/src/core/core.janet @@ -1252,11 +1252,11 @@ value, one key will be ignored." (res) (do (:= good false) - (def {:error err :line errl :column errc :fiber errf} res) + (def {:error err :start start :end end :fiber errf} res) (onstatus :compile - (if (< 0 errl) - (string err "\n in a form at line " errl ", column " errc) + (if (<= 0 start) + (string err "\n at (" start ":" end ")") err) errf where)))) @@ -1309,7 +1309,7 @@ value, one key will be ignored." "\n") (when f (loop - [nf :in (reverse (fiber/lineage f)) + [nf :in (reverse (debug/lineage f)) :before (file/write stderr " (fiber)\n") {:function func :tail tail @@ -1317,8 +1317,8 @@ value, one key will be ignored." :c c :name name :source source - :line source-line - :column source-col} :in (fiber/stack nf)] + :source-start start + :source-end end} :in (debug/stack nf)] (file/write stderr " in") (when c (file/write stderr " cfunction")) (if name @@ -1327,14 +1327,15 @@ value, one key will be ignored." (if source (do (file/write stderr " [" source "]") - (if source-line + (if start (file/write stderr - " on line " - (string source-line) - ", column " - (string source-col))))) - (if (and (not source-line) pc) + " at (" + (string start) + ":" + (string end) + ")")))) + (if (and (not start) pc) (file/write stderr " (pc=" (string pc) ")")) (when tail (file/write stderr " (tailcall)")) (file/write stderr "\n")))) @@ -1507,6 +1508,7 @@ value, one key will be ignored." (def newenv (make-env)) (default chunks (fn [buf _] (file/read stdin :line buf))) (default onsignal (fn [sig x f source] + (put newenv '_fiber @{:value f}) (case sig :dead (do (put newenv '_ @{:value x}) diff --git a/src/core/corelib.c b/src/core/corelib.c index 0190e698..d1282c40 100644 --- a/src/core/corelib.c +++ b/src/core/corelib.c @@ -801,6 +801,7 @@ JanetTable *janet_core_env(void) { janet_lib_os(args); janet_lib_parse(args); janet_lib_compile(args); + janet_lib_debug(args); janet_lib_string(args); janet_lib_marsh(args); #ifdef JANET_ASSEMBLER diff --git a/src/core/debug.c b/src/core/debug.c new file mode 100644 index 00000000..7e9c679f --- /dev/null +++ b/src/core/debug.c @@ -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 +#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; +} diff --git a/src/core/fiber.c b/src/core/fiber.c index 7f806c0c..31f9b6e3 100644 --- a/src/core/fiber.c +++ b/src/core/fiber.c @@ -349,88 +349,11 @@ static int cfun_new(JanetArgs args) { static int cfun_status(JanetArgs args) { JanetFiber *fiber; - const char *status = ""; JANET_FIXARITY(args, 1); JANET_ARG_FIBER(fiber, args, 0); uint32_t s = (fiber->flags & JANET_FIBER_STATUS_MASK) >> JANET_FIBER_STATUS_OFFSET; - switch (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); + JANET_RETURN_CSYMBOL(args, janet_status_names[s]); } static int cfun_current(JanetArgs args) { @@ -438,19 +361,6 @@ static int cfun_current(JanetArgs args) { 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) { JanetFiber *fiber; 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: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)\n\n" "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 fib)\n\n" "Gets the maximum stack size in janet values allowed for a fiber. While memory for " diff --git a/src/core/marsh.c b/src/core/marsh.c index 4b794e92..53c9103a 100644 --- a/src/core/marsh.c +++ b/src/core/marsh.c @@ -234,8 +234,8 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) { if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCEMAP) { for (int32_t i = 0; i < def->bytecode_length; i++) { JanetSourceMapping map = def->sourcemap[i]; - pushint(st, map.line); - pushint(st, map.column); + pushint(st, map.start); + pushint(st, map.end); } } } @@ -740,8 +740,8 @@ static const uint8_t *unmarshal_one_def( JANET_OUT_OF_MEMORY; } for (int32_t i = 0; i < bytecode_length; i++) { - def->sourcemap[i].line = readint(st, &data); - def->sourcemap[i].column = readint(st, &data); + def->sourcemap[i].start = readint(st, &data); + def->sourcemap[i].end = readint(st, &data); } } else { def->sourcemap = NULL; diff --git a/src/core/parse.c b/src/core/parse.c index a4845c06..a1cf7d1f 100644 --- a/src/core/parse.c +++ b/src/core/parse.c @@ -102,8 +102,7 @@ struct JanetParseState { int32_t counter; int32_t argn; int flags; - size_t start_line; - size_t start_col; + size_t start; Consumer consumer; }; @@ -147,8 +146,7 @@ static void pushstate(JanetParser *p, Consumer consumer, int flags) { s.argn = 0; s.flags = flags; s.consumer = consumer; - s.start_line = p->line; - s.start_col = p->col; + s.start = p->offset; _pushstate(p, s); } @@ -159,8 +157,8 @@ static void popstate(JanetParser *p, Janet val) { if (newtop->flags & PFLAG_CONTAINER) { /* Source mapping info */ if (janet_checktype(val, JANET_TUPLE)) { - janet_tuple_sm_line(janet_unwrap_tuple(val)) = (int32_t) top.start_line; - janet_tuple_sm_col(janet_unwrap_tuple(val)) = (int32_t) top.start_col; + janet_tuple_sm_start(janet_unwrap_tuple(val)) = (int32_t) top.start; + janet_tuple_sm_end(janet_unwrap_tuple(val)) = (int32_t) p->offset; } newtop->argn++; push_arg(p, val); @@ -176,8 +174,8 @@ static void popstate(JanetParser *p, Janet val) { t[0] = janet_csymbolv(which); t[1] = val; /* Quote source mapping info */ - janet_tuple_sm_line(t) = (int32_t) newtop->start_line; - janet_tuple_sm_col(t) = (int32_t) newtop->start_col; + janet_tuple_sm_start(t) = (int32_t) newtop->start; + janet_tuple_sm_end(t) = (int32_t) p->offset; val = janet_wrap_tuple(janet_tuple_end(t)); } else { 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 consumed = 0; if (parser->error) return 0; - if (c == '\n') { - parser->line++; - parser->col = 0; - } else if (c != '\r') { - parser->col++; - } while (!consumed && !parser->error) { JanetParseState *state = parser->states + parser->statecount - 1; consumed = state->consumer(parser, state, c); } + parser->offset++; parser->lookback = c; return 1; } @@ -584,9 +577,8 @@ void janet_parser_init(JanetParser *parser) { parser->statecount = 0; parser->statecap = 0; parser->error = NULL; - parser->line = 1; - parser->col = 0; parser->lookback = -1; + parser->offset = 0; pushstate(parser, root, PFLAG_CONTAINER); } @@ -742,10 +734,7 @@ static int cfun_where(JanetArgs args) { JANET_FIXARITY(args, 1); JANET_CHECKABSTRACT(args, 0, &janet_parse_parsertype); p = (JanetParser *) janet_unwrap_abstract(args.v[0]); - Janet *tup = janet_tuple_begin(2); - 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)); + JANET_RETURN_INTEGER(args, p->offset); } static int cfun_state(JanetArgs args) { diff --git a/src/core/run.c b/src/core/run.c index 1d5be862..6a44ab65 100644 --- a/src/core/run.c +++ b/src/core/run.c @@ -67,7 +67,7 @@ void janet_stacktrace(JanetFiber *fiber, const char *errtype, Janet err) { int32_t off = (int32_t) (frame->pc - def->bytecode); if (def->sourcemap) { 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 { fprintf(stderr, " pc=%d", off); } @@ -75,6 +75,8 @@ void janet_stacktrace(JanetFiber *fiber, const char *errtype, Janet err) { fprintf(stderr, "\n"); } } + + janet_v_free(fibers); } /* Run a string */ diff --git a/src/core/tuple.c b/src/core/tuple.c index c8f3dd65..b64a9b52 100644 --- a/src/core/tuple.c +++ b/src/core/tuple.c @@ -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)); Janet *tuple = (Janet *)(data + (4 * sizeof(int32_t))); janet_tuple_length(tuple) = length; - janet_tuple_sm_line(tuple) = 0; - janet_tuple_sm_col(tuple) = 0; + janet_tuple_sm_start(tuple) = -1; + janet_tuple_sm_end(tuple) = -1; return tuple; } diff --git a/src/core/util.c b/src/core/util.c index 27450911..200e381f 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -53,6 +53,42 @@ const char *const janet_type_names[16] = { ":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 */ int32_t janet_string_calchash(const uint8_t *str, int32_t len) { diff --git a/src/core/util.h b/src/core/util.h index 4b1cecfb..1bc7cc4d 100644 --- a/src/core/util.h +++ b/src/core/util.h @@ -57,5 +57,6 @@ int janet_lib_parse(JanetArgs args); int janet_lib_asm(JanetArgs args); #endif int janet_lib_compile(JanetArgs args); +int janet_lib_debug(JanetArgs args); #endif diff --git a/src/core/vm.c b/src/core/vm.c index 09a1b6f3..1e1d2453 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -54,6 +54,8 @@ JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out) { /* Expected types on type error */ uint16_t expected_types; + uint8_t first_opcode; + /* Signal to return when done */ JanetSignal signal = JANET_SIGNAL_OK; @@ -92,16 +94,24 @@ JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out) { * instruction. */ retreg = in; 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 */ /* If waiting for response to signal, use input and increment pc */ stack[oparg(1, 0xFF)] = in; 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 */ -#ifdef __GNUC__ -#define VM_START() {vm_next(); +#ifdef ____GNUC__ +#define VM_START() { goto *op_lookup[first_opcode]; #define VM_END() } #define VM_OP(op) label_##op : #define VM_DEFAULT() label_unknown_op: @@ -193,11 +203,11 @@ static void *op_lookup[255] = { &&label_unknown_op }; #else -#define VM_START() for(;;){switch(*pc & 0xFF){ +#define VM_START() uint8_t opcode = first_opcode; for (;;) {switch(opcode) { #define VM_END() }} #define VM_OP(op) case op : #define VM_DEFAULT() default: -#define vm_next() continue +#define vm_next() opcode = *pc & 0xFF; continue #endif #define vm_checkgc_next() janet_maybe_collect(); vm_next() @@ -279,6 +289,7 @@ static void *op_lookup[255] = { VM_START(); VM_DEFAULT(); + signal = JANET_SIGNAL_DEBUG; retreg = janet_wrap_nil(); goto vm_exit; @@ -535,7 +546,6 @@ static void *op_lookup[255] = { pc++; vm_next(); - /* Candidate */ VM_OP(JOP_GREATER_THAN_INTEGER) stack[oparg(1, 0xFF)] = janet_wrap_boolean( janet_unwrap_integer(stack[oparg(2, 0xFF)]) > @@ -543,7 +553,6 @@ static void *op_lookup[255] = { pc++; vm_next(); - /* Candidate */ VM_OP(JOP_GREATER_THAN_IMMEDIATE) stack[oparg(1, 0xFF)] = janet_wrap_boolean( janet_unwrap_integer(stack[oparg(2, 0xFF)]) > ((*(int32_t *)pc) >> 24) @@ -551,7 +560,6 @@ static void *op_lookup[255] = { pc++; vm_next(); - /* Candidate */ VM_OP(JOP_GREATER_THAN_REAL) stack[oparg(1, 0xFF)] = janet_wrap_boolean( janet_unwrap_real(stack[oparg(2, 0xFF)]) > @@ -559,7 +567,6 @@ static void *op_lookup[255] = { pc++; vm_next(); - /* Candidate */ VM_OP(JOP_GREATER_THAN_EQUAL_REAL) stack[oparg(1, 0xFF)] = janet_wrap_boolean( janet_unwrap_real(stack[oparg(2, 0xFF)]) >= @@ -575,7 +582,6 @@ static void *op_lookup[255] = { pc++; vm_next(); - /* Candidate */ VM_OP(JOP_EQUALS_INTEGER) stack[oparg(1, 0xFF)] = janet_wrap_boolean( janet_unwrap_integer(stack[oparg(2, 0xFF)]) == @@ -584,7 +590,6 @@ static void *op_lookup[255] = { pc++; vm_next(); - /* Candidate */ VM_OP(JOP_EQUALS_REAL) stack[oparg(1, 0xFF)] = janet_wrap_boolean( janet_unwrap_real(stack[oparg(2, 0xFF)]) == @@ -593,7 +598,6 @@ static void *op_lookup[255] = { pc++; vm_next(); - /* Candidate */ VM_OP(JOP_EQUALS_IMMEDIATE) stack[oparg(1, 0xFF)] = janet_wrap_boolean( janet_unwrap_integer(stack[oparg(2, 0xFF)]) == ((*(int32_t *)pc) >> 24) diff --git a/src/include/janet/janet.h b/src/include/janet/janet.h index ec5bcf1d..2543441b 100644 --- a/src/include/janet/janet.h +++ b/src/include/janet/janet.h @@ -204,6 +204,8 @@ extern "C" { /* Names of all of the types */ 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 */ typedef enum { @@ -667,8 +669,8 @@ struct JanetKV { /* Source mapping structure for a bytecode instruction */ struct JanetSourceMapping { - int32_t line; - int32_t column; + int32_t start; + int32_t end; }; /* A function definition. Contains information needed to instantiate closures. */ @@ -731,8 +733,7 @@ struct JanetParser { size_t statecap; size_t bufcount; size_t bufcap; - size_t line; - size_t col; + size_t offset; 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_length(t) (janet_tuple_raw(t)[0]) #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_col(t) ((janet_tuple_raw(t)[3])) +#define janet_tuple_sm_start(t) ((janet_tuple_raw(t)[2])) +#define janet_tuple_sm_end(t) ((janet_tuple_raw(t)[3])) JANET_API Janet *janet_tuple_begin(int32_t length); JANET_API const Janet *janet_tuple_end(Janet *tuple); JANET_API const Janet *janet_tuple_n(const Janet *values, int32_t n); diff --git a/src/mainclient/init.janet b/src/mainclient/init.janet index d82d10fd..5ac289eb 100644 --- a/src/mainclient/init.janet +++ b/src/mainclient/init.janet @@ -55,6 +55,6 @@ (do (print (string "Janet " janet/version "-" janet/build " Copyright (C) 2017-2018 Calvin Rose")) (repl (fn [buf p] - (def [line] (parser/where p)) - (def prompt (string "janet:" line ":" (parser/state p) "> ")) + (def offset (parser/where p)) + (def prompt (string "janet:" offset ":" (parser/state p) "> ")) (getline prompt buf)))))))