Browse Source

Add support for syntax sugar when writing out the ILAst.

pull/870/head
Daniel Grunwald 8 years ago
parent
commit
53a050552b
  1. 3
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LiftedOperators.cs
  2. 4
      ICSharpCode.Decompiler/IL/Instructions.cs
  3. 15
      ICSharpCode.Decompiler/IL/Instructions.tt
  4. 6
      ICSharpCode.Decompiler/IL/Instructions/Comp.cs
  5. 9
      ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
  6. 18
      ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs
  7. 49
      ICSharpCode.Decompiler/IL/Instructions/MemoryInstructions.cs
  8. 21
      ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
  9. 26
      ILSpy/Languages/ILAstLanguage.cs

3
ICSharpCode.Decompiler.Tests/TestCases/Pretty/LiftedOperators.cs

@ -134,8 +134,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
a |= x(); a |= x();
a ^= x(); a ^= x();
Console.WriteLine(x() & a);
Console.WriteLine(x() | a);
Console.WriteLine(x() ^ a); Console.WriteLine(x() ^ a);
(new bool?[0])[0] ^= x(); (new bool?[0])[0] ^= x();
(new bool?[0])[0] ^= a;
} }
public static void BoolValueConst(bool? a) public static void BoolValueConst(bool? a)

4
ICSharpCode.Decompiler/IL/Instructions.cs

