Even though the killer feature that everyone is waiting for is the decompiler; it's often still is interesting to directly look at the IL code. Of course, this feature can't be missing in a tool called ILSpy.
Implementing it also provided me with a way to test the GUI logic while the decompiler is still under construction.
We use AvalonEdit for the code view, so you can expect some advanced features:
The most obvious one is syntax highlighting, which is using AvalonEdit's built-in highlighting engine. As usual for AvalonEdit, copying the text into any HTML-capable application (such as this blog's edit box in Firefox) will preserve the highlighting.
.method public static hidebysig string CreateHtmlFragment
Code Folding allows you to collapse and expand methods - useful if you're viewing a complete class.
Finally, there's an important invisible feature: every reference is a hyperlink.
Click on a branch target (e.g. IL_0063), and the code view jumps to the target IL instruction. Click on a type/member reference, and it will be selected in the tree view and will be decompiled.
The disassembler itself also can perform a nice trick: it will display the code in an indented form.
For this purpose, I wrote a very simple detector for try-catch-finally and loop structures.
Basically, the exception handling table in the method's footer is converted into a tree, and then displayed inline (ILDasm has this feature as well).
For loops, the detection logic is dead simple: if there's a backwards branch in the code, then that's probably a loop with the body from start to end. To verify whether it's indeed a well-formed loop, I find all branches from outside the loop body jumping into the loop body, and test whether all of those jump to the same instruction. If there are multiple entry points, then the structure is not considered a loop.
As a last test, any loops that cannot be inserted into the tree structure (because the don't nest properly and overlap with loops detected earlier) are ignored as well.
So if you have IL code like this: "ABAB" where A is a loop and B is another (with jump instructions from the end of one A to the start of the other), then this simple algorithm will be wrong and detect only one loop "(ABA)B" and incorrectly consider B to be part of the loop body.
I experimented with some other loop detection algorithms and actually had one which worked pretty well and could handle even the above case. However, the loops detected by that algorithm were not necessarily consecutive code blocks (as with the example above), so they couldn't be displayed in the disassembler view without reording the IL code.
I don't know of any compilers that generate loops in non-consecutive blocks of code; but some obfuscators employ such unusual code patterns. The decompiler will likely used a more advanced form of loop detection in order to deal with those cases.