mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
Support for "else if" syntax
This commit is contained in:
parent
6b04eb793f
commit
79fd837145
7 changed files with 197 additions and 25 deletions
|
@ -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|
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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\) && \(a < 300\)\)</stringmatch>
|
||||
</decompilertest>
|
Loading…
Add table
Add a link
Reference in a new issue