@ -2946,7 +2946,7 @@ namespace ICSharpCode.Decompiler.IL
return InstructionFlags.SideEffect | InstructionFlags.MayThrow; return InstructionFlags.SideEffect | InstructionFlags.MayThrow;
} }
} }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) void OriginalWriteTo(ITextOutput output, ILAstWritingOptions options)
{ {
if (IsVolatile) if (IsVolatile)
output.Write("volatile."); output.Write("volatile.");
@ -3070,7 +3070,7 @@ namespace ICSharpCode.Decompiler.IL
return InstructionFlags.SideEffect | InstructionFlags.MayThrow; return InstructionFlags.SideEffect | InstructionFlags.MayThrow;
} }
} }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) void OriginalWriteTo(ITextOutput output, ILAstWritingOptions options)
{ {
if (IsVolatile) if (IsVolatile)
output.Write("volatile."); output.Write("volatile.");

15
ICSharpCode.Decompiler/IL/Instructions.tt

@ -172,16 +172,16 @@
CustomClassName("LdFlda"), CustomArguments("target"), MayThrowIfNotDelayed, HasFieldOperand, ResultType("Ref")), CustomClassName("LdFlda"), CustomArguments("target"), MayThrowIfNotDelayed, HasFieldOperand, ResultType("Ref")),
new OpCode("ldsflda", "Load static field address", new OpCode("ldsflda", "Load static field address",
CustomClassName("LdsFlda"), NoArguments, ResultType("Ref"), HasFieldOperand), CustomClassName("LdsFlda"), NoArguments, ResultType("Ref"), HasFieldOperand),
new OpCode("castclass", "Casts an object to a class.", new OpCode("castclass", "Casts an object to a class.",
CustomClassName("CastClass"), Unary, HasTypeOperand, MayThrow, ResultType("type.GetStackType()")), CustomClassName("CastClass"), Unary, HasTypeOperand, MayThrow, ResultType("type.GetStackType()")),
new OpCode("isinst", "Test if object is instance of class or interface.", new OpCode("isinst", "Test if object is instance of class or interface.",
CustomClassName("IsInst"), Unary, HasTypeOperand, ResultType("O")), CustomClassName("IsInst"), Unary, HasTypeOperand, ResultType("O")),
new OpCode("ldobj", "Indirect load (ref/pointer dereference).", new OpCode("ldobj", "Indirect load (ref/pointer dereference).",
CustomClassName("LdObj"), CustomArguments("target"), HasTypeOperand, MemoryAccess, CustomClassName("LdObj"), CustomArguments("target"), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal,
SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")), SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")),
new OpCode("stobj", "Indirect store (store to ref/pointer).", new OpCode("stobj", "Indirect store (store to ref/pointer).",
CustomClassName("StObj"), CustomArguments("target", "value"), HasTypeOperand, MemoryAccess, CustomClassName("StObj"), CustomArguments("target", "value"), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal,
SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")), SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")),
new OpCode("box", "Boxes a value.", new OpCode("box", "Boxes a value.",
@ -277,7 +277,11 @@ namespace <#=opCode.Namespace#>
} }
<# } #> <# } #>
<# if (opCode.GenerateWriteTo) { #> <# if (opCode.GenerateWriteTo) { #>
<# if (opCode.CustomWriteToButKeepOriginal) { #>
void OriginalWriteTo(ITextOutput output, ILAstWritingOptions options)
<# } else { #>
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
<# } #>
{<#=Body(opCode.WriteToBody)#>} {<#=Body(opCode.WriteToBody)#>}
<# } #> <# } #>
<# if (opCode.GenerateAcceptVisitor) { #> <# if (opCode.GenerateAcceptVisitor) { #>
@ -462,6 +466,7 @@ namespace ICSharpCode.Decompiler.IL
public bool GenerateWriteTo = false; public bool GenerateWriteTo = false;
public bool CustomWriteToButKeepOriginal = false;
public List<string> WriteOpCodePrefix = new List<string>(); public List<string> WriteOpCodePrefix = new List<string>();
public List<string> WriteOpCodeSuffix = new List<string>(); public List<string> WriteOpCodeSuffix = new List<string>();
public List<string> WriteOperand = new List<string>(); public List<string> WriteOperand = new List<string>();
@ -512,6 +517,10 @@ namespace ICSharpCode.Decompiler.IL
static Action<OpCode> CustomWriteTo = opCode => { static Action<OpCode> CustomWriteTo = opCode => {
opCode.GenerateWriteTo = false; opCode.GenerateWriteTo = false;
}; };
static Action<OpCode> CustomWriteToButKeepOriginal = opCode => {
opCode.CustomWriteToButKeepOriginal = true;
};
static Action<OpCode> CustomComputeFlags = opCode => { static Action<OpCode> CustomComputeFlags = opCode => {
opCode.GenerateComputeFlags = false; opCode.GenerateComputeFlags = false;

6
ICSharpCode.Decompiler/IL/Instructions/Comp.cs

@ -175,6 +175,12 @@ namespace ICSharpCode.Decompiler.IL
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{ {
if (options.UseLogicOperationSugar && MatchLogicNot(out var arg)) {
output.Write("logic.not(");
arg.WriteTo(output, options);
output.Write(')');
return;
}
output.Write(OpCode); output.Write(OpCode);
switch (Sign) { switch (Sign) {
case Sign.Signed: case Sign.Signed:

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

@ -743,5 +743,14 @@ namespace ICSharpCode.Decompiler.IL
public class ILAstWritingOptions public class ILAstWritingOptions
{ {
/// <summary>
/// Sugar for logic.not/and/or.
/// </summary>
public bool UseLogicOperationSugar { get; set; }
/// <summary>
/// Sugar for ldfld/stfld.
/// </summary>
public bool UseFieldSugar { get; set; }
} }
} }

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

@ -78,6 +78,24 @@ namespace ICSharpCode.Decompiler.IL
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{ {
if (options.UseLogicOperationSugar) {
if (MatchLogicAnd(out var lhs, out var rhs)) {
output.Write("logic.and(");
lhs.WriteTo(output, options);
output.Write(", ");
rhs.WriteTo(output, options);
output.Write(')');
return;
}
if (MatchLogicOr(out lhs, out rhs)) {
output.Write("logic.or(");
lhs.WriteTo(output, options);
output.Write(", ");
rhs.WriteTo(output, options);
output.Write(')');
return;
}
}
output.Write(OpCode); output.Write(OpCode);
output.Write(" ("); output.Write(" (");
condition.WriteTo(output, options); condition.WriteTo(output, options);

49
ICSharpCode.Decompiler/IL/Instructions/MemoryInstructions.cs

@ -34,4 +34,53 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
bool IsVolatile { get; set; } bool IsVolatile { get; set; }
} }
partial class LdObj
{
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
if (options.UseFieldSugar) {
if (this.MatchLdFld(out var target, out var field)) {
output.Write("ldfld ");
Disassembler.DisassemblerHelpers.WriteOperand(output, field);
output.Write('(');
this.target.WriteTo(output, options);
output.Write(')');
return;
} else if (this.MatchLdsFld(out field)) {
output.Write("ldsfld ");
Disassembler.DisassemblerHelpers.WriteOperand(output, field);
return;
}
}
OriginalWriteTo(output, options);
}
}
partial class StObj
{
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
if (options.UseFieldSugar) {
if (this.MatchStFld(out var target, out var field, out var value)) {
output.Write("stfld ");
Disassembler.DisassemblerHelpers.WriteOperand(output, field);
output.Write('(');
this.target.WriteTo(output, options);
output.Write(", ");
this.value.WriteTo(output, options);
output.Write(')');
return;
} else if (this.MatchStsFld(out field, out value)) {
output.Write("stsfld ");
Disassembler.DisassemblerHelpers.WriteOperand(output, field);
output.Write('(');
this.value.WriteTo(output, options);
output.Write(')');
return;
}
}
OriginalWriteTo(output, options);
}
}
} }

21
ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs

@ -324,17 +324,9 @@ namespace ICSharpCode.Decompiler.IL
} }
} }
public bool MatchLdsFld(IField field)
{
if (this is LdObj ldobj && ldobj.Target is LdsFlda ldsflda) {
return field.Equals(ldsflda.Field);
}
return false;
}
public bool MatchLdFld(out ILInstruction target, out IField field) public bool MatchLdFld(out ILInstruction target, out IField field)
{ {
if (this is LdObj ldobj && ldobj.Target is LdFlda ldflda) { if (this is LdObj ldobj && ldobj.Target is LdFlda ldflda && ldobj.UnalignedPrefix == 0 && !ldobj.IsVolatile) {
target = ldflda.Target; target = ldflda.Target;
field = ldflda.Field; field = ldflda.Field;
return true; return true;
@ -346,7 +338,7 @@ namespace ICSharpCode.Decompiler.IL
public bool MatchLdsFld(out IField field) public bool MatchLdsFld(out IField field)
{ {
if (this is LdObj ldobj && ldobj.Target is LdsFlda ldsflda) { if (this is LdObj ldobj && ldobj.Target is LdsFlda ldsflda && ldobj.UnalignedPrefix == 0 && !ldobj.IsVolatile) {
field = ldsflda.Field; field = ldsflda.Field;
return true; return true;
} }
@ -354,9 +346,14 @@ namespace ICSharpCode.Decompiler.IL
return false; return false;
} }
public bool MatchLdsFld(IField field)
{
return MatchLdsFld(out var f) && f.Equals(field);
}
public bool MatchStsFld(out IField field, out ILInstruction value) public bool MatchStsFld(out IField field, out ILInstruction value)
{ {
if (this is StObj stobj && stobj.Target is LdsFlda ldsflda) { if (this is StObj stobj && stobj.Target is LdsFlda ldsflda && stobj.UnalignedPrefix == 0 && !stobj.IsVolatile) {
field = ldsflda.Field; field = ldsflda.Field;
value = stobj.Value; value = stobj.Value;
return true; return true;
@ -368,7 +365,7 @@ namespace ICSharpCode.Decompiler.IL
public bool MatchStFld(out ILInstruction target, out IField field, out ILInstruction value) public bool MatchStFld(out ILInstruction target, out IField field, out ILInstruction value)
{ {
if (this is StObj stobj && stobj.Target is LdFlda ldflda) { if (this is StObj stobj && stobj.Target is LdFlda ldflda && stobj.UnalignedPrefix == 0 && !stobj.IsVolatile) {
target = ldflda.Target; target = ldflda.Target;
field = ldflda.Field; field = ldflda.Field;
value = stobj.Value; value = stobj.Value;

26
ILSpy/Languages/ILAstLanguage.cs

@ -141,12 +141,16 @@ namespace ICSharpCode.ILSpy
class BlockIL : ILAstLanguage class BlockIL : ILAstLanguage
{ {
readonly IReadOnlyList<IILTransform> transforms; readonly IReadOnlyList<IILTransform> transforms;
readonly ILAstWritingOptions writingOptions = new ILAstWritingOptions {
UseFieldSugar = true,
UseLogicOperationSugar = true
};
public BlockIL(IReadOnlyList<IILTransform> transforms) : base("ILAst") public BlockIL(IReadOnlyList<IILTransform> transforms) : base("ILAst")
{ {
this.transforms = transforms; this.transforms = transforms;
} }
public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options)
{ {
base.DecompileMethod(method, output, options); base.DecompileMethod(method, output, options);
@ -173,11 +177,27 @@ namespace ICSharpCode.ILSpy
OnStepperUpdated(new EventArgs()); OnStepperUpdated(new EventArgs());
} }
} }
(output as ISmartTextOutput)?.AddUIElement(OptionsCheckBox(nameof(writingOptions.UseFieldSugar)));
output.WriteLine();
(output as ISmartTextOutput)?.AddUIElement(OptionsCheckBox(nameof(writingOptions.UseLogicOperationSugar)));
output.WriteLine();
(output as ISmartTextOutput)?.AddButton(Images.ViewCode, "Show Steps", delegate { (output as ISmartTextOutput)?.AddButton(Images.ViewCode, "Show Steps", delegate {
DebugSteps.Show(); DebugSteps.Show();
}); });
output.WriteLine(); output.WriteLine();
il.WriteTo(output); il.WriteTo(output, writingOptions);
}
Func<System.Windows.UIElement> OptionsCheckBox(string propertyName)
{
return () => {
var checkBox = new System.Windows.Controls.CheckBox();
checkBox.Content = propertyName;
checkBox.Cursor = System.Windows.Input.Cursors.Arrow;
checkBox.SetBinding(System.Windows.Controls.CheckBox.IsCheckedProperty,
new System.Windows.Data.Binding(propertyName) { Source = writingOptions });
return checkBox;
};
} }
} }
} }

Loading…
Cancel
Save