Skip to content

Commit 2d8cf82

Browse files
committed
align comments with code in decompiler
Add a configuration option for the decompiler to align comments with code instead of using the fixed indentation setting.
1 parent d55b7e9 commit 2d8cf82

File tree

8 files changed

+156
-4
lines changed

8 files changed

+156
-4
lines changed

Ghidra/Features/Decompiler/src/decompile/cpp/options.cc

+12
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ ElementId ELEM_SETLANGUAGE = ElementId("setlanguage",207);
5555
ElementId ELEM_STRUCTALIGN = ElementId("structalign",208);
5656
ElementId ELEM_TOGGLERULE = ElementId("togglerule",209);
5757
ElementId ELEM_WARNING = ElementId("warning",210);
58+
ElementId ELEM_COMMENTINDENTALIGN = ElementId("commentindentalign",271);
5859

5960
/// If the parameter is "on" return \b true, if "off" return \b false.
6061
/// Any other value causes an exception.
@@ -109,6 +110,7 @@ OptionDatabase::OptionDatabase(Architecture *g)
109110
registerOption(new OptionMaxLineWidth());
110111
registerOption(new OptionIndentIncrement());
111112
registerOption(new OptionCommentIndent());
113+
registerOption(new OptionCommentIndentAlign());
112114
registerOption(new OptionCommentStyle());
113115
registerOption(new OptionCommentHeader());
114116
registerOption(new OptionCommentInstruction());
@@ -524,6 +526,16 @@ string OptionCommentIndent::apply(Architecture *glb,const string &p1,const strin
524526
return "Comment indent set to "+p1;
525527
}
526528

529+
/// \class OptionCommentIndentAlign
530+
/// \brief Toggle whether to align the comment with the current code rather or use a fixed indentation.
531+
string OptionCommentIndentAlign::apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const
532+
533+
{
534+
bool val = onOrOff(p1);
535+
glb->print->setLineCommentIndentAlign(val);
536+
return "Comment indent alignment turned "+p1;
537+
}
538+
527539
/// \class OptionCommentStyle
528540
/// \brief Set the style of comment emitted by the decompiler
529541
///

Ghidra/Features/Decompiler/src/decompile/cpp/options.hh

+7
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ extern ElementId ELEM_SETLANGUAGE; ///< Marshaling element \<setlanguage>
6161
extern ElementId ELEM_STRUCTALIGN; ///< Marshaling element \<structalign>
6262
extern ElementId ELEM_TOGGLERULE; ///< Marshaling element \<togglerule>
6363
extern ElementId ELEM_WARNING; ///< Marshaling element \<warning>
64+
extern ElementId ELEM_COMMENTINDENTALIGN; ///< Marshaling element \<commentindentalign>
6465

6566
/// \brief Base class for options classes that affect the configuration of the Architecture object
6667
///
@@ -212,6 +213,12 @@ public:
212213
virtual string apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const;
213214
};
214215

216+
class OptionCommentIndentAlign : public ArchOption {
217+
public:
218+
OptionCommentIndentAlign(void) { name = "commentindentalign"; } ///< Constructor
219+
virtual string apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const;
220+
};
221+
215222
class OptionCommentStyle : public ArchOption {
216223
public:
217224
OptionCommentStyle(void) { name = "commentstyle"; } ///< Constructor

Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.cc

+16-3
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ void PrintLanguage::setLineCommentIndent(int4 val)
8787
line_commentindent = val;
8888
}
8989

90+
/// \param val is whether to align comments with code or use a fixed indentation
91+
void PrintLanguage::setLineCommentIndentAlign(bool val)
92+
93+
{
94+
line_commentindentalign = val;
95+
}
96+
9097
/// By default, comments are indicated in the high-level language by preceding
9198
/// them with a specific sequence of delimiter characters, and optionally
9299
/// by ending the comment with another set of delimiter characters.
@@ -573,6 +580,7 @@ void PrintLanguage::resetDefaultsInternal(void)
573580
mods = 0;
574581
head_comment_type = Comment::header | Comment::warningheader;
575582
line_commentindent = 20;
583+
line_commentindentalign = false;
576584
namespc_strategy = MINIMAL_NAMESPACES;
577585
instr_comment_type = Comment::user2 | Comment::warning;
578586
}
@@ -587,9 +595,14 @@ void PrintLanguage::emitLineComment(int4 indent,const Comment *comm)
587595
const string &text( comm->getText() );
588596
const AddrSpace *spc = comm->getAddr().getSpace();
589597
uintb off = comm->getAddr().getOffset();
590-
if (indent <0)
591-
indent = line_commentindent; // User specified default indent
592-
emit->tagLine(indent);
598+
if (line_commentindentalign) {
599+
emit->tagLine();
600+
}
601+
else {
602+
if (indent <0)
603+
indent = line_commentindent; // User specified default indent
604+
emit->tagLine(indent);
605+
}
593606
int4 id = emit->startComment();
594607
// The comment delimeters should not be printed as
595608
// comment tags, so that they won't get filled

Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.hh

