mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-31 15:43:01 +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:
		| @@ -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) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose