From ec520227c541e7ef782b92c68b2b86d35d6ac464 Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Mon, 27 Jul 2026 16:02:10 -0700 Subject: [PATCH] enforce standalone 'expression' child for watch expressions in cfg tree, adjust eval / ui to work on that child, instead of assuming the first child of a watch expression node is the expression string itself - this will let us tag watch expressions with other info, like project / lock status etc. --- project.raddbg | 2 +- src/raddbg/generated/raddbg.meta.c | 5 +++-- src/raddbg/generated/raddbg.meta.h | 2 +- src/raddbg/raddbg.mdesk | 3 ++- src/raddbg/raddbg_core.c | 30 ++++++++++++++++-------------- src/raddbg/raddbg_eval.c | 5 +++-- 6 files changed, 26 insertions(+), 21 deletions(-) diff --git a/project.raddbg b/project.raddbg index 689b88cc..9637d013 100644 --- a/project.raddbg +++ b/project.raddbg @@ -71,5 +71,5 @@ target: target: { executable: "../../Program Files/SuperTux/bin/supertux2.exe" - working_directory: "../../Program Files/SuperTux/bin/" + working_directory: "../../Program Files/SuperTux/bin" } diff --git a/src/raddbg/generated/raddbg.meta.c b/src/raddbg/generated/raddbg.meta.c index adfc729f..6b9a64e0 100644 --- a/src/raddbg/generated/raddbg.meta.c +++ b/src/raddbg/generated/raddbg.meta.c @@ -62,13 +62,14 @@ str8_lit_comp(""), str8_lit_comp(""), }; -RD_VocabInfo rd_vocab_info_table[368] = +RD_VocabInfo rd_vocab_info_table[369] = { {str8_lit_comp("type_view"), str8_lit_comp("type_views"), str8_lit_comp("Type View"), str8_lit_comp("Type Views"), RD_IconKind_Binoculars}, {str8_lit_comp("file_path_map"), str8_lit_comp("file_path_maps"), str8_lit_comp("File Path Map"), str8_lit_comp("File Path Maps"), RD_IconKind_FileOutline}, {str8_lit_comp("watch_pin"), str8_lit_comp("watch_pins"), str8_lit_comp("Watch Pin"), str8_lit_comp("Watch Pins"), RD_IconKind_Pin}, {str8_lit_comp("debug_info"), str8_lit_comp("debug_infos"), str8_lit_comp("Debug Info"), str8_lit_comp("Debug Info"), RD_IconKind_Module}, {str8_lit_comp("watch"), str8_lit_comp("watches"), str8_lit_comp("Watch"), str8_lit_comp("Watches"), RD_IconKind_Binoculars}, +{str8_lit_comp("watch_expression"), str8_lit_comp("watch_expressions"), str8_lit_comp("Watch Expression"), str8_lit_comp("Watch Expressions"), RD_IconKind_Binoculars}, {str8_lit_comp("view"), str8_lit_comp("views"), str8_lit_comp("View"), str8_lit_comp("Views"), RD_IconKind_Binoculars}, {str8_lit_comp("breakpoint"), str8_lit_comp("breakpoints"), str8_lit_comp("Breakpoint"), str8_lit_comp("Breakpoints"), RD_IconKind_CircleFilled}, {str8_lit_comp("condition"), str8_lit_comp("conditions"), str8_lit_comp("Condition"), str8_lit_comp("Conditions"), RD_IconKind_Null}, @@ -456,7 +457,7 @@ RD_NameSchemaInfo rd_name_schema_info_table[39] = {str8_lit_comp("array"), 1, str8_lit_comp("x:{ @description('An expression of the base address of the array.') 'base_address': expr_string, @description('The number of elements in the array.') count}")}, {str8_lit_comp("slice"), 1, str8_lit_comp("x:{ @description('An expression of a structure that is to be interpreted as a slice.') 'expression': expr_string}")}, {str8_lit_comp("list"), 1, str8_lit_comp("x:\n{\n @description(\"An expression describing the first node in the list.\")\n 'expression': expr_string,\n @order(0) @description(\"The name of the member which encodes the link to the next node.\")\n 'member_name': code_string,\n}\n")}, -{str8_lit_comp("watch"), 0, str8_lit_comp("@inherit(tab) x:\n{\n @override @display_name('Tab Row Height') @description(\"Controls the tab's row height, in multiples of the font size.\")\n 'row_height': @range[1.75f, 5.f] f32,\n 'label': code_string,\n @description(\"The root expression which is evaluated to produce the watch window.\")\n 'expression': expr_string,\n @no_expand 'watches': set,\n}\n")}, +{str8_lit_comp("watch"), 0, str8_lit_comp("@inherit(tab) x:\n{\n @override @display_name('Tab Row Height') @description(\"Controls the tab's row height, in multiples of the font size.\")\n 'row_height': @range[1.75f, 5.f] f32,\n 'label': code_string,\n @description(\"The root expression which is evaluated to produce the watch window.\")\n 'expression': expr_string,\n @no_expand 'watch_expressions': set,\n}\n")}, {str8_lit_comp("text"), 1, str8_lit_comp("@inherit(tab) @expand_commands(@output clear_output) x:\n{\n @description(\"An expression to describe data which should be viewed as text or code.\")\n 'expression': expr_string,\n @optional @description(\"The language that the text should be interpreted as being within. Used for syntax highlighting and other parsing features.\")\n 'lang': code_string,\n @no_callee_helper @default(1) @description(\"Controls whether or not line numbers are shown.\")\n 'show_line_numbers':bool,\n @no_callee_helper @default(1) @display_name('Line Wrapping') @description(\"Splits textual lines into multiple visual lines, so that all text is within the visible area.\")\n 'line_wrapping': bool,\n @no_callee_helper @default(0) @display_name('Scroll To Bottom On Change') @description(\"Scrolls to the bottom if the text is changed.\")\n 'scroll_to_bottom_on_change': bool,\n @no_callee_helper @no_revert @default(0) @display_name('Transient') @description(\"Controls whether or not this tab will be automatically replaced by the debugger when it snaps to new source code locations.\")\n 'auto': bool,\n}\n")}, {str8_lit_comp("disasm"), 1, str8_lit_comp("@inherit(tab) x:\n{\n @description(\"An expression to describe the base address or offset of the disassembly.\")\n 'expression': expr_string,\n @optional @description(\"The maximum number of bytes to disassemble.\")\n 'size': expr_string,\n @optional @description(\"The architecture to interpret the data as when disassembling.\")\n 'arch': code_string,\n @optional @description(\"The syntax style to use when displaying the disassembly textually.\")\n 'syntax': code_string,\n @no_callee_helper @default(1) @description(\"Controls whether or not addresses are shown in the disassembly text.\")\n 'show_addresses': bool,\n @no_callee_helper @default(0) @description(\"Controls whether or not code bytes are shown in the disassembly text.\")\n 'show_code_bytes': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not source lines, corresponding to disassembly instruction ranges, are shown in the disassembly text.\")\n 'show_source_lines': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not disassembly text is decorated with symbol names.\")\n 'show_symbol_names': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not line numbers are shown.\")\n 'show_line_numbers': bool,\n}\n")}, {str8_lit_comp("memory"), 1, str8_lit_comp("@inherit(tab) x:\n{\n @display_name(\"Base Address\") @description(\"An expression which refers to the base address of data which should be viewed as memory.\")\n 'expression': expr_string,\n @no_callee_helper @display_name(\"Zoom\") @description(\"The zoom level for displaying bytes in the memory view.\")\n @default(1.0) 'zoom': @range[0.5, 2] f32,\n @optional @display_name(\"Address Range Size\") @description(\"The number of bytes of the viewed memory range.\")\n 'size': expr_string,\n @no_callee_helper @display_name(\"Cursor Address\") @description(\"The address of the cursor.\")\n 'cursor': expr_string,\n @no_callee_helper @default(1) @display_name(\"Cursor Size\") @description(\"The size, in bytes, of the cursor.\")\n 'cursor_size': @range[1, 16] u64,\n @optional @expand_if(\"!$.auto_columns\") @default(16) @description(\"The number of columns to build before building new rows.\")\n 'num_columns': @range[1, 64] u64,\n @optional @default(16) @display_name(\"Default Radix\") @description(\"The default radix with which numeric values should be displayed, when peeking.\")\n @or(2, 8, 10, 16)\n 'default_radix': u64,\n @no_callee_helper @default(1) @display_name(\"Track Mark To Cursor\") @description(\"Ensures that the mark always follows the cursor, if the cursor value is updated.\")\n 'track_mark_to_cursor': bool,\n @no_callee_helper @default(1) @display_name(\"Allow Mutation\") @description(\"Allows operations which mutate memory.\")\n 'allow_mutation': bool,\n @no_callee_helper @default(0) @display_name(\"Automatically Size Columns\") @description(\"Determines the number of columns based on the available space.\")\n 'auto_columns': bool,\n @no_callee_helper @default(1) @display_name(\"Peek As Unsigned\") 'peek_as_unsigned': bool,\n @no_callee_helper @default(1) @display_name(\"Peek As Signed\") 'peek_as_signed': bool,\n @no_callee_helper @default(1) @display_name(\"Peek As Float\") 'peek_as_float': bool,\n @no_callee_helper @display_name(\"Extra Peek Types\") @description(\"A list of types as which to interpret selected memory.\")\n 'peek_types': set,\n}\n")}, diff --git a/src/raddbg/generated/raddbg.meta.h b/src/raddbg/generated/raddbg.meta.h index 0fb38188..744003f2 100644 --- a/src/raddbg/generated/raddbg.meta.h +++ b/src/raddbg/generated/raddbg.meta.h @@ -623,7 +623,7 @@ Z(getting_started)\ C_LINKAGE_BEGIN extern String8 rd_tab_fast_path_view_name_table[25]; extern String8 rd_tab_fast_path_query_name_table[25]; -extern RD_VocabInfo rd_vocab_info_table[368]; +extern RD_VocabInfo rd_vocab_info_table[369]; extern RD_NameSchemaInfo rd_name_schema_info_table[39]; extern String8 rd_reg_slot_code_name_table[54]; extern Rng1U64 rd_reg_slot_range_table[54]; diff --git a/src/raddbg/raddbg.mdesk b/src/raddbg/raddbg.mdesk index 7464c3be..4355bb5b 100644 --- a/src/raddbg/raddbg.mdesk +++ b/src/raddbg/raddbg.mdesk @@ -94,6 +94,7 @@ RD_VocabTable: {watch_pin _ "Watch Pin" _ Pin } {debug_info _ "Debug Info" "Debug Info" Module } {watch watches "Watch" "Watches" Binoculars } + {watch_expression _ "Watch Expression" _ Binoculars } {view _ "View" _ Binoculars } {breakpoint _ "Breakpoint" _ CircleFilled } {condition _ "Condition" _ Null } @@ -560,7 +561,7 @@ RD_VocabTable: 'label': code_string, @description("The root expression which is evaluated to produce the watch window.") 'expression': expr_string, - @no_expand 'watches': set, + @no_expand 'watch_expressions': set, } ``` } diff --git a/src/raddbg/raddbg_core.c b/src/raddbg/raddbg_core.c index 14f952a3..38dc7316 100644 --- a/src/raddbg/raddbg_core.c +++ b/src/raddbg/raddbg_core.c @@ -2034,7 +2034,7 @@ rd_view_ui(Rng2F32 rect) { if(expr_string.size == 0) { - expr_string = str8f(scratch.arena, "query:config.$%I64x.watches", rd_regs()->view); + expr_string = str8f(scratch.arena, "query:config.$%I64x.watch_expressions", rd_regs()->view); } E_Eval eval = e_eval_from_string(expr_string); RD_WatchViewState *ewv = rd_view_state(RD_WatchViewState); @@ -2778,13 +2778,13 @@ rd_view_ui(Rng2F32 rect) { CFG_Node *cfg = row_info.group_cfg_child; String8 child_key = {0}; // str8_lit("expression"); + if(str8_match(row_info.group_cfg_name, s("watch_expression"), 0)) + { + child_key = s("expression"); + } if(cfg == &cfg_nil_node && editing_complete && new_string.size != 0) { CFG_Node *new_cfg_parent = row_info.group_cfg_parent; - if(new_cfg_parent != &cfg_nil_node) - { - child_key = str8_zero(); - } if(new_cfg_parent == &cfg_nil_node) { CFG_NodePtrList all_cfgs = cfg_node_top_level_list_from_string(scratch.arena, row_info.group_cfg_name); @@ -3526,8 +3526,9 @@ rd_view_ui(Rng2F32 rect) if(cfg == &cfg_nil_node) { cfg = cfg_node_alloc(rd_state->cfg); - cfg_node_equip_stringf(rd_state->cfg, cfg, "watch"); - cfg_node_new(rd_state->cfg, cfg, drag_regs->expr); + cfg_node_equip_stringf(rd_state->cfg, cfg, "watch_expression"); + CFG_Node *expr = cfg_node_new(rd_state->cfg, cfg, s("expression")); + cfg_node_new(rd_state->cfg, expr, drag_regs->expr); } cfg_node_insert_child(rd_state->cfg, drag_parent_cfg, drag_prev_cfg, cfg); }break; @@ -12187,10 +12188,10 @@ rd_frame(void) { continue; } - if(str8_match(child->string, str8_lit("watch"), 0)) + if(str8_match(child->string, str8_lit("watch_expression"), 0)) { CFG_Node *watch = child; - String8 expr = watch->first->string; + String8 expr = cfg_node_child_from_string(watch, s("expression"))->first->string; E_Parse parse = e_parse_from_string(expr); if(parse.msgs.max_kind == E_MsgKind_Null) { @@ -12383,10 +12384,10 @@ rd_frame(void) .id_from_num = E_TYPE_EXPAND_ID_FROM_NUM_FUNCTION_NAME(environment), .num_from_id = E_TYPE_EXPAND_NUM_FROM_ID_FUNCTION_NAME(environment), })); - e_string2typekey_map_insert(rd_frame_arena(), rd_state->meta_name2type_map, str8_lit("watches"), + e_string2typekey_map_insert(rd_frame_arena(), rd_state->meta_name2type_map, str8_lit("watch_expressions"), e_type_key_cons(.kind = E_TypeKind_Set, .flags = E_TypeFlag_EditableChildren|E_TypeFlag_StubSingleLineExpansion, - .name = str8_lit("watches"), + .name = str8_lit("watch_expressions"), .irext = E_TYPE_IREXT_FUNCTION_NAME(watches), .access = E_TYPE_ACCESS_FUNCTION_NAME(watches), .expand = @@ -16563,7 +16564,7 @@ rd_frame(void) { continue; } - if(str8_match(child->string, str8_lit("watch"), 0) && str8_match(child->first->string, rd_regs()->string, 0)) + if(str8_match(child->string, str8_lit("watch_expression"), 0) && str8_match(child->first->string, rd_regs()->string, 0)) { existing_watch = child; break; @@ -16579,8 +16580,9 @@ rd_frame(void) // rjf: otherwise, create it else if(watch_tab != &cfg_nil_node) { - CFG_Node *watch = cfg_node_new(rd_state->cfg, watch_tab, str8_lit("watch")); - cfg_node_new(rd_state->cfg, watch, rd_regs()->string); + CFG_Node *watch = cfg_node_new(rd_state->cfg, watch_tab, s("watch_expression")); + CFG_Node *expr = cfg_node_new(rd_state->cfg, watch, s("expression")); + cfg_node_new(rd_state->cfg, expr, rd_regs()->string); } }break; diff --git a/src/raddbg/raddbg_eval.c b/src/raddbg/raddbg_eval.c index f5fc95ca..ea0f0d62 100644 --- a/src/raddbg/raddbg_eval.c +++ b/src/raddbg/raddbg_eval.c @@ -1389,7 +1389,7 @@ E_TYPE_IREXT_FUNCTION_DEF(watches) for(CFG_Node *child = target->first; child != &cfg_nil_node; child = child->next) { if(rd_cfg_is_project_filtered(child)) {continue;} - if(str8_match(child->string, str8_lit("watch"), 0)) + if(str8_match(child->string, str8_lit("watch_expression"), 0)) { cfg_node_ptr_list_push(scratch.arena, &cfgs, child); } @@ -1461,7 +1461,8 @@ E_TYPE_EXPAND_RANGE_FUNCTION_DEF(watches) if(cfg_idx < accel->cfgs.count) { CFG_Node *cfg = accel->cfgs.v[cfg_idx]; - evals_out[idx] = e_eval_from_string(cfg->first->string); + CFG_Node *expr = cfg_node_child_from_string(cfg, s("expression")); + evals_out[idx] = e_eval_from_string(expr->first->string); } } }