+2
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ private:
248248
vector<NodePending> nodepend; ///< Data-flow nodes waiting to be pushed onto the RPN stack
249249
int4 pending; ///< Number of data-flow nodes waiting to be pushed
250250
int4 line_commentindent; ///< Number of characters a comment line should be indented
251+
bool line_commentindentalign; ///< Whether to align comment lines with code or use a fixed indentation
251252
string commentstart; ///< Delimiter characters for the start of a comment
252253
string commentend; ///< Delimiter characters (if any) for the end of a comment
253254
protected:
@@ -428,6 +429,7 @@ public:
428429
void setMaxLineSize(int4 mls) { emit->setMaxLineSize(mls); } ///< Set the maximum number of characters per line
429430
void setIndentIncrement(int4 inc) { emit->setIndentIncrement(inc); } ///< Set the number of characters to indent per level of code nesting
430431
void setLineCommentIndent(int4 val); ///< Set the number of characters to indent comment lines
432+
void setLineCommentIndentAlign(bool val); ///< Set whether to align comment lines with code lines or not
431433
void setCommentDelimeter(const string &start,const string &stop,
432434
bool usecommentfill); ///< Establish comment delimiters for the language
433435
uint4 getInstructionComment(void) const { return instr_comment_type; } ///< Get the type of comments suitable within the body of a function

Ghidra/Features/Decompiler/src/main/help/help/topics/DecompilePlugin/DecompilerOptions.html

+9
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,15 @@
399399
</p>
400400
</dd>
401401
<dt>
402+
<a name="DisplayCommentIndentAlign"></a><span class="term"><span class="bold"><strong>Align comments with code</strong></span></span>
403+
</dt>
404+
<dd>
405+
<p>
406+
Aligns comment lines with the current indentation level of the decompiler output, instead of using a
407+
fixed amount of spaces. When checked, the comment line indent level option is ignored.
408+
</p>
409+
</dd>
410+
<dt>
402411
<a name="DisplayCommentStyle"></a><span class="term"><span class="bold"><strong>Comment style</strong></span></span>
403412
</dt>
404413
<dd>

Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java

