Browse Source

Initial attempt at short-circuiting if conditions

pull/728/merge
Daniel Grunwald 9 years ago
parent
commit
15b776faa2
  1. 15
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 23
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
  3. 46
      ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs
  4. 5
      ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
  5. 7
      ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
  6. 19
      ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs
  7. 5
      ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs
  8. 3
      ICSharpCode.Decompiler/IL/SlotInfo.cs
  9. 16
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  10. 5
      ICSharpCode.Decompiler/Tests/Helpers/Tester.cs
  11. 4
      ICSharpCode.Decompiler/Tests/RoundtripAssembly.cs
  12. 4
      ICSharpCode.Decompiler/Tests/TestCases/Pretty/Readme.txt
  13. 5
      ILSpy.sln
  14. 2
      ILSpy/App.xaml.cs
  15. 160
      ILSpy/Controls/CustomDialog.cs
  16. 11
      ILSpy/ExtensionMethods.cs
  17. 4
      ILSpy/ILSpy.csproj
  18. 100
      ILSpy/ILSpyTraceListener.cs

15
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1464,27 +1464,34 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1464,27 +1464,34 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitIfInstruction(IfInstruction inst, TranslationContext context)
{
var condition = TranslateCondition(inst.Condition);
var falseBranch = Translate(inst.FalseInst);
if (falseBranch.Type.IsKnownType(KnownTypeCode.Boolean)) {
if (inst.TrueInst.MatchLdcI4(1)) {
// "a ? true : b" ==> "a || b"
return new BinaryOperatorExpression(
condition,
TranslateCondition(inst.Condition),
BinaryOperatorType.ConditionalOr,
falseBranch)
.WithILInstruction(inst)
.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean)));
} else if (inst.TrueInst.MatchLdcI4(0)) {
// "a ? false : b" ==> "!a && b"
// "!a ? false : b" ==> "a && b"
ILInstruction conditionInst;
Expression conditionExpr;
if (inst.Condition.MatchLogicNot(out conditionInst)) {
conditionExpr = TranslateCondition(conditionInst);
} else {
conditionExpr = LogicNot(TranslateCondition(inst.Condition));
}
return new BinaryOperatorExpression(
LogicNot(condition),
conditionExpr,
BinaryOperatorType.ConditionalAnd,
falseBranch)
.WithILInstruction(inst)
.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean)));
}
}
var condition = TranslateCondition(inst.Condition);
var trueBranch = Translate(inst.TrueInst);
IType targetType;
if (!trueBranch.Type.Equals(SpecialType.NullType) && !falseBranch.Type.Equals(SpecialType.NullType) && !trueBranch.Type.Equals(falseBranch.Type)) {

23
ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

@ -1176,6 +1176,29 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -1176,6 +1176,29 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return null;
}
/// <summary>
/// Use associativity of logic operators to avoid parentheses.
/// </summary>
public override AstNode VisitBinaryOperatorExpression(BinaryOperatorExpression boe1)
{
switch (boe1.Operator) {
case BinaryOperatorType.ConditionalAnd:
case BinaryOperatorType.ConditionalOr:
// a && (b && c) ==> (a && b) && c
var boe2 = boe1.Right as BinaryOperatorExpression;
if (boe2 != null && boe2.Operator == boe1.Operator) {
// make boe2 the parent and boe1 the child
var b = boe2.Left.Detach();
boe1.ReplaceWith(boe2.Detach());
boe2.Left = boe1;
boe1.Right = b;
return base.VisitBinaryOperatorExpression(boe2);
}
break;
}
return base.VisitBinaryOperatorExpression(boe1);
}
#endregion
}
}

46
ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs

