diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index e74d0d3b7..b38581f86 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -62,6 +62,8 @@ + + diff --git a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs index d7b6ebefb..37203d3e1 100644 --- a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs @@ -54,6 +54,12 @@ namespace ICSharpCode.Decompiler.Tests Run(); } + [Test] + public void Issue1038() + { + Run(); + } + [Test] public void Issue1047() { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1038.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1038.cs new file mode 100644 index 000000000..bc89e56f4 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1038.cs @@ -0,0 +1,10 @@ +using System; + +namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty +{ + public class Issue1038 where TR : class, new() + { + public event Action TestEvent = delegate { + }; + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1038.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1038.il new file mode 100644 index 000000000..2cfc108f0 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1038.il @@ -0,0 +1,125 @@ +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly extern System +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly extern System.Core +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly ConsoleApp11 +{ + .ver 1:0:0:0 +} +.module ConsoleApp11.exe +// MVID: {B973FCD6-A9C4-48A9-8291-26DDC248E208} +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00020003 // ILONLY 32BITPREFERRED +// Image base: 0x000001C4B6C90000 + +.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2 +{ + // Fields + .field private class [System.Core]System.Action`2 TestEvent + .field private static class [System.Core]System.Action`2 '<>f__am$cache0' + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + // Methods + .method public hidebysig specialname rtspecialname instance void .ctor () cil managed + { + .maxstack 8 + + ldarg.0 + ldsfld class [System.Core]System.Action`2 class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::'<>f__am$cache0' + brtrue.s IL_0019 + + ldnull + ldftn void class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::'m__0'(!0, !1) + newobj instance void class [System.Core]System.Action`2::.ctor(object, native int) + stsfld class [System.Core]System.Action`2 class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::'<>f__am$cache0' + + IL_0019: ldsfld class [System.Core]System.Action`2 class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::'<>f__am$cache0' + stfld class [System.Core]System.Action`2 class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::TestEvent + ret + } + + .method public hidebysig specialname instance void add_TestEvent (class [System.Core]System.Action`2 'value') cil managed + { + .maxstack 3 + .locals init ( + [0] class [System.Core]System.Action`2, + [1] class [System.Core]System.Action`2 + ) + + ldarg.0 + ldfld class [System.Core]System.Action`2 class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::TestEvent + stloc.0 + IL_0007: ldloc.0 + stloc.1 + ldarg.0 + ldflda class [System.Core]System.Action`2 class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::TestEvent + ldloc.1 + ldarg.1 + call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) + castclass class [System.Core]System.Action`2 + ldloc.0 + call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange>(!!0&, !!0, !!0) + stloc.0 + ldloc.0 + ldloc.1 + bne.un IL_0007 + ret + } + + .method public hidebysig specialname instance void remove_TestEvent (class [System.Core]System.Action`2 'value') cil managed + { + .maxstack 3 + .locals init ( + [0] class [System.Core]System.Action`2, + [1] class [System.Core]System.Action`2 + ) + + ldarg.0 + ldfld class [System.Core]System.Action`2 class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::TestEvent + stloc.0 + IL_0007: ldloc.0 + stloc.1 + ldarg.0 + ldflda class [System.Core]System.Action`2 class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::TestEvent + ldloc.1 + ldarg.1 + call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) + castclass class [System.Core]System.Action`2 + ldloc.0 + call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange>(!!0&, !!0, !!0) + stloc.0 + ldloc.0 + ldloc.1 + bne.un IL_0007 + ret + } + + .method private hidebysig static void 'm__0' (!TK '', !TR '') cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .maxstack 8 + ret + } + + // Events + .event class [System.Core]System.Action`2 TestEvent + { + .addon instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::add_TestEvent(class [System.Core]System.Action`2) + .removeon instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1038`2::remove_TestEvent(class [System.Core]System.Action`2) + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index baa74e9ed..e8a19c3e7 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -633,7 +633,46 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } } }; - + + static readonly Accessor automaticEventPatternV4MCS = new Accessor { + Attributes = { new Repeat(new AnyNode()) }, + Body = new BlockStatement { + new AssignmentExpression { + Left = new NamedNode("var1", new IdentifierExpression(Pattern.AnyString)), + Operator = AssignmentOperatorType.Assign, + Right = new NamedNode( + "field", + new MemberReferenceExpression { + Target = new Choice { new ThisReferenceExpression(), new TypeReferenceExpression { Type = new AnyNode() } }, + MemberName = Pattern.AnyString + } + ) + }, + new DoWhileStatement { + EmbeddedStatement = new BlockStatement { + new AssignmentExpression(new NamedNode("var2", new IdentifierExpression(Pattern.AnyString)), new IdentifierExpressionBackreference("var1")), + new AssignmentExpression { + Left = new IdentifierExpressionBackreference("var1"), + Right = new InvocationExpression(new MemberReferenceExpression(new TypeReferenceExpression(new TypePattern(typeof(System.Threading.Interlocked)).ToType()), + "CompareExchange", + new AstType[] { new AnyNode("type") }), // type argument+ + new Expression[] { // arguments + new DirectionExpression { FieldDirection = FieldDirection.Ref, Expression = new Backreference("field") }, + new CastExpression(new Backreference("type"), new InvocationExpression(new AnyNode("delegateCombine").ToExpression(), new IdentifierExpressionBackreference("var2"), new IdentifierExpression("value"))), + new IdentifierExpressionBackreference("var1") + } + ) + } + }, + Condition = new BinaryOperatorExpression { + Left = new CastExpression(new TypePattern(typeof(object)), new IdentifierExpressionBackreference("var1")), + Operator = BinaryOperatorType.InEquality, + Right = new IdentifierExpressionBackreference("var2") + }, + } + } + }; + bool CheckAutomaticEventMatch(Match m, CustomEventDeclaration ev, bool isAddAccessor) { if (!m.Success) @@ -678,10 +717,22 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return true; } + bool CheckAutomaticEventV4MCS(CustomEventDeclaration ev, out Match addMatch, out Match removeMatch) + { + addMatch = removeMatch = default(Match); + addMatch = automaticEventPatternV4MCS.Match(ev.AddAccessor); + if (!CheckAutomaticEventMatch(addMatch, ev, true)) + return false; + removeMatch = automaticEventPatternV4MCS.Match(ev.RemoveAccessor); + if (!CheckAutomaticEventMatch(removeMatch, ev, false)) + return false; + return true; + } + EventDeclaration TransformAutomaticEvents(CustomEventDeclaration ev) { Match m1, m2; - if (!CheckAutomaticEventV4(ev, out m1, out m2) && !CheckAutomaticEventV2(ev, out m1, out m2)) + if (!CheckAutomaticEventV4(ev, out m1, out m2) && !CheckAutomaticEventV2(ev, out m1, out m2) && !CheckAutomaticEventV4MCS(ev, out m1, out m2)) return null; RemoveCompilerGeneratedAttribute(ev.AddAccessor.Attributes, attributeTypesToRemoveFromAutoEvents); EventDeclaration ed = new EventDeclaration();