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:
parent
4b01409d2d
commit
e8c0dcd14e
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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})
|
||||
|
@ -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
|
||||
|
289
src/core/debug.c
Normal file
289
src/core/debug.c
Normal 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;
|
||||
}
|
114
src/core/fiber.c
114
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 "
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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)))))))
|
||||
|
Loading…
Reference in New Issue
Block a user