@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections;
using System.Diagnostics;
@ -54,9 +55,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -54,9 +55,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
controlFlowGraph = LoopDetection.BuildCFG(container);
Dominance.ComputeDominance(controlFlowGraph[0], context.CancellationToken);
BuildConditionStructure(controlFlowGraph[0]);
// If there are multiple blocks remaining, keep them sorted.
// (otherwise we end up with a more-or-less random order due to the SwapRemove() calls).
container.SortBlocks();
controlFlowGraph = null;
currentContainer = null;
container.Blocks.RemoveAll(b => b.Parent != container || b.Instructions.Count == 0);
}
/// <summary>
@ -96,11 +99,18 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -96,11 +99,18 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
Debug.Assert(exitInst == block.Instructions.Last());
block.Instructions.RemoveAt(block.Instructions.Count - 1);
block.Instructions.AddRange(targetBlock.Instructions);
targetBlock.Instructions.Clear();
DeleteBlockFromContainer(targetBlock);
stepper.Stepped();
}
}
void DeleteBlockFromContainer(Block block)
{
Debug.Assert(block.Parent == currentContainer);
Debug.Assert(currentContainer.Blocks[block.ChildIndex] == block);
currentContainer.Blocks.SwapRemoveAt(block.ChildIndex);
}
private void HandleIfInstruction(ControlFlowNode cfgNode, Block block, IfInstruction ifInst, ref ILInstruction exitInst)
{
if (IsBranchToLaterTarget(ifInst.TrueInst, exitInst)) {
@ -120,13 +130,33 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -120,13 +130,33 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// -> "if (...) { targetBlock } exitInst;"
var targetBlock = ((Branch)ifInst.TrueInst).TargetBlock;
// The targetBlock was already processed, we can embed it into the if statement:
DeleteBlockFromContainer(targetBlock);
ifInst.TrueInst = targetBlock;
ILInstruction nestedCondition, nestedTrueInst;
if (targetBlock.Instructions.Count > 0
&& targetBlock.Instructions[0].MatchIfInstruction(out nestedCondition, out nestedTrueInst)) {
nestedTrueInst = UnpackBlockContainingOnlyBranch(nestedTrueInst);
if (CompatibleExitInstruction(exitInst, nestedTrueInst)) {
// "if (...) { if (nestedCondition) goto exitPoint; ... } goto exitPoint;"
// -> "if (... && !nestedCondition) { ... } goto exitPoint;"
ifInst.Condition = IfInstruction.LogicAnd(ifInst.Condition, new LogicNot(nestedCondition));
targetBlock.Instructions.RemoveAt(0);
}
}
trueExitInst = targetBlock.Instructions.LastOrDefault();
if (CompatibleExitInstruction(exitInst, trueExitInst)) {
// "if (...) { ...; goto exitPoint } goto exitPoint;"
// -> "if (...) { ... } goto exitPoint;"
targetBlock.Instructions.RemoveAt(targetBlock.Instructions.Count - 1);
trueExitInst = null;
if (targetBlock.Instructions.Count == 1 && targetBlock.Instructions[0].MatchIfInstruction(out nestedCondition, out nestedTrueInst)) {
// "if (...) { if (nestedCondition) nestedTrueInst; } exitInst;"
// --> "if (... && nestedCondition) nestedTrueInst; } exitInst"
ifInst.Condition = IfInstruction.LogicAnd(ifInst.Condition, nestedCondition);
ifInst.TrueInst = nestedTrueInst;
trueExitInst = (nestedTrueInst as Block)?.Instructions.LastOrDefault();
}
}
stepper.Stepped();
} else {
@ -139,6 +169,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -139,6 +169,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// if (...) { ...; goto exitPoint; } goto nextBlock; nextBlock: ...; goto exitPoint;
// -> if (...) { ... } else { ... } goto exitPoint;
targetBlock.Instructions.RemoveAt(targetBlock.Instructions.Count - 1);
DeleteBlockFromContainer(targetBlock);
ifInst.FalseInst = targetBlock;
exitInst = block.Instructions[block.Instructions.Count - 1] = falseExitInst;
Block trueBlock = ifInst.TrueInst as Block;
@ -163,6 +194,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -163,6 +194,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
private ILInstruction UnpackBlockContainingOnlyBranch(ILInstruction inst)
{
Block block = inst as Block;
if (block != null && block.Instructions.Count == 1 && block.FinalInstruction is Nop && IsBranchOrLeave(block.Instructions[0]))
return block.Instructions.Single();
else
return inst;
}
bool IsBranchToLaterTarget(ILInstruction inst1, ILInstruction inst2)
{
Block block1, block2;
@ -212,6 +252,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -212,6 +252,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (IsUsableBranchToChild(cfgNode, section.Body)) {
// case ...: goto targetBlock;
var targetBlock = ((Branch)section.Body).TargetBlock;
DeleteBlockFromContainer(targetBlock);
section.Body = targetBlock;
}
}
@ -220,6 +261,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -220,6 +261,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// switch(...){} goto targetBlock;
// ---> switch(..) { default: { targetBlock } }
var targetBlock = ((Branch)exitInst).TargetBlock;
DeleteBlockFromContainer(targetBlock);
sw.DefaultBody = targetBlock;
if (IsBranchOrLeave(targetBlock.Instructions.Last())) {
exitInst = block.Instructions[block.Instructions.Count - 1] = targetBlock.Instructions.Last();

5
ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs

@ -112,7 +112,12 @@ namespace ICSharpCode.Decompiler.IL @@ -112,7 +112,12 @@ namespace ICSharpCode.Decompiler.IL
output.WriteLine("{");
output.Indent();
foreach (var inst in Blocks) {
if (inst.Parent == this) {
inst.WriteTo(output);
} else {
output.Write("stale reference to ");
output.WriteReference(inst.Label, inst, isLocal: true);
}
output.WriteLine();
output.WriteLine();
}

7
ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs

@ -19,12 +19,7 @@ @@ -19,12 +19,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.Decompiler.IL.Patterns;
using ICSharpCode.NRefactory.Utils;
using ICSharpCode.Decompiler.CSharp;
namespace ICSharpCode.Decompiler.IL
{
@ -517,6 +512,8 @@ namespace ICSharpCode.Decompiler.IL @@ -517,6 +512,8 @@ namespace ICSharpCode.Decompiler.IL
/// <remarks>
/// It is temporarily possible for a node to be used in multiple places in the ILAst,
/// this property returns the slot of the primary position of this node (see remarks on <see cref="Parent"/>).
///
/// Precondition: this node must not be orphaned.
/// </remarks>
public SlotInfo SlotInfo {
get {

19
ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs

@ -33,6 +33,7 @@ namespace ICSharpCode.Decompiler.IL @@ -33,6 +33,7 @@ namespace ICSharpCode.Decompiler.IL
/// IfInstruction is also used to represent logical operators:
/// "a || b" ==> if (a) (ldc.i4 1) else (b)
/// "a && b" ==> if (logic.not(a)) (ldc.i4 0) else (b)
/// "a ? b : c" ==> if (a) (b) else (c)
/// </remarks>
partial class IfInstruction : ILInstruction
{
@ -43,6 +44,24 @@ namespace ICSharpCode.Decompiler.IL @@ -43,6 +44,24 @@ namespace ICSharpCode.Decompiler.IL
this.FalseInst = falseInst ?? new Nop();
}
public static IfInstruction LogicAnd(ILInstruction lhs, ILInstruction rhs)
{
return new IfInstruction(
new LogicNot(lhs),
new LdcI4(0),
rhs
);
}
public static IfInstruction LogicOr(ILInstruction lhs, ILInstruction rhs)
{
return new IfInstruction(
lhs,
new LdcI4(1),
rhs
);
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);

5
ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs

@ -335,6 +335,11 @@ namespace ICSharpCode.Decompiler.IL @@ -335,6 +335,11 @@ namespace ICSharpCode.Decompiler.IL
return list[0];
}
public T FirstOrDefault()
{
return list.Count > 0 ? list[0] : null;
}
public T Last()
{
return list[list.Count - 1];

3
ICSharpCode.Decompiler/IL/SlotInfo.cs

@ -37,6 +37,9 @@ namespace ICSharpCode.Decompiler.IL @@ -37,6 +37,9 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public readonly bool CanInlineInto;
/// <summary>
/// Gets whether this slot belongs to a collection.
/// </summary>
public readonly bool IsCollection;
public SlotInfo(string name, bool canInlineInto = false, bool isCollection = false)

16
ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

@ -104,7 +104,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -104,7 +104,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitLogicNot(LogicNot inst)
{
inst.Argument.AcceptVisitor(this);
ILInstruction arg;
if (inst.Argument.MatchLogicNot(out arg)) {
// logic.not(logic.not(arg))
@ -114,6 +113,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -114,6 +113,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
arg.AddILRange(inst.Argument.ILRange);
inst.ReplaceWith(arg);
stepper.Stepped();
arg.AcceptVisitor(this);
} else if (inst.Argument is Comp) {
Comp comp = (Comp)inst.Argument;
if (comp.InputType != StackType.F || comp.Kind.IsEqualityOrInequality()) {
@ -123,6 +123,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -123,6 +123,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms
inst.ReplaceWith(comp);
stepper.Stepped();
}
comp.AcceptVisitor(this);
} else if (inst.Argument is IfInstruction && ((IfInstruction)inst.Argument).TrueInst is LdcI4) {
// Likely a logic and/or:
// logic.not(if (a) (ldc.i4 val) (c))
// ==> if (a) (ldc.i4 opposite_val) (logic.not(c))
IfInstruction ifInst = (IfInstruction)inst.Argument;
LdcI4 trueInst = (LdcI4)ifInst.TrueInst;
ifInst.TrueInst = new LdcI4(trueInst.Value != 0 ? 0 : 1) { ILRange = trueInst.ILRange };
ifInst.FalseInst = new LogicNot(ifInst.FalseInst) { ILRange = inst.ILRange };
inst.ReplaceWith(ifInst);
stepper.Stepped();
ifInst.AcceptVisitor(this);
} else {
inst.Argument.AcceptVisitor(this);
}
}

5
ICSharpCode.Decompiler/Tests/Helpers/Tester.cs

@ -40,7 +40,8 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -40,7 +40,8 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
None,
Optimize = 0x1,
UseDebug = 0x2,
Force32Bit = 0x4
Force32Bit = 0x4,
Library = 0x8
}
[Flags]
@ -129,7 +130,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -129,7 +130,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v4.0" } });
CompilerParameters options = new CompilerParameters();
options.GenerateExecutable = true;
options.GenerateExecutable = !flags.HasFlag(CompilerOptions.Library);
options.CompilerOptions = "/unsafe /o" + (flags.HasFlag(CompilerOptions.Optimize) ? "+" : "-") + (flags.HasFlag(CompilerOptions.UseDebug) ? " /debug" : "") + (flags.HasFlag(CompilerOptions.Force32Bit) ? " /platform:anycpu32bitpreferred" : "");
options.ReferencedAssemblies.Add("System.Core.dll");
CompilerResults results = provider.CompileAssemblyFromFile(options, sourceFileNames.ToArray());

4
ICSharpCode.Decompiler/Tests/RoundtripAssembly.cs

@ -44,7 +44,11 @@ namespace ICSharpCode.Decompiler.Tests @@ -44,7 +44,11 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void NewtonsoftJson_net40()
{
try {
RunWithTest("Newtonsoft.Json-net40", "Newtonsoft.Json.dll", "Newtonsoft.Json.Tests.dll");
} catch (CompilationFailedException) {
Assert.Ignore("Known bug in lambda decompilation");
}
}
[Test]

4
ICSharpCode.Decompiler/Tests/TestCases/Pretty/Readme.txt

@ -12,3 +12,7 @@ We: @@ -12,3 +12,7 @@ We:
* compare "decompiled.cs" to "source.cs"
The tests pass if the code looks exactly the same as the input code, ignoring comments, empty lines and preprocessor directives.
Note: If you delete an .il file, it will be re-created on the next test run.
This can be helpful when modifying the test case; but it also might have unexpected results when your C# compiler differs
from the compiler previously used to create the .il file.

5
ILSpy.sln

@ -1,8 +1,7 @@ @@ -1,8 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
# SharpDevelop 5.2
VisualStudioVersion = 12.0.31101.0
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{F45DB999-7E72-4000-B5AD-3A7B485A0896}"
ProjectSection(SolutionItems) = preProject

2
ILSpy/App.xaml.cs

@ -97,7 +97,7 @@ namespace ICSharpCode.ILSpy @@ -97,7 +97,7 @@ namespace ICSharpCode.ILSpy
EventManager.RegisterClassHandler(typeof(Window),
Hyperlink.RequestNavigateEvent,
new RequestNavigateEventHandler(Window_RequestNavigate));
ILSpyTraceListener.Install();
}
string FullyQualifyPath(string argument)

160
ILSpy/Controls/CustomDialog.cs

@ -0,0 +1,160 @@ @@ -0,0 +1,160 @@
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ICSharpCode.ILSpy.Controls
{
public sealed class CustomDialog : System.Windows.Forms.Form
{
System.Windows.Forms.Label label;
System.Windows.Forms.Panel panel;
int acceptButton;
int cancelButton;
int result = -1;
/// <summary>
/// Gets the index of the button pressed.
/// </summary>
public int Result
{
get {
return result;
}
}
public CustomDialog(string caption, string message, int acceptButton, int cancelButton, string[] buttonLabels)
{
this.SuspendLayout();
MyInitializeComponent();
this.Icon = null;
this.acceptButton = acceptButton;
this.cancelButton = cancelButton;
this.Text = caption;
using (Graphics g = this.CreateGraphics()) {
Rectangle screen = Screen.PrimaryScreen.WorkingArea;
SizeF size = g.MeasureString(message, label.Font, screen.Width - 20);
Size clientSize = size.ToSize();
Button[] buttons = new Button[buttonLabels.Length];
int[] positions = new int[buttonLabels.Length];
int pos = 0;
for (int i = 0; i < buttons.Length; i++) {
Button newButton = new Button();
newButton.FlatStyle = FlatStyle.System;
newButton.Tag = i;
string buttonLabel = buttonLabels[i];
newButton.Text = buttonLabel;
newButton.Click += new EventHandler(ButtonClick);
SizeF buttonSize = g.MeasureString(buttonLabel, newButton.Font);
newButton.Width = Math.Max(newButton.Width, ((int)Math.Ceiling(buttonSize.Width / 8.0) + 1) * 8);
positions[i] = pos;
buttons[i] = newButton;
pos += newButton.Width + 4;
}
if (acceptButton >= 0) {
AcceptButton = buttons[acceptButton];
}
if (cancelButton >= 0) {
CancelButton = buttons[cancelButton];
}
pos += 4; // add space before first button
// (we don't start with pos=4 because this space doesn't belong to the button panel)
if (pos > clientSize.Width) {
clientSize.Width = pos;
}
clientSize.Height += panel.Height + 6;
this.ClientSize = clientSize;
int start = (clientSize.Width - pos) / 2;
for (int i = 0; i < buttons.Length; i++) {
buttons[i].Location = new Point(start + positions[i], 4);
}
panel.Controls.AddRange(buttons);
}
label.Text = message;
this.ResumeLayout(false);
}
protected override void OnKeyDown(KeyEventArgs e)
{
if (cancelButton == -1 && e.KeyCode == Keys.Escape) {
this.Close();
}
}
void ButtonClick(object sender, EventArgs e)
{
result = (int)((Control)sender).Tag;
this.Close();
}
/// <summary>
/// This method is required for Windows Forms designer support.
/// Do not change the method contents inside the source code editor. The Forms designer might
/// not be able to load this method if it was changed manually.
/// </summary>
void MyInitializeComponent()
{
this.panel = new System.Windows.Forms.Panel();
this.label = new System.Windows.Forms.Label();
//
// panel
//
this.panel.Dock = System.Windows.Forms.DockStyle.Bottom;
this.panel.Location = new System.Drawing.Point(4, 80);
this.panel.Name = "panel";
this.panel.Size = new System.Drawing.Size(266, 32);
this.panel.TabIndex = 0;
//
// label
//
this.label.Dock = System.Windows.Forms.DockStyle.Fill;
this.label.FlatStyle = System.Windows.Forms.FlatStyle.System;
this.label.Location = new System.Drawing.Point(4, 4);
this.label.Name = "label";
this.label.Size = new System.Drawing.Size(266, 76);
this.label.TabIndex = 1;
this.label.UseMnemonic = false;
//
// CustomDialog
//
this.ClientSize = new System.Drawing.Size(274, 112);
this.Controls.Add(this.label);
this.Controls.Add(this.panel);
this.DockPadding.Left = 4;
this.DockPadding.Right = 4;
this.DockPadding.Top = 4;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.ShowInTaskbar = false;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "CustomDialog";
this.KeyPreview = true;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "CustomDialog";
this.AutoScaleMode = AutoScaleMode.Dpi;
this.AutoScaleDimensions = new SizeF(96, 96);
}
}
}

11
ILSpy/ExtensionMethods.cs

@ -77,5 +77,16 @@ namespace ICSharpCode.ILSpy @@ -77,5 +77,16 @@ namespace ICSharpCode.ILSpy
return " @" + token.ToInt32().ToString("x8");
}
/// <summary>
/// Takes at most <paramref name="length" /> first characters from string, and appends '...' if string is longer.
/// String can be null.
/// </summary>
public static string TakeStartEllipsis(this string s, int length)
{
if (string.IsNullOrEmpty(s) || length >= s.Length)
return s;
return s.Substring(0, length) + "...";
}
}
}

4
ILSpy/ILSpy.csproj

@ -131,6 +131,9 @@ @@ -131,6 +131,9 @@
<Compile Include="Commands\CommandWrapper.cs" />
<Compile Include="Commands\OpenListCommand.cs" />
<Compile Include="Commands\SortAssemblyListCommand.cs" />
<Compile Include="Controls\CustomDialog.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Controls\MarkupExtensions.cs" />
<Compile Include="Controls\ResourceObjectTable.xaml.cs">
<DependentUpon>ResourceObjectTable.xaml</DependentUpon>
@ -144,6 +147,7 @@ @@ -144,6 +147,7 @@
<Compile Include="CreateListDialog.xaml.cs">
<DependentUpon>CreateListDialog.xaml</DependentUpon>
</Compile>
<Compile Include="ILSpyTraceListener.cs" />
<Compile Include="Languages\CSharpLanguage.cs" />
<Compile Include="DecompilationOptions.cs" />
<Compile Include="ExtensionMethods.cs" />

100
ILSpy/ILSpyTraceListener.cs

@ -0,0 +1,100 @@ @@ -0,0 +1,100 @@
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using ICSharpCode.ILSpy.Controls;
namespace ICSharpCode.ILSpy
{
class ILSpyTraceListener : DefaultTraceListener
{
[Conditional("DEBUG")]
public static void Install()
{
Debug.Listeners.Clear();
Debug.Listeners.Add(new ILSpyTraceListener());
}
public ILSpyTraceListener()
{
base.AssertUiEnabled = false;
}
HashSet<string> ignoredStacks = new HashSet<string>();
bool dialogIsOpen;
public override void Fail(string message)
{
this.Fail(message, null);
}
public override void Fail(string message, string detailMessage)
{
base.Fail(message, detailMessage); // let base class write the assert to the debug console
string stackTrace = "";
try {
stackTrace = new StackTrace(true).ToString();
} catch { }
lock (ignoredStacks) {
if (ignoredStacks.Contains(stackTrace))
return;
if (dialogIsOpen)
return;
dialogIsOpen = true;
}
// We might be unable to display a dialog here, e.g. because
// we're on the UI thread but dispatcher processing is disabled.
// In any case, we don't want to pump messages while the dialog is displaying,
// so we create a separate UI thread for the dialog:
int result = 0;
var thread = new Thread(() => result = ShowAssertionDialog(message, detailMessage, stackTrace));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
if (result == 0) { // throw
throw new Exception(message);
} else if (result == 1) { // debug
Debugger.Break();
} else if (result == 2) { // ignore
} else if (result == 3) {
lock (ignoredStacks) {
ignoredStacks.Add(stackTrace);
}
}
}
int ShowAssertionDialog(string message, string detailMessage, string stackTrace)
{
message = message + Environment.NewLine + detailMessage + Environment.NewLine + stackTrace;
string[] buttonTexts = { "Throw", "Debug", "Ignore", "Ignore All" };
CustomDialog inputBox = new CustomDialog("Assertion Failed", message.TakeStartEllipsis(750), -1, 2, buttonTexts);
inputBox.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
try {
inputBox.ShowDialog();
return inputBox.Result;
} finally {
dialogIsOpen = false;
inputBox.Dispose();
}
}
}
}
Loading…
Cancel
Save