From 7432cf82d53e5f0733b9293f02a9354e83d08d4d Mon Sep 17 00:00:00 2001 From: "Aaron K. Clark" Date: Tue, 26 May 2026 01:52:52 -0500 Subject: [PATCH] =?UTF-8?q?feat(decompiler):=20Rec=2031=20#31-3=20RAII=20S?= =?UTF-8?q?tage=202B=20=E2=80=94=20xml=5Fparse=20global=5Fscan=20unique=5F?= =?UTF-8?q?ptr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stacked PR on top of the XmlScan::lvalue migration (PR #51). Same hand-edit pattern: epilogue change in xml.y + matching parallel edit in xml.cc, no bison regeneration required. Converts xml_parse()'s pairing of `global_scan = new XmlScan(i)` / `delete global_scan;` to a scope-bound unique_ptr that holds ownership across the yyparse call: auto scan = make_unique(i); global_scan = scan.get(); // raw observer pointer for grammar actions ... int4 res = yyparse(); ... global_scan = (XmlScan *)0; // null observer before scan goes out of scope return res; `global_scan` stays a `static XmlScan *` (raw observer) because it's accessed from yyparse / yylex / grammar semantic actions — those see it through the symbol declared at file scope, and changing it to unique_ptr would require changing every accessor. The observer pattern keeps the surface area minimal: only xml_parse owns; everyone else just borrows during the parse. The explicit `global_scan = nullptr` before return is defensive — if anything ever tries to dereference global_scan after xml_parse exits, crash on null is far better than use-after-free on dangling. After this PR, the only raw `new`s remaining in the xml.y / xml.cc epilogue are: - line 538 `new Element(cur)` — parse-tree node allocation, owned by parent's children list; needs Element class field refactor. - line 624 `new Document()` — returned across xml_tree() API to XmlDecode; needs xml.hh + XmlDecode coordination. Both are separate PRs. The bison semantic-action raw `new`s (xml.y:150, 153, 198, 200, 208) remain blocked on `%union` redesign. Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/ Co-Authored-By: Claude Opus 4.7 (1M context) --- Ghidra/Features/Decompiler/src/decompile/cpp/xml.cc | 5 +++-- Ghidra/Features/Decompiler/src/decompile/cpp/xml.y | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/xml.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/xml.cc index 13bf3ccf233..3d8ea8208e0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/xml.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/xml.cc @@ -2380,13 +2380,14 @@ int4 xml_parse(istream &i,ContentHandler *hand,int4 dbg) #if YYDEBUG yydebug = dbg; #endif - global_scan = new XmlScan(i); + auto scan = make_unique(i); + global_scan = scan.get(); // raw observer pointer for yyparse / yylex / grammar actions handler = hand; handler->startDocument(); int4 res = yyparse(); if (res == 0) handler->endDocument(); - delete global_scan; + global_scan = (XmlScan *)0; // scan goes out of scope below — null the observer first return res; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/xml.y b/Ghidra/Features/Decompiler/src/decompile/cpp/xml.y index f50093b8c31..28896aa347a 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/xml.y +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/xml.y @@ -522,13 +522,14 @@ int4 xml_parse(istream &i,ContentHandler *hand,int4 dbg) #if YYDEBUG yydebug = dbg; #endif - global_scan = new XmlScan(i); + auto scan = make_unique(i); + global_scan = scan.get(); // raw observer pointer for yyparse / yylex / grammar actions handler = hand; handler->startDocument(); int4 res = yyparse(); if (res == 0) handler->endDocument(); - delete global_scan; + global_scan = (XmlScan *)0; // scan goes out of scope below — null the observer first return res; }