• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/debug_highlight.h

  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