1 /* 2 * Copyright (C) 2026 Jo-Philipp Wich <jo@mein.io> 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ANY IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 * 16 * --- 17 * 18 * ucode/utpl syntax highlighting and ANSI source rendering, ported from the 19 * pre-protocol interactive debugger (formerly lib/debug.c's 20 * highlight_rules[]/compile_patterns()/print_source_location()) so it can 21 * be reused by any client speaking the line-based debug protocol - or 22 * anything else that wants to print ucode source with the same styling. 23 * 24 * This module is intentionally standalone: no ucode headers, no protocol 25 * knowledge, just POSIX regex + stdio. A caller supplies already-split 26 * source lines and, optionally, a single line/column range to shade as the 27 * "current statement" - the multi-range/ellipsis-gap layout the original 28 * server-side renderer supported for very large statements is not ported, 29 * since every client of the debug protocol only ever receives one 30 * contiguous range at a time (see SOURCE_RANGE in lib/debug_proto.h). 31 */ 32 33 #ifndef _DEBUG_HIGHLIGHT_H 34 #define _DEBUG_HIGHLIGHT_H 35 36 #include <stdio.h> 37 #include <stddef.h> 38 #include <stdbool.h> 39 #include <stdint.h> 40 41 /* A statement span to shade, in 1-based line numbers and 0-based byte 42 * columns within those lines (matching the debug protocol's "col" fields). 43 * Set from_line to 0 for "no highlight". `have_ip` additionally underlines 44 * the single character at {ip_line, ip_col} - the exact current 45 * instruction position, as opposed to {from,to} which mark the enclosing 46 * statement's extent. */ 47 typedef struct { 48 size_t from_line, from_col; 49 size_t to_line, to_col; 50 bool have_ip; 51 size_t ip_line, ip_col; 52 } debug_highlight_span_t; 53 54 /* Compile the highlight regexes once; safe to call repeatedly. Returns 55 * false (and prints a diagnostic to stderr) on a regex compile error, in 56 * which case debug_highlight_print_source() below still works, just 57 * without coloring. */ 58 bool debug_highlight_init(void); 59 60 /* Print source lines [from, to] (1-based, inclusive, clamped to 61 * [1, nlines]) from the `nlines`-element `lines` array (as produced by 62 * splitting raw source text on '\n', with no trailing newlines) to `out`, 63 * applying ucode/utpl syntax highlighting and, if `hl` is non-NULL, shading 64 * the statement range it describes. Every printed line is prefixed with 65 * `left_pad` blank columns plus a right-aligned line number gutter. 66 * `columns` is the terminal width to wrap/pad to (pass 0 for "don't know", 67 * which disables truncation and trailing-space padding). */ 68 void debug_highlight_print_source(FILE *out, char **lines, size_t nlines, 69 size_t from, size_t to, 70 const debug_highlight_span_t *hl, 71 size_t left_pad, size_t columns); 72 73 /* A single [from, to] (1-based, inclusive) line range, for the multi-range 74 * form below. */ 75 typedef struct { 76 size_t from, to; 77 } debug_highlight_range_t; 78 79 /* Like debug_highlight_print_source(), but for up to `nranges` disjoint 80 * ranges at once - lines that fall in a gap between two ranges are skipped 81 * with a single " … " ellipsis marker rather than printed, matching the 82 * original format_context_statement()'s handling of a statement too long 83 * to show in full: a window of context at its start, a gap, and a window 84 * around the current instruction/its end. Ranges need not be sorted; a 85 * {0, 0} entry is ignored (so callers can pass a fixed-size array without 86 * always filling every slot). */ 87 void debug_highlight_print_source_ranges(FILE *out, char **lines, size_t nlines, 88 size_t nranges, 89 const debug_highlight_range_t *ranges, 90 const debug_highlight_span_t *hl, 91 size_t left_pad, size_t columns); 92 93 /* Print a full-width "[bracket] rest " status bar to `out` on a solid 94 * background, ported from the original format_context_header_backtrace()/ 95 * format_context_header_callframe() (the bar shown above a paused 96 * location's or a backtrace frame's source snippet) - `bracket` is the 97 * source file (or "C" for a native frame), `rest` the call breadcrumb or 98 * frame signature. Long `rest` values are elided from the front (ellipsis 99 * first, keeping the tail - the original's choice, since the innermost/ 100 * current part of a chain matters more than the outermost when both don't 101 * fit) if `columns` is nonzero; pass 0 to disable width awareness (no 102 * truncation, no trailing padding). */ 103 void debug_highlight_print_header_bar(FILE *out, const char *bracket, 104 const char *rest, 105 size_t left_pad, size_t columns); 106 107 /* A single decoded bytecode instruction, as reported by the DISASSEMBLE 108 * protocol response's "instructions" array, for 109 * debug_highlight_print_disassembly() below. Which of the optional fields 110 * are populated selects what annotation (if any) is shown after the raw 111 * operand - the renderer itself has no notion of opcode names or their 112 * meaning, it only reacts to which fields the caller filled in. */ 113 typedef struct { 114 size_t offset; 115 const char *mnemonic; 116 int format; /* uc_vm_insn_format[] value: 0, 1, 2, 4 or -4 */ 117 const unsigned char *bytes; size_t nbytes; /* raw instruction bytes */ 118 int64_t operand; /* decoded operand; sign only meaningful for format -4; 119 * unused when format == 0 */ 120 121 bool have_constant; 122 const char *constant_repr; /* JSON text of the constant value */ 123 bool constant_is_string; 124 125 const char *variable_kind; /* "local", "upval", "global", or NULL */ 126 const char *variable_name; 127 128 bool have_closure; 129 const char *closure_kind; /* "closure" or "arrow" */ 130 uint32_t closure_index; 131 132 bool have_call; 133 bool call_mcall; /* method call: an implicit `this` arg follows */ 134 uint32_t call_nargs; 135 bool call_tail; /* tail call: I_RETURN + 0x00 marker follows the 136 * call's operand span, the VM reuses the current 137 * frame for the callee */ 138 139 bool return_tailcall; /* this I_RETURN terminates a tail call: the 0x00 140 * marker byte the compiler emits right after it is 141 * consumed as part of this instruction (pure data, 142 * never executed) */ 143 144 struct { 145 int64_t slot; 146 bool upval; 147 const char *name; 148 unsigned char bytes[4]; 149 } *captures; size_t ncaptures; 150 151 struct { 152 uint16_t slot; 153 unsigned char bytes[2]; 154 } *unpacks; size_t nunpacks; 155 } debug_disasm_insn_t; 156 157 /* Print a disassembly listing exactly as the pre-protocol interactive 158 * debugger's `disassemble` command did: address, a color-coded raw byte 159 * dump (opcode byte plain, operand bytes bright magenta), the mnemonic, 160 * the decoded operand, and - when the caller supplied it - a semantic 161 * annotation (constant value, local/upval/global name, closure/arrow 162 * index) plus extra indented lines for closure upvalue captures or call 163 * argument unpacks. `columns` is the terminal width to wrap to (pass 0 164 * for "don't know", which disables truncation). */ 165 void debug_highlight_print_disassembly(FILE *out, const char *function, 166 const debug_disasm_insn_t *insns, 167 size_t ninsns, size_t columns); 168 169 /* One entry of a VARIABLES (or a BACKTRACE frame's inline "variables") 170 * protocol response, for debug_highlight_print_variables() below. `kind` 171 * is one of "this", "local", "internal" (a synthetic, parenthesized slot 172 * name such as a `for`-loop's hidden iterator) or "upvalue". `shadowed` 173 * marks a same-named, less-nested declaration that a more-nested one 174 * currently hides - still a real, live slot, just not what plain script 175 * code resolves this name to right now. */ 176 typedef struct { 177 const char *name; 178 const char *kind; 179 const char *value_repr; 180 bool shadowed; 181 } debug_variable_t; 182 183 /* Print a "name : value" variable listing exactly as the pre-protocol 184 * interactive debugger's print_variables() did: the name in a fixed 185 * 16-column field (tail-truncated with an ellipsis if longer), styled 186 * bold cyan for an upvalue or faint white for "this"/an internal slot 187 * (plain otherwise), a faint " : " separator, then the value - styled 188 * bold red instead of truncated when it is the literal sentinel 189 * "<out of range>". A shadowed entry (see above - not part of the 190 * original pre-protocol listing, which never showed more than one 191 * variable per name to begin with) is rendered faint throughout with a 192 * trailing "(shadowed)" marker. Every line is prefixed with `indent`. 193 * `columns` is the terminal width the value is truncated to fit (pass 0 194 * for "don't know", which disables value truncation only - the name 195 * field is always truncated to 16 regardless). */ 196 void debug_highlight_print_variables(FILE *out, const debug_variable_t *vars, 197 size_t nvars, const char *indent, 198 size_t columns); 199 200 #endif 201
This page was automatically generated by LXR 0.3.1. • OpenWrt