Support for "else if" syntax

This commit is contained in:
caheckman 2021-07-29 14:43:36 -04:00
parent 6b04eb793f
commit 79fd837145
7 changed files with 197 additions and 25 deletions

View file

@ -11,6 +11,7 @@ src/decompile/cpp/Doxyfile||GHIDRA|||Most of this file is autogenerated by doxyg
src/decompile/cpp/Makefile||GHIDRA||||END|
src/decompile/datatests/convert.xml||GHIDRA||||END|
src/decompile/datatests/deadvolatile.xml||GHIDRA||||END|
src/decompile/datatests/elseif.xml||GHIDRA||||END|
src/decompile/datatests/floatprint.xml||GHIDRA||||END|
src/decompile/datatests/forloop1.xml||GHIDRA||||END|
src/decompile/datatests/forloop_loaditer.xml||GHIDRA||||END|

View file

@ -71,6 +71,7 @@ void EmitXml::endBlock(int4 id) {
/// Tell the emitter that a new line is desired at the current indent level
void EmitXml::tagLine(void) {
emitPending();
*s << "<break " << highlight[(int4)no_color] << " indent=\"0x" << hex <<
indentlevel << "\"/>";
}
@ -79,6 +80,7 @@ void EmitXml::tagLine(void) {
/// is overridden only for the line, then it returns to its previous value.
/// \param indent is the desired indent level for the new line
void EmitXml::tagLine(int4 indent) {
emitPending();
*s << "<break " << highlight[(int4)no_color] << " indent=\"0x" << hex <<
indent << "\"/>";
}
@ -923,6 +925,7 @@ void EmitPrettyPrint::endBlock(int4 id)
void EmitPrettyPrint::tagLine(void)
{
emitPending();
checkbreak();
TokenSplit &tok( tokqueue.push() );
tok.tagLine();
@ -932,6 +935,7 @@ void EmitPrettyPrint::tagLine(void)
void EmitPrettyPrint::tagLine(int4 indent)
{
emitPending();
checkbreak();
TokenSplit &tok( tokqueue.push() );
tok.tagLine(indent);

View file

@ -26,6 +26,7 @@ class PcodeOp;
class FlowBlock;
class Funcdata;
class Symbol;
class PendPrint;
/// \brief Base class (and interface) for pretty printing and XML markup of tokens
///
@ -80,9 +81,11 @@ protected:
int4 indentlevel; ///< Current indent level (in fixed width characters)
int4 parenlevel; ///< Current depth of parentheses
int4 indentincrement; ///< Change in indentlevel per level of nesting
PendPrint *pendPrint; ///< Pending print callback
void resetDefaultsInternal(void) { indentincrement = 2; } ///< Set options to default values for EmitXml
void emitPending(void); ///< Emit any pending print commands
public:
EmitXml(void) { s = (ostream *)0; indentlevel=0; parenlevel=0; resetDefaultsInternal(); } ///< Constructor
EmitXml(void) { s = (ostream *)0; indentlevel=0; parenlevel=0; pendPrint=(PendPrint *)0; resetDefaultsInternal(); } ///< Constructor
/// \brief Possible types of syntax highlighting
enum syntax_highlight {
@ -136,7 +139,7 @@ public:
/// Inform the emitter that a printing group is ending.
/// \param id is the id associated with the group (as returned by openGroup)
virtual void closeGroup(int4 id) {}
virtual void clear(void) { parenlevel = 0; indentlevel=0; } ///< Reset the emitter to its initial state
virtual void clear(void) { parenlevel = 0; indentlevel=0; pendPrint=(PendPrint *)0; } ///< Reset the emitter to its initial state
virtual void setOutputStream(ostream *t) { s = t; } ///< Set the output stream for the emitter
virtual ostream *getOutputStream(void) const { return s; } ///< Get the current output stream
virtual void spaces(int4 num,int4 bump=0);
@ -214,6 +217,24 @@ public:
///
/// \param val is the desired number of characters to indent
void setIndentIncrement(int4 val) { indentincrement = val; }
/// \brief Set a pending print callback
///
/// The callback will be issued prior to the the next call to tagLine() unless
/// a the method cancelPendingPrint() is called first.
/// \param pend is the callback to be issued
void setPendingPrint(PendPrint *pend) { pendPrint = pend; }
/// \brief Cancel any pending print callback
///
/// If there is any print callback pending, cancel it
void cancelPendingPrint(void) { pendPrint = (PendPrint *)0; }
/// \brief Check if the given print callback is still pending
///
/// \param pend is the given print callback to check
/// \return \b true if the specific print callback is pending
bool hasPendingPrint(PendPrint *pend) const { return (pendPrint == pend); }
};
/// \brief A trivial emitter that outputs syntax straight to the stream
@ -779,4 +800,25 @@ public:
void setXML(bool val); ///< Toggle whether the low-level emitter emits XML markup or not
};
/// \brief Helper class for sending cancelable print commands to an ExitXml
///
/// The PendPrint is issued as a placeholder for commands to the emitter using its
/// setPendingPrint() method. The callback() method is overridden to tailor the exact
/// sequence of print commands. The print commands will be executed prior to the next
/// tagLine() call to the emitter, unless the PendPrint is cancelled.
class PendPrint {
public:
virtual ~PendPrint(void) {} ///< Destructor
virtual void callback(EmitXml *emit)=0; ///< Callback that executes the actual print commands
};
inline void EmitXml::emitPending(void)
{
if (pendPrint != (PendPrint *)0) {
pendPrint->callback(this);
pendPrint = (PendPrint *)0;
}
}
#endif

View file

@ -2596,17 +2596,28 @@ void PrintC::emitBlockCondition(const BlockCondition *bl)
}
}
void PendingBrace::callback(EmitXml *emit)
{
emit->print("{");
indentId = emit->startIndent();
}
void PrintC::emitBlockIf(const BlockIf *bl)
{
const PcodeOp *op;
PendingBrace pendingBrace;
if (isSet(pending_brace))
emit->setPendingPrint(&pendingBrace);
// if block never prints final branch
// so no_branch and only_branch don't matter
// and shouldn't be passed automatically to
// the subblocks
pushMod();
unsetMod(no_branch|only_branch);
unsetMod(no_branch|only_branch|pending_brace);
pushMod();
setMod(no_branch);
@ -2614,7 +2625,11 @@ void PrintC::emitBlockIf(const BlockIf *bl)
condBlock->emit(this);
popMod();
emitCommentBlockTree(condBlock);
emit->tagLine();
if (emit->hasPendingPrint(&pendingBrace)) // If we issued a brace but it did not emit
emit->cancelPendingPrint(); // Cancel the brace in order to have "else if" syntax
else
emit->tagLine(); // Otherwise start the "if" on a new line
op = condBlock->lastOp();
emit->tagOp("if",EmitXml::keyword_color,op);
emit->spaces(1);
@ -2625,10 +2640,8 @@ void PrintC::emitBlockIf(const BlockIf *bl)
if (bl->getGotoTarget() != (FlowBlock *)0) {
emit->spaces(1);
emitGotoStatement(condBlock,bl->getGotoTarget(),bl->getGotoType());
popMod();
return;
}
else {
setMod(no_branch);
emit->spaces(1);
int4 id = emit->startIndent();
@ -2639,20 +2652,36 @@ void PrintC::emitBlockIf(const BlockIf *bl)
emit->stopIndent(id);
emit->tagLine();
emit->print("}");
if (bl->getSize()==3) {
if (bl->getSize() == 3) {
emit->tagLine();
emit->print("else",EmitXml::keyword_color);
emit->spaces(1);
FlowBlock *elseBlock = bl->getBlock(2);
if (elseBlock->getType() == FlowBlock::t_if) {
// Attempt to merge the "else" and "if" syntax
setMod(pending_brace);
int4 id2 = emit->beginBlock(elseBlock);
elseBlock->emit(this);
emit->endBlock(id2);
}
else {
int4 id = emit->startIndent();
emit->print("{");
int4 id2 = emit->beginBlock(bl->getBlock(2));
bl->getBlock(2)->emit(this);
int4 id2 = emit->beginBlock(elseBlock);
elseBlock->emit(this);
emit->endBlock(id2);
emit->stopIndent(id);
emit->tagLine();
emit->print("}");
}
}
}
popMod();
if (pendingBrace.getIndentId() >= 0) {
emit->stopIndent(pendingBrace.getIndentId());
emit->tagLine();
emit->print("}");
}
}
/// Print the loop using the keyword \e for, followed by a semicolon separated

View file

@ -301,4 +301,16 @@ public:
virtual void opPopcountOp(const PcodeOp *op) { opFunc(op); }
};
/// \brief Set of print commands for displaying an open brace '{' and setting a new indent level
///
/// These are the print commands sent to the emitter prior to printing and \e else block.
/// The open brace can be canceled if the block decides it wants to use "else if" syntax.
class PendingBrace : public PendPrint {
int4 indentId; ///< Id associated with the new indent level
public:
PendingBrace(void) { indentId = -1; } ///< Constructor
int4 getIndentId(void) const { return indentId; } ///< If commands have been issued, returns the new indent level id.
virtual void callback(EmitXml *emit);
};
#endif

View file

@ -149,7 +149,8 @@ public:
falsebranch = 0x800, ///< Print the false branch (for flat)
nofallthru = 0x1000, ///< Fall-thru no longer exists
negatetoken = 0x2000, ///< Print the token representing the negation of current token
hide_thisparam = 0x4000 ///< Do not print the 'this' parameter in argument lists
hide_thisparam = 0x4000, ///< Do not print the 'this' parameter in argument lists
pending_brace = 0x8000 ///< The current block may need to surround itself with additional braces
};
/// \brief Possible types of Atom
enum tagtype {
@ -282,7 +283,6 @@ protected:
int4 getPending(void) const { return pending; } ///< Get the number of pending nodes yet to be put on the RPN stack
void resetDefaultsInternal(void); ///< Reset options to default for PrintLanguage
/// \brief Print a single unicode character as a \e character \e constant for the high-level language
///
/// For most languages, this prints the character surrounded by single quotes.

View file

@ -0,0 +1,84 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
Contrived example testing various forms of nested if/else structures,
where the some should collapse to "else if" syntax and others should not.
-->
<bytechunk space="ram" offset="0x1006ca" readonly="true">
554889e54883
ec10897dfc8975f8837dfc0a75188b45
f889c6488d3d3a030000b800000000e8
9cfeffffeb17837dfc017511488d3d2a
030000b800000000e883feffff837df8
01751b8b45fc89c6488d3d05030000b8
00000000e867feffffe9bb000000837d
f80b751e8b45fc83c06489c6488d3de1
020000b800000000e843feffffe99700
0000837df815751b8b45fc6bc06489c6
488d3dbd020000b800000000e81ffeff
ffeb76837df81f751d8b55fc8b45f801
d089c6488d3d9a020000b800000000e8
fcfdffffeb53837df8f9751b8b45fc2b
45f889c6488d3d79020000b800000000
e8dbfdffffeb32837df800751b8b45fc
83e80189c6488d3d58020000b8000000
00e8bafdffffeb11488d3d53020000b8
00000000e8a7fdffff8b45fc3b45f875
1b8b45fc83e80289c6488d3d24020000
b800000000e886fdffffeb28837dfc25
7511488d3d1f020000b800000000e86d
fdffff488d3d18020000b800000000e8
5cfdffff8b45fc3b45f87d1b8b45fc83
e80389c6488d3dd9010000b800000000
e83bfdffffeb1e8b55fc8b45f801d083
f80a7511488d3ddd010000b800000000
e81bfdffff817df8c8000000751b8b45
fc83c06789c6488d3d97010000b80000
0000e8f9fcffffeb23817df8c9000000
751a817dfc2b0100007f11488d3d7b01
0000b800000000e8d4fcffff837df81b
751b8b45fc83c01b89c6488d3d530100
00b800000000e8b5fcffffeb5d837df8
1d7557eb2e488d3d64010000e88ffcff
ff837dfc64751b8b45f883c06f89c648
8d3d4e010000b800000000e880fcffff
eb1890837dfc677511488d3d3c010000
b800000000e866fcffff488d3d310100
00e84afcffff8345fc018b45fc3b45f8
7ca390c9c3
</bytechunk>
<bytechunk space="ram" offset="0x100a24" readonly="true">
426f64792025640a00456c73650046
696e616c004e6f7420717569746500
457874726100436f6d6d656e740054
6f7000426f6479202564004c616265
6c00426f74746f6d00
</bytechunk>
</binaryimage>
<script>
<com>map fun r0x1006ca testElseIf</com>
<com>map fun r0x100590 printf</com>
<com>parse line extern void testElseIf(int4 a,int4 b);</com>
<com>parse line extern void printf(char *,...);</com>
<com>map label mylabel r0x100913</com>
<com>lo fu testElseIf</com>
<com>comment instr r0x10085a Comment</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Else-if #1" min="1" max="1">else if \(a == 1\)</stringmatch>
<stringmatch name="Else-if #2" min="1" max="1">else if \(b == 0xb\)</stringmatch>
<stringmatch name="Else-if #3" min="1" max="1">else if \(b == 0x15\)</stringmatch>
<stringmatch name="Else-if #4" min="1" max="1">else if \(b == 0x1f\)</stringmatch>
<stringmatch name="Else-if #5" min="1" max="1">else if \(b == -7\)</stringmatch>
<stringmatch name="Else-if #6" min="1" max="1">else if \(b == 0\)</stringmatch>
<stringmatch name="Else-if #7" min="0" max="0">else if \(a == 0x25\)</stringmatch>
<stringmatch name="Else-if #8" min="1" max="1">if \(a == 0x25\)</stringmatch>
<stringmatch name="Else-if #9" min="0" max="0">else if \(b \+ a == 10\)</stringmatch>
<stringmatch name="Else-if #10" min="1" max="1">if \(b \+ a == 10\)</stringmatch>
<stringmatch name="Else-if #11" min="1" max="1">else if \(b == 0x1d\) goto</stringmatch>
<stringmatch name="Else-if #12" min="0" max="0">else if \(.* == 0x67\)</stringmatch>
<stringmatch name="Else-if #13" min="1" max="1">if \(.* == 0x67\)</stringmatch>
<stringmatch name="Else-if #14" min="1" max="1">else if \(\(b == 0xc9\) &amp;&amp; \(a &lt; 300\)\)</stringmatch>
</decompilertest>