Browse Source

Implement C# 2, C# 4 legacy and Roslyn lock patterns.

pull/863/head
Siegfried Pammer 8 years ago
parent
commit
4da1addd2b
  1. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  3. 19
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Lock.cs
  4. 146
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Lock.il
  5. 122
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Lock.opt.il
  6. 125
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Lock.opt.roslyn.il
  7. 138
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Lock.roslyn.il
  8. 1
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  9. 10
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  10. 103
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
  11. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  12. 120
      ICSharpCode.Decompiler/IL/Instructions.cs
  13. 5
      ICSharpCode.Decompiler/IL/Instructions.tt
  14. 32
      ICSharpCode.Decompiler/IL/Instructions/LockInstruction.cs
  15. 225
      ICSharpCode.Decompiler/IL/Transforms/LockTransform.cs

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -84,6 +84,7 @@ @@ -84,6 +84,7 @@
<Compile Include="TestCases\Correctness\ValueTypeCall.cs" />
<Compile Include="CorrectnessTestRunner.cs" />
<Compile Include="TestCases\Pretty\AutoProperties.cs" />
<Compile Include="TestCases\Pretty\Lock.cs" />
<Compile Include="TestCases\Pretty\Loops.cs" />
<Compile Include="TestCases\Correctness\YieldReturn.cs" />
<Compile Include="TestCases\Pretty\CompoundAssignmentTest.cs" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -109,6 +109,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -109,6 +109,12 @@ namespace ICSharpCode.Decompiler.Tests
Run(cscOptions: cscOptions);
}
[Test]
public void Lock([ValueSource("defaultOptions")] CompilerOptions cscOptions)
{
Run(cscOptions: cscOptions);
}
[Test, Ignore("Not implemented")]
public void LiftedOperators([ValueSource("defaultOptions")] CompilerOptions cscOptions)
{

19
ICSharpCode.Decompiler.Tests/Lock.cs → ICSharpCode.Decompiler.Tests/TestCases/Pretty/Lock.cs

@ -18,21 +18,22 @@ @@ -18,21 +18,22 @@
using System;
public class Lock
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public void LockThis()
public class Lock
{
lock (this)
public void LockThis()
{
Console.WriteLine();
lock (this) {
Console.WriteLine();
}
}
}
public void LockOnType()
{
lock (typeof(Lock))
public void LockOnType()
{
Console.WriteLine();
lock (typeof(Lock)) {
Console.WriteLine();
}
}
}
}

146
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Lock.il