+31
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,13 @@ public String toString() {
179179
private final static int COMMENTINDENT_OPTIONDEFAULT = 20; // Must match PrintLanguage::resetDefaultsInternal
180180
private int commentindent;
181181

182+
private final static String COMMENTINDENTALIGN_OPTIONSTRING = "Display.Comment lines aligned with code";
183+
private final static String COMMENTINDENTALIGN_OPTIONDESCRIPTION =
184+
"Align each comment with the indentation of the code immediately " +
185+
"following it, instead of using the comment line indent level";
186+
private final static boolean COMMENTINDENTALIGN_OPTIONDEFAULT = false; // Must match PrintLanguage::resetDefaultsInternal
187+
private boolean commentindentAlign;
188+
182189
private final static String COMMENTSTYLE_OPTIONSTRING = "Display.Comment style";
183190
private final static String COMMENTSTYLE_OPTIONDESCRIPTION =
184191
"Choice between either the C style comments /* */ or C++ style // ";
@@ -391,6 +398,7 @@ public DecompileOptions() {
391398
maxwidth = MAXWIDTH_OPTIONDEFAULT;
392399
indentwidth = INDENTWIDTH_OPTIONDEFAULT;
393400
commentindent = COMMENTINDENT_OPTIONDEFAULT;
401+
commentindentAlign = COMMENTINDENTALIGN_OPTIONDEFAULT;
394402
commentStyle = COMMENTSTYLE_OPTIONDEFAULT;
395403
commentPREInclude = COMMENTPRE_OPTIONDEFAULT;
396404
commentPLATEInclude = COMMENTPLATE_OPTIONDEFAULT;
@@ -443,6 +451,7 @@ public void grabFromToolAndProgram(Plugin ownerPlugin, ToolOptions opt, Program
443451
maxwidth = opt.getInt(MAXWIDTH_OPTIONSTRING, MAXWIDTH_OPTIONDEFAULT);
444452
indentwidth = opt.getInt(INDENTWIDTH_OPTIONSTRING, INDENTWIDTH_OPTIONDEFAULT);
445453
commentindent = opt.getInt(COMMENTINDENT_OPTIONSTRING, COMMENTINDENT_OPTIONDEFAULT);
454+
commentindentAlign = opt.getBoolean(COMMENTINDENTALIGN_OPTIONSTRING, COMMENTINDENTALIGN_OPTIONDEFAULT);
446455
commentStyle = opt.getEnum(COMMENTSTYLE_OPTIONSTRING, COMMENTSTYLE_OPTIONDEFAULT);
447456
commentEOLInclude = opt.getBoolean(COMMENTEOL_OPTIONSTRING, COMMENTEOL_OPTIONDEFAULT);
448457
commentPREInclude = opt.getBoolean(COMMENTPRE_OPTIONSTRING, COMMENTPRE_OPTIONDEFAULT);
@@ -559,6 +568,9 @@ public void registerOptions(Plugin ownerPlugin, ToolOptions opt, Program program
559568
opt.registerOption(COMMENTINDENT_OPTIONSTRING, COMMENTINDENT_OPTIONDEFAULT,
560569
new HelpLocation(HelpTopics.DECOMPILER, "DisplayCommentIndent"),
561570
COMMENTINDENT_OPTIONDESCRIPTION);
571+
opt.registerOption(COMMENTINDENTALIGN_OPTIONSTRING, COMMENTINDENTALIGN_OPTIONDEFAULT,
572+
new HelpLocation(HelpTopics.DECOMPILER, "DisplayCommentIndentAlign"),
573+
COMMENTINDENTALIGN_OPTIONDESCRIPTION);
562574
opt.registerOption(COMMENTSTYLE_OPTIONSTRING, COMMENTSTYLE_OPTIONDEFAULT,
563575
new HelpLocation(HelpTopics.DECOMPILER, "DisplayCommentStyle"),
564576
COMMENTSTYLE_OPTIONDESCRIPTION);
@@ -723,6 +735,9 @@ public void encode(Encoder encoder, DecompInterface iface) throws IOException {
723735
if (commentindent != COMMENTINDENT_OPTIONDEFAULT) {
724736
appendOption(encoder, ELEM_COMMENTINDENT, Integer.toString(commentindent), "", "");
725737
}
738+
if (commentindentAlign != COMMENTINDENTALIGN_OPTIONDEFAULT) {
739+
appendOption(encoder, ELEM_COMMENTINDENTALIGN, commentindentAlign ? "on" : "off", "", "");
740+
}
726741
if (commentStyle != COMMENTSTYLE_OPTIONDEFAULT) {
727742
String curstyle = CommentStyleEnum.CPPStyle.equals(commentStyle) ? "cplusplus" : "c";
728743
appendOption(encoder, ELEM_COMMENTSTYLE, curstyle, "", "");
@@ -1006,6 +1021,22 @@ public void setMaxInstructions(int num) {
10061021
maxIntructionsPer = num;
10071022
}
10081023

1024+
public int getCommentIndent() {
1025+
return commentindent;
1026+
}
1027+
1028+
public void setCommentIndent(int indent) {
1029+
commentindent = indent;
1030+
}
1031+
1032+
public boolean isCommentIndentAlign() {
1033+
return commentindentAlign;
1034+
}
1035+
1036+
public void setCommentIndentAlign(boolean align) {
1037+
commentindentAlign = align;
1038+
}
1039+
10091040
public CommentStyleEnum getCommentStyle() {
10101041
return commentStyle;
10111042
}

Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/DecompilerTest.java

+76-1
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@
1515
*/
1616
package ghidra.app.plugin.core.decompile;
1717

18+
import java.util.Optional;
19+
1820
import org.junit.*;
1921

2022
import ghidra.app.decompiler.*;
2123
import ghidra.program.model.address.Address;
24+
import ghidra.program.model.address.AddressSpace;
25+
import ghidra.program.model.listing.CodeUnit;
2226
import ghidra.program.model.listing.Function;
2327
import ghidra.program.model.listing.Program;
2428
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
@@ -28,13 +32,14 @@
2832
public class DecompilerTest extends AbstractGhidraHeadedIntegrationTest {
2933
private Program prog;
3034
private DecompInterface decompiler;
35+
private long returnBytesOffset = 0x0;
3136

3237
@Before
3338
public void setUp() throws Exception {
3439

3540
ToyProgramBuilder builder = new ToyProgramBuilder("notepad_decompiler", true);
3641
builder.createMemory("test", "0x0", 2);
37-
builder.addBytesReturn(0x0);
42+
builder.addBytesReturn(returnBytesOffset);
3843
builder.createFunction("0x0");
3944
prog = builder.getProgram();
4045

@@ -58,4 +63,74 @@ public void testDecompileInterfaceReturnsAFunction() throws Exception {
5863
String decompilation = decompResults.getDecompiledFunction().getC();
5964
Assert.assertNotNull(decompilation);
6065
}
66+
67+
@Test
68+
public void testAlignedCommentIndentation() throws Exception {
69+
int indent = 20;
70+
DecompileOptions options = new DecompileOptions();
71+
options.setCommentIndent(indent);
72+
options.setCommentIndentAlign(true);
73+
options.setPRECommentIncluded(true);
74+
decompiler.setOptions(options);
75+
76+
AddressSpace space = prog.getAddressFactory().getDefaultAddressSpace();
77+
78+
// add a comment to the program listing
79+
Address returnBytesAddr = space.getAddress(returnBytesOffset);
80+
int transaction = prog.startTransaction("add comment for indentation test");
81+
String comment = "aligned-comment-indentation-test";
82+
prog.getListing().getCodeUnitAt(returnBytesAddr).setComment(CodeUnit.PRE_COMMENT, comment);
83+
prog.endTransaction(transaction, true);
84+
85+
Address addr = space.getAddress(0x0);
86+
Function func = prog.getListing().getFunctionAt(addr);
87+
DecompileResults decompResults = decompiler.decompileFunction(func,
88+
DecompileOptions.SUGGESTED_DECOMPILE_TIMEOUT_SECS, TaskMonitor.DUMMY);
89+
String decompilation = decompResults.getDecompiledFunction().getC();
90+
Assert.assertNotNull(decompilation);
91+
92+
Optional<String> commentLineCheck = decompilation.lines().filter(line -> line.contains(comment)).findFirst();
93+
Optional<String> returnLineCheck = decompilation.lines().filter(line -> line.endsWith("return;")).findFirst();
94+
Assert.assertTrue(commentLineCheck.isPresent());
95+
Assert.assertTrue(returnLineCheck.isPresent());
96+
97+
String commentLine = commentLineCheck.get();
98+
String returnLine = returnLineCheck.get();
99+
100+
Assert.assertFalse(commentLine.startsWith(" ".repeat(indent)));
101+
102+
int commentIndentation = commentLine.indexOf(commentLine.stripLeading());
103+
int returnIndentation = returnLine.indexOf(returnLine.stripLeading());
104+
Assert.assertEquals(commentIndentation, returnIndentation);
105+
}
106+
107+
@Test
108+
public void testFixedCommentIndentation() throws Exception {
109+
int indent = 20;
110+
DecompileOptions options = new DecompileOptions();
111+
options.setCommentIndent(indent);
112+
options.setCommentIndentAlign(false);
113+
options.setPRECommentIncluded(true);
114+
decompiler.setOptions(options);
115+
116+
AddressSpace space = prog.getAddressFactory().getDefaultAddressSpace();
117+
118+
// add a comment to the program listing
119+
Address returnBytesAddr = space.getAddress(returnBytesOffset);
120+
int transaction = prog.startTransaction("add comment for indentation test");
121+
String comment = "fixed-comment-indentation-test";
122+
prog.getListing().getCodeUnitAt(returnBytesAddr).setComment(CodeUnit.PRE_COMMENT, comment);
123+
prog.endTransaction(transaction, true);
124+
125+
Address addr = space.getAddress(0x0);
126+
Function func = prog.getListing().getFunctionAt(addr);
127+
DecompileResults decompResults = decompiler.decompileFunction(func,
128+
DecompileOptions.SUGGESTED_DECOMPILE_TIMEOUT_SECS, TaskMonitor.DUMMY);
129+
String decompilation = decompResults.getDecompiledFunction().getC();
130+
Assert.assertNotNull(decompilation);
131+
132+
Optional<String> commentLine = decompilation.lines().filter(line -> line.contains(comment)).findFirst();
133+
Assert.assertTrue(commentLine.isPresent());
134+
Assert.assertTrue(commentLine.get().startsWith(" ".repeat(indent)));
135+
}
61136
}

Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ElementId.java

+3
Original file line numberDiff line numberDiff line change
@@ -421,5 +421,8 @@ public record ElementId(String name, int id) {
421421
public static final ElementId ELEM_COMMAND_GETUSEROPNAME =
422422
new ElementId("command_getuseropname", COMMAND_GETUSEROPNAME);
423423

424+
// option to allow comments to align with code
425+
public static final ElementId ELEM_COMMENTINDENTALIGN = new ElementId("commentindentalign", 271);
426+
424427
public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 270);
425428
}

0 commit comments

Comments
 (0)