@ -0,0 +1,146 @@ @@ -0,0 +1,146 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
// Copyright (c) Microsoft Corporation. All rights reserved.
// 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 '1c2baaro'
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
.permissionset reqmin
= {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}}
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module '1c2baaro.dll'
// MVID: {5FC5B58D-AAD7-4436-A58F-6F3D75486C66}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x00C60000
// =============== CLASS MEMBERS DECLARATION ===================
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Lock
extends [mscorlib]System.Object
{
.method public hidebysig instance void
LockThis() cil managed
{
// Code size 42 (0x2a)
.maxstack 2
.locals init (bool V_0,
class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Lock V_1,
bool V_2)
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
.try
{
IL_0003: ldarg.0
IL_0004: dup
IL_0005: stloc.1
IL_0006: ldloca.s V_0
IL_0008: call void [mscorlib]System.Threading.Monitor::Enter(object,
bool&)
IL_000d: nop
IL_000e: nop
IL_000f: call void [mscorlib]System.Console::WriteLine()
IL_0014: nop
IL_0015: nop
IL_0016: leave.s IL_0028
} // end .try
finally
{
IL_0018: ldloc.0
IL_0019: ldc.i4.0
IL_001a: ceq
IL_001c: stloc.2
IL_001d: ldloc.2
IL_001e: brtrue.s IL_0027
IL_0020: ldloc.1
IL_0021: call void [mscorlib]System.Threading.Monitor::Exit(object)
IL_0026: nop
IL_0027: endfinally
} // end handler
IL_0028: nop
IL_0029: ret
} // end of method Lock::LockThis
.method public hidebysig instance void
LockOnType() cil managed
{
// Code size 51 (0x33)
.maxstack 2
.locals init (bool V_0,
class [mscorlib]System.Type V_1,
bool V_2)
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
.try
{
IL_0003: ldtoken ICSharpCode.Decompiler.Tests.TestCases.Pretty.Lock
IL_0008: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_000d: dup
IL_000e: stloc.1
IL_000f: ldloca.s V_0
IL_0011: call void [mscorlib]System.Threading.Monitor::Enter(object,
bool&)
IL_0016: nop
IL_0017: nop
IL_0018: call void [mscorlib]System.Console::WriteLine()
IL_001d: nop
IL_001e: nop
IL_001f: leave.s IL_0031
} // end .try
finally
{
IL_0021: ldloc.0
IL_0022: ldc.i4.0
IL_0023: ceq
IL_0025: stloc.2
IL_0026: ldloc.2
IL_0027: brtrue.s IL_0030
IL_0029: ldloc.1
IL_002a: call void [mscorlib]System.Threading.Monitor::Exit(object)
IL_002f: nop
IL_0030: endfinally
} // end handler
IL_0031: nop
IL_0032: ret
} // end of method Lock::LockOnType
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Lock::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Lock
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************
// WARNING: Created Win32 resource file ../../../TestCases/Pretty\Lock.res

122
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Lock.opt.il

@ -0,0 +1,122 @@ @@ -0,0 +1,122 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
// Copyright (c) Microsoft Corporation. All rights reserved.
// 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 titpuxma
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
.permissionset reqmin
= {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}}
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module titpuxma.dll
// MVID: {8B90EB29-A139-4376-848E-C5D4D42952F1}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x02AF0000
// =============== CLASS MEMBERS DECLARATION ===================
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Lock
extends [mscorlib]System.Object
{
.method public hidebysig instance void
LockThis() cil managed
{
// Code size 30 (0x1e)
.maxstack 2
.locals init (bool V_0,
class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Lock V_1)
IL_0000: ldc.i4.0
IL_0001: stloc.0
.try
{
IL_0002: ldarg.0
IL_0003: dup
IL_0004: stloc.1
IL_0005: ldloca.s V_0
IL_0007: call void [mscorlib]System.Threading.Monitor::Enter(object,
bool&)
IL_000c: call void [mscorlib]System.Console::WriteLine()
IL_0011: leave.s IL_001d
} // end .try
finally
{
IL_0013: ldloc.0
IL_0014: brfalse.s IL_001c
IL_0016: ldloc.1
IL_0017: call void [mscorlib]System.Threading.Monitor::Exit(object)
IL_001c: endfinally
} // end handler
IL_001d: ret
} // end of method Lock::LockThis
.method public hidebysig instance void
LockOnType() cil managed
{
// Code size 39 (0x27)
.maxstack 2
.locals init (bool V_0,
class [mscorlib]System.Type V_1)
IL_0000: ldc.i4.0
IL_0001: stloc.0
.try
{
IL_0002: ldtoken ICSharpCode.Decompiler.Tests.TestCases.Pretty.Lock
IL_0007: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_000c: dup
IL_000d: stloc.1
IL_000e: ldloca.s V_0
IL_0010: call void [mscorlib]System.Threading.Monitor::Enter(object,
bool&)
IL_0015: call void [mscorlib]System.Console::WriteLine()
IL_001a: leave.s IL_0026
} // end .try
finally
{
IL_001c: ldloc.0
IL_001d: brfalse.s IL_0025
IL_001f: ldloc.1
IL_0020: call void [mscorlib]System.Threading.Monitor::Exit(object)
IL_0025: endfinally
} // end handler
IL_0026: ret
} // end of method Lock::LockOnType
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Lock::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Lock
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************
// WARNING: Created Win32 resource file ../../../TestCases/Pretty\Lock.opt.res

125
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Lock.opt.roslyn.il

@ -0,0 +1,125 @@ @@ -0,0 +1,125 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
// Copyright (c) Microsoft Corporation. All rights reserved.
// 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 Lock
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
// --- The following custom attribute is added automatically, do not uncomment -------
// .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 )
.permissionset reqmin
= {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}}
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module Lock.dll
// MVID: {5161B18E-7152-4B49-B4A8-67524BD74953}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x00E10000
// =============== CLASS MEMBERS DECLARATION ===================
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Lock
extends [mscorlib]System.Object
{
.method public hidebysig instance void
LockThis() cil managed
{
// Code size 30 (0x1e)
.maxstack 2
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Lock V_0,
bool V_1)
IL_0000: ldarg.0
IL_0001: stloc.0
IL_0002: ldc.i4.0
IL_0003: stloc.1
.try
{
IL_0004: ldloc.0
IL_0005: ldloca.s V_1
IL_0007: call void [mscorlib]System.Threading.Monitor::Enter(object,
bool&)
IL_000c: call void [mscorlib]System.Console::WriteLine()
IL_0011: leave.s IL_001d
} // end .try
finally
{
IL_0013: ldloc.1
IL_0014: brfalse.s IL_001c
IL_0016: ldloc.0
IL_0017: call void [mscorlib]System.Threading.Monitor::Exit(object)
IL_001c: endfinally
} // end handler
IL_001d: ret
} // end of method Lock::LockThis
.method public hidebysig instance void
LockOnType() cil managed
{
// Code size 39 (0x27)
.maxstack 2
.locals init (class [mscorlib]System.Type V_0,
bool V_1)
IL_0000: ldtoken ICSharpCode.Decompiler.Tests.TestCases.Pretty.Lock
IL_0005: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_000a: stloc.0
IL_000b: ldc.i4.0
IL_000c: stloc.1
.try
{
IL_000d: ldloc.0
IL_000e: ldloca.s V_1
IL_0010: call void [mscorlib]System.Threading.Monitor::Enter(object,
bool&)
IL_0015: call void [mscorlib]System.Console::WriteLine()
IL_001a: leave.s IL_0026
} // end .try
finally
{
IL_001c: ldloc.1
IL_001d: brfalse.s IL_0025
IL_001f: ldloc.0
IL_0020: call void [mscorlib]System.Threading.Monitor::Exit(object)
IL_0025: endfinally
} // end handler
IL_0026: ret
} // end of method Lock::LockOnType
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Lock::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Lock
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************

138
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Lock.roslyn.il

@ -0,0 +1,138 @@ @@ -0,0 +1,138 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
// Copyright (c) Microsoft Corporation. All rights reserved.
// 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 Lock
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
// --- The following custom attribute is added automatically, do not uncomment -------
// .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 )
.permissionset reqmin
= {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}}
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module Lock.dll
// MVID: {3273D43A-61AF-4B6E-B6CF-5B3C9A7E807A}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x029B0000
// =============== CLASS MEMBERS DECLARATION ===================
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Lock
extends [mscorlib]System.Object
{
.method public hidebysig instance void
LockThis() cil managed
{
// Code size 36 (0x24)
.maxstack 2
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Lock V_0,
bool V_1)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.0
IL_0003: ldc.i4.0
IL_0004: stloc.1
.try
{
IL_0005: ldloc.0
IL_0006: ldloca.s V_1
IL_0008: call void [mscorlib]System.Threading.Monitor::Enter(object,
bool&)
IL_000d: nop
IL_000e: nop
IL_000f: call void [mscorlib]System.Console::WriteLine()
IL_0014: nop
IL_0015: nop
IL_0016: leave.s IL_0023
} // end .try
finally
{
IL_0018: ldloc.1
IL_0019: brfalse.s IL_0022
IL_001b: ldloc.0
IL_001c: call void [mscorlib]System.Threading.Monitor::Exit(object)
IL_0021: nop
IL_0022: endfinally
} // end handler
IL_0023: ret
} // end of method Lock::LockThis
.method public hidebysig instance void
LockOnType() cil managed
{
// Code size 45 (0x2d)
.maxstack 2
.locals init (class [mscorlib]System.Type V_0,
bool V_1)
IL_0000: nop
IL_0001: ldtoken ICSharpCode.Decompiler.Tests.TestCases.Pretty.Lock
IL_0006: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_000b: stloc.0
IL_000c: ldc.i4.0
IL_000d: stloc.1
.try
{
IL_000e: ldloc.0
IL_000f: ldloca.s V_1
IL_0011: call void [mscorlib]System.Threading.Monitor::Enter(object,
bool&)
IL_0016: nop
IL_0017: nop
IL_0018: call void [mscorlib]System.Console::WriteLine()
IL_001d: nop
IL_001e: nop
IL_001f: leave.s IL_002c
} // end .try
finally
{
IL_0021: ldloc.1
IL_0022: brfalse.s IL_002b
IL_0024: ldloc.0
IL_0025: call void [mscorlib]System.Threading.Monitor::Exit(object)
IL_002a: nop
IL_002b: endfinally
} // end handler
IL_002c: ret
} // end of method Lock::LockOnType
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method Lock::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Lock
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************

1
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -101,6 +101,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -101,6 +101,7 @@ namespace ICSharpCode.Decompiler.CSharp
PostOrderTransforms = {
//new UseExitPoints(),
new ConditionDetection(),
new LockTransform(),
// CachedDelegateInitialization must run after ConditionDetection and before/in LoopingBlockTransform
// and must run before NullCoalescingTransform
new CachedDelegateInitialization(),

10
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -245,7 +245,15 @@ namespace ICSharpCode.Decompiler.CSharp @@ -245,7 +245,15 @@ namespace ICSharpCode.Decompiler.CSharp
tryCatch.CatchClauses.Add(new CatchClause { Body = faultBlock });
return tryCatch;
}
protected internal override Statement VisitLockInstruction(LockInstruction inst)
{
return new LockStatement {
Expression = exprBuilder.Translate(inst.OnExpression),
EmbeddedStatement = ConvertAsBlock(inst.Body)
};
}
protected internal override Statement VisitPinnedRegion(PinnedRegion inst)
{
var fixedStmt = new FixedStatement();

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

@ -85,11 +85,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -85,11 +85,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
result = TransformFor(expressionStatement);
if (result != null)
return result;
if (context.Settings.LockStatement) {
result = TransformLock(expressionStatement);
if (result != null)
return result;
}
if (context.Settings.AutomaticProperties) {
result = ReplaceBackingFieldUsage(expressionStatement);
if (result != null)
@ -677,104 +672,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -677,104 +672,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
#endregion
#region lock
static readonly AstNode lockFlagInitPattern = new ExpressionStatement(
new AssignmentExpression(
new NamedNode("variable", new IdentifierExpression(Pattern.AnyString)),
new PrimitiveExpression(false)
));
static readonly AstNode lockTryCatchPattern = new TryCatchStatement {
TryBlock = new BlockStatement {
new OptionalNode(new VariableDeclarationStatement()).ToStatement(),
new InvocationExpression(new MemberReferenceExpression(new TypeReferenceExpression(new TypePattern(typeof(System.Threading.Monitor)).ToType()), "Enter"),
new AnyNode("enter"),
new DirectionExpression {
FieldDirection = FieldDirection.Ref,
Expression = new NamedNode("flag", new IdentifierExpression(Pattern.AnyString))
}),
new Repeat(new AnyNode()).ToStatement()
},
FinallyBlock = new BlockStatement {
new IfElseStatement {
Condition = new Backreference("flag"),
TrueStatement = new BlockStatement {
new InvocationExpression(new MemberReferenceExpression(new TypeReferenceExpression(new TypePattern(typeof(System.Threading.Monitor)).ToType()), "Exit"), new AnyNode("exit"))
}
}
}};
static readonly AstNode oldMonitorCallPattern = new ExpressionStatement(
new InvocationExpression(new MemberReferenceExpression(new TypeReferenceExpression(new TypePattern(typeof(System.Threading.Monitor)).ToType()), "Enter"), new AnyNode("enter"))
);
static readonly AstNode oldLockTryCatchPattern = new TryCatchStatement
{
TryBlock = new BlockStatement {
new Repeat(new AnyNode()).ToStatement()
},
FinallyBlock = new BlockStatement {
new InvocationExpression(new MemberReferenceExpression(new TypeReferenceExpression(new TypePattern(typeof(System.Threading.Monitor)).ToType()), "Exit"), new AnyNode("exit"))
}
};
bool AnalyzeLockV2(ExpressionStatement node, out Expression enter, out Expression exit)
{
enter = null;
exit = null;
Match m1 = oldMonitorCallPattern.Match(node);
if (!m1.Success) return false;
Match m2 = oldLockTryCatchPattern.Match(node.NextSibling);
if (!m2.Success) return false;
enter = m1.Get<Expression>("enter").Single();
exit = m2.Get<Expression>("exit").Single();
return true;
}
bool AnalyzeLockV4(ExpressionStatement node, out Expression enter, out Expression exit)
{
enter = null;
exit = null;
Match m1 = lockFlagInitPattern.Match(node);
if (!m1.Success) return false;
Match m2 = lockTryCatchPattern.Match(node.NextSibling);
if (!m2.Success) return false;
enter = m2.Get<Expression>("enter").Single();
exit = m2.Get<Expression>("exit").Single();
return m1.Get<IdentifierExpression>("variable").Single().Identifier == m2.Get<IdentifierExpression>("flag").Single().Identifier;
}
public LockStatement TransformLock(ExpressionStatement node)
{
Expression enter, exit;
bool isV2 = AnalyzeLockV2(node, out enter, out exit);
if (isV2 || AnalyzeLockV4(node, out enter, out exit)) {
AstNode tryCatch = node.NextSibling;
if (!exit.IsMatch(enter)) {
// If exit and enter are not the same, then enter must be "exit = ..."
AssignmentExpression assign = enter as AssignmentExpression;
if (assign == null)
return null;
if (!exit.IsMatch(assign.Left))
return null;
enter = assign.Right;
// TODO: verify that 'obj' variable can be removed
}
// TODO: verify that 'flag' variable can be removed
// transform the code into a lock statement:
LockStatement l = new LockStatement();
l.Expression = enter.Detach();
l.EmbeddedStatement = ((TryCatchStatement)tryCatch).TryBlock.Detach();
if (!isV2) // Remove 'Enter()' call
((BlockStatement)l.EmbeddedStatement).Statements.First().Remove();
tryCatch.ReplaceWith(l);
node.Remove(); // remove flag variable
return l;
}
return null;
}
#endregion
#region switch on strings
static readonly IfElseStatement switchOnStringPattern = new IfElseStatement {
Condition = new BinaryOperatorExpression {

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -273,12 +273,14 @@ @@ -273,12 +273,14 @@
<Compile Include="IL\ControlFlow\SymbolicExecution.cs" />
<Compile Include="IL\Instructions\Await.cs" />
<Compile Include="IL\Instructions\ILVariableCollection.cs" />
<Compile Include="IL\Instructions\LockInstruction.cs" />
<Compile Include="IL\Instructions\NullCoalescingInstruction.cs" />
<Compile Include="IL\Patterns\AnyNode.cs" />
<Compile Include="IL\ControlFlow\YieldReturnDecompiler.cs" />
<Compile Include="IL\DetectedLoop.cs" />
<Compile Include="IL\Transforms\AssignVariableNames.cs" />
<Compile Include="IL\Transforms\DetectCatchWhenConditionBlocks.cs" />
<Compile Include="IL\Transforms\LockTransform.cs" />
<Compile Include="IL\Transforms\NullableLiftingTransform.cs" />
<Compile Include="IL\Transforms\NullCoalescingTransform.cs" />
<Compile Include="IL\Transforms\TransformCollectionAndObjectInitializers.cs" />

120
ICSharpCode.Decompiler/IL/Instructions.cs

@ -73,6 +73,8 @@ namespace ICSharpCode.Decompiler.IL @@ -73,6 +73,8 @@ namespace ICSharpCode.Decompiler.IL
TryFinally,
/// <summary>Try-fault statement</summary>
TryFault,
/// <summary>Lock statement</summary>
LockInstruction,
/// <summary>Breakpoint instruction</summary>
DebugBreak,
/// <summary>Comparison. The inputs must be both integers; or both floats; or both object references. Object references can only be compared for equality or inequality. Floating-point comparisons evaluate to 0 (false) when an input is NaN, except for 'NaN != NaN' which evaluates to 1 (true).</summary>
@ -1621,6 +1623,99 @@ namespace ICSharpCode.Decompiler.IL @@ -1621,6 +1623,99 @@ namespace ICSharpCode.Decompiler.IL
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Lock statement</summary>
public sealed partial class LockInstruction : ILInstruction
{
public LockInstruction(ILInstruction onExpression, ILInstruction body) : base(OpCode.LockInstruction)
{
this.OnExpression = onExpression;
this.Body = body;
}
public static readonly SlotInfo OnExpressionSlot = new SlotInfo("OnExpression", canInlineInto: true);
ILInstruction onExpression;
public ILInstruction OnExpression {
get { return this.onExpression; }
set {
ValidateChild(value);
SetChildInstruction(ref this.onExpression, value, 0);
}
}
public static readonly SlotInfo BodySlot = new SlotInfo("Body");
ILInstruction body;
public ILInstruction Body {
get { return this.body; }
set {
ValidateChild(value);
SetChildInstruction(ref this.body, value, 1);
}
}
protected sealed override int GetChildCount()
{
return 2;
}
protected sealed override ILInstruction GetChild(int index)
{
switch (index) {
case 0:
return this.onExpression;
case 1:
return this.body;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override void SetChild(int index, ILInstruction value)
{
switch (index) {
case 0:
this.OnExpression = value;
break;
case 1:
this.Body = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return OnExpressionSlot;
case 1:
return BodySlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone()
{
var clone = (LockInstruction)ShallowClone();
clone.OnExpression = this.onExpression.Clone();
clone.Body = this.body.Clone();
return clone;
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitLockInstruction(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitLockInstruction(this);
}
public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{
return visitor.VisitLockInstruction(this, context);
}
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as LockInstruction;
return o != null && this.onExpression.PerformMatch(o.onExpression, ref match) && this.body.PerformMatch(o.body, ref match);
}
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Breakpoint instruction</summary>
public sealed partial class DebugBreak : SimpleInstruction
@ -4188,6 +4283,10 @@ namespace ICSharpCode.Decompiler.IL @@ -4188,6 +4283,10 @@ namespace ICSharpCode.Decompiler.IL
{
Default(inst);
}
protected internal virtual void VisitLockInstruction(LockInstruction inst)
{
Default(inst);
}
protected internal virtual void VisitDebugBreak(DebugBreak inst)
{
Default(inst);
@ -4462,6 +4561,10 @@ namespace ICSharpCode.Decompiler.IL @@ -4462,6 +4561,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
protected internal virtual T VisitLockInstruction(LockInstruction inst)
{
return Default(inst);
}
protected internal virtual T VisitDebugBreak(DebugBreak inst)
{
return Default(inst);
@ -4736,6 +4839,10 @@ namespace ICSharpCode.Decompiler.IL @@ -4736,6 +4839,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst, context);
}
protected internal virtual T VisitLockInstruction(LockInstruction inst, C context)
{
return Default(inst, context);
}
protected internal virtual T VisitDebugBreak(DebugBreak inst, C context)
{
return Default(inst, context);
@ -4939,6 +5046,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4939,6 +5046,7 @@ namespace ICSharpCode.Decompiler.IL
"try.catch.handler",
"try.finally",
"try.fault",
"lock",
"debug.break",
"comp",
"call",
@ -5069,6 +5177,18 @@ namespace ICSharpCode.Decompiler.IL @@ -5069,6 +5177,18 @@ namespace ICSharpCode.Decompiler.IL
variable = default(ILVariable);
return false;
}
public bool MatchLockInstruction(out ILInstruction onExpression, out ILInstruction body)
{
var inst = this as LockInstruction;
if (inst != null) {
onExpression = inst.OnExpression;
body = inst.Body;
return true;
}
onExpression = default(ILInstruction);
body = default(ILInstruction);
return false;
}
public bool MatchDebugBreak()
{
var inst = this as DebugBreak;

5
ICSharpCode.Decompiler/IL/Instructions.tt

@ -116,6 +116,11 @@ @@ -116,6 +116,11 @@
BaseClass("TryInstruction"), CustomConstructor, CustomWriteTo, CustomComputeFlags,
MatchCondition("TryBlock.PerformMatch(o.TryBlock, ref match)"),
MatchCondition("faultBlock.PerformMatch(o.faultBlock, ref match)")),
new OpCode("lock", "Lock statement", CustomClassName("LockInstruction"),
CustomChildren(new [] {
new ArgumentInfo("onExpression"),
new ChildInfo("body")
}), CustomWriteTo, CustomComputeFlags),
new OpCode("debug.break", "Breakpoint instruction",
NoArguments, VoidResult, SideEffect),
new OpCode("comp", "Comparison. The inputs must be both integers; or both floats; or both object references. "

32
ICSharpCode.Decompiler/IL/Instructions/LockInstruction.cs

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ICSharpCode.Decompiler.IL
{
partial class LockInstruction
{
protected override InstructionFlags ComputeFlags()
{
return Body.Flags | OnExpression.Flags | InstructionFlags.ControlFlow | InstructionFlags.SideEffect;
}
public override InstructionFlags DirectFlags => InstructionFlags.ControlFlow | InstructionFlags.SideEffect;
public override StackType ResultType => StackType.Void;
public override void WriteTo(ITextOutput output)
{
output.Write(".lock (");
OnExpression.WriteTo(output);
output.WriteLine(") {");
output.Indent();
Body.WriteTo(output);
output.Unindent();
output.WriteLine();
output.Write("}");
}
}
}

225
ICSharpCode.Decompiler/IL/Transforms/LockTransform.cs

@ -0,0 +1,225 @@ @@ -0,0 +1,225 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
class LockTransform : IBlockTransform
{
BlockTransformContext context;
void IBlockTransform.Run(Block block, BlockTransformContext context)
{
if (!context.Settings.LockStatement) return;
this.context = context;
for (int i = block.Instructions.Count - 1; i >= 0; i--) {
if (!TransformLockRoslyn(block, i))
if (!TransformLockV4(block, i))
TransformLockV2(block, i);
}
}
/// <summary>
/// stloc lockObj(ldloc tempVar)
/// call Enter(ldloc tempVar)
/// .try BlockContainer {
/// Block lockBlock(incoming: 1) {
/// call WriteLine()
/// leave lockBlock (nop)
/// }
/// } finally BlockContainer {
/// Block exitBlock(incoming: 1) {
/// call Exit(ldloc lockObj)
/// leave exitBlock (nop)
/// }
/// }
/// .lock (lockObj) BlockContainer {
/// Block lockBlock (incoming: 1) {
/// call WriteLine()
/// leave lockBlock (nop)
/// }
/// }
/// </summary>
bool TransformLockV2(Block block, int i)
{
if (i < 2) return false;
if (!(block.Instructions[i] is TryFinally body) || !(block.Instructions[i - 2] is StLoc objectStore) ||
!objectStore.Value.MatchLdLoc(out var tempVar) || !MatchCall(block.Instructions[i - 1] as Call, "Enter", tempVar))
return false;
if (!objectStore.Variable.IsSingleDefinition)
return false;
if (!(body.TryBlock is BlockContainer tryContainer) || tryContainer.EntryPoint.Instructions.Count == 0 || tryContainer.EntryPoint.IncomingEdgeCount != 1)
return false;
if (!(body.FinallyBlock is BlockContainer finallyContainer) || !MatchExitBlock(finallyContainer.EntryPoint, null, objectStore.Variable))
return false;
context.Step("LockTransformV2", block);
block.Instructions.RemoveAt(i - 1);
block.Instructions.RemoveAt(i - 2);
body.ReplaceWith(new LockInstruction(objectStore.Value, body.TryBlock));
return true;
}
/// <summary>
/// stloc flag(ldc.i4 0)
/// .try BlockContainer {
/// Block lockBlock (incoming: 1) {
/// call Enter(stloc obj(lockObj), ldloca flag)
/// call WriteLine()
/// leave lockBlock (nop)
/// }
/// } finally BlockContainer {
/// Block (incoming: 1) {
/// if (ldloc flag) Block {
/// call Exit(ldloc obj)
/// }
/// leave lockBlock (nop)
/// }
/// }
/// =>
/// .lock (lockObj) BlockContainer {
/// Block lockBlock (incoming: 1) {
/// call WriteLine()
/// leave lockBlock (nop)
/// }
/// }
/// </summary>
bool TransformLockV4(Block block, int i)
{
if (i < 1) return false;
if (!(block.Instructions[i] is TryFinally body) || !(block.Instructions[i - 1] is StLoc flagStore))
return false;
if (!flagStore.Variable.Type.IsKnownType(KnownTypeCode.Boolean) || !flagStore.Value.MatchLdcI4(0))
return false;
if (!(body.TryBlock is BlockContainer tryContainer) || !MatchLockEntryPoint(tryContainer.EntryPoint, flagStore.Variable, out StLoc objectStore))
return false;
if (!(body.FinallyBlock is BlockContainer finallyContainer) || !MatchExitBlock(finallyContainer.EntryPoint, flagStore.Variable, objectStore.Variable))
return false;
context.Step("LockTransformV4", block);
block.Instructions.RemoveAt(i - 1);
tryContainer.EntryPoint.Instructions.RemoveAt(0);
body.ReplaceWith(new LockInstruction(objectStore.Value, body.TryBlock));
return true;
}
/// <summary>
/// stloc obj(lockObj)
/// stloc flag(ldc.i4 0)
/// .try BlockContainer {
/// Block lockBlock (incoming: 1) {
/// call Enter(ldloc obj, ldloca flag)
/// call WriteLine()
/// leave lockBlock (nop)
/// }
/// } finally BlockContainer {
/// Block (incoming: 1) {
/// if (ldloc flag) Block {
/// call Exit(ldloc obj)
/// }
/// leave lockBlock (nop)
/// }
/// }
/// =>
/// .lock (lockObj) BlockContainer {
/// Block lockBlock (incoming: 1) {
/// call WriteLine()
/// leave lockBlock (nop)
/// }
/// }
/// </summary>
bool TransformLockRoslyn(Block block, int i)
{
if (i < 2) return false;
if (!(block.Instructions[i] is TryFinally body) || !(block.Instructions[i - 1] is StLoc flagStore) || !(block.Instructions[i - 2] is StLoc objectStore))
return false;
if (!objectStore.Variable.IsSingleDefinition || !flagStore.Variable.Type.IsKnownType(KnownTypeCode.Boolean) || !flagStore.Value.MatchLdcI4(0))
return false;
if (!(body.TryBlock is BlockContainer tryContainer) || !MatchLockEntryPoint(tryContainer.EntryPoint, flagStore.Variable, objectStore.Variable))
return false;
if (!(body.FinallyBlock is BlockContainer finallyContainer) || !MatchExitBlock(finallyContainer.EntryPoint, flagStore.Variable, objectStore.Variable))
return false;
context.Step("LockTransformRoslyn", block);
block.Instructions.RemoveAt(i - 1);
block.Instructions.RemoveAt(i - 2);
tryContainer.EntryPoint.Instructions.RemoveAt(0);
body.ReplaceWith(new LockInstruction(objectStore.Value, body.TryBlock));
return true;
}
bool MatchExitBlock(Block entryPoint, ILVariable flag, ILVariable obj)
{
if (entryPoint.Instructions.Count != 2 || entryPoint.IncomingEdgeCount != 1)
return false;
if (flag != null) {
if (!entryPoint.Instructions[0].MatchIfInstruction(out var cond, out var trueInst) || !(trueInst is Block trueBlock))
return false;
if (!(cond.MatchLdLoc(flag) || (cond.MatchCompNotEquals(out var left, out var right) && left.MatchLdLoc(flag) && right.MatchLdcI4(0))) || !MatchExitBlock(trueBlock, obj))
return false;
} else {
if (!MatchCall(entryPoint.Instructions[0] as Call, "Exit", obj))
return false;
}
if (!entryPoint.Instructions[1].MatchLeave((BlockContainer)entryPoint.Parent, out var retVal) || !retVal.MatchNop())
return false;
return true;
}
bool MatchExitBlock(Block exitBlock, ILVariable obj)
{
if (exitBlock.Instructions.Count != 1)
return false;
if (!MatchCall(exitBlock.Instructions[0] as Call, "Exit", obj))
return false;
return true;
}
bool MatchLockEntryPoint(Block entryPoint, ILVariable flag, ILVariable obj)
{
if (entryPoint.Instructions.Count == 0 || entryPoint.IncomingEdgeCount != 1)
return false;
if (!MatchCall(entryPoint.Instructions[0] as Call, "Enter", obj, flag))
return false;
return true;
}
bool MatchLockEntryPoint(Block entryPoint, ILVariable flag, out StLoc obj)
{
obj = null;
if (entryPoint.Instructions.Count == 0 || entryPoint.IncomingEdgeCount != 1)
return false;
if (!MatchCall(entryPoint.Instructions[0] as Call, "Enter", flag, out obj))
return false;
return true;
}
bool MatchCall(Call call, string methodName, ILVariable flag, out StLoc obj)
{
obj = null;
const string ThreadingMonitor = "System.Threading.Monitor";
if (call == null || call.Method.Name != methodName || call.Method.DeclaringType.FullName != ThreadingMonitor ||
call.Method.TypeArguments.Count != 0 || call.Arguments.Count != 2)
return false;
if (!call.Arguments[1].MatchLdLoca(flag) || !(call.Arguments[0] is StLoc val))
return false;
obj = val;
return true;
}
bool MatchCall(Call call, string methodName, params ILVariable[] variables)
{
const string ThreadingMonitor = "System.Threading.Monitor";
if (call == null || call.Method.Name != methodName || call.Method.DeclaringType.FullName != ThreadingMonitor ||
call.Method.TypeArguments.Count != 0 || call.Arguments.Count != variables.Length)
return false;
if (!call.Arguments[0].MatchLdLoc(variables[0]))
return false;
if (variables.Length == 2) {
if (!call.Arguments[1].MatchLdLoca(variables[1]))
return false;
}
return true;
}
}
}
Loading…
Cancel
Save