Browse Source

fix #560: properly recognize `lock` pattern of C# 2, and add `lock (this)` pattern

pull/569/head
Siegfried Pammer 10 years ago
parent
commit
9f161006a5
  1. 58
      ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  2. 11
      ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs
  3. 1
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  4. 38
      ICSharpCode.Decompiler/Tests/Lock.cs
  5. 17
      ICSharpCode.Decompiler/Tests/TestRunner.cs

58
ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs

@ -617,6 +617,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -617,6 +617,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
static readonly AstNode lockTryCatchPattern = new TryCatchStatement {
TryBlock = new BlockStatement {
new OptionalNode(new VariableDeclarationStatement()).ToStatement(),
new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke(
"Enter", new AnyNode("enter"),
new DirectionExpression {
@ -629,21 +630,57 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -629,21 +630,57 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
new IfElseStatement {
Condition = new Backreference("flag"),
TrueStatement = new BlockStatement {
new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("Exit", new NamedNode("exit", new IdentifierExpression(Pattern.AnyString)))
new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("Exit", new AnyNode("exit"))
}
}
}};
static readonly AstNode oldMonitorCallPattern = new ExpressionStatement(
new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("Enter", new AnyNode("enter"))
);
static readonly AstNode oldLockTryCatchPattern = new TryCatchStatement
{
TryBlock = new BlockStatement {
new Repeat(new AnyNode()).ToStatement()
},
FinallyBlock = new BlockStatement {
new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("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)
{
Match m1 = lockFlagInitPattern.Match(node);
if (!m1.Success) return null;
AstNode tryCatch = node.NextSibling;
Match m2 = lockTryCatchPattern.Match(tryCatch);
if (!m2.Success) return null;
if (m1.Get<IdentifierExpression>("variable").Single().Identifier == m2.Get<IdentifierExpression>("flag").Single().Identifier) {
Expression enter = m2.Get<Expression>("enter").Single();
IdentifierExpression exit = m2.Get<IdentifierExpression>("exit").Single();
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;
@ -659,7 +696,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -659,7 +696,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
LockStatement l = new LockStatement();
l.Expression = enter.Detach();
l.EmbeddedStatement = ((TryCatchStatement)tryCatch).TryBlock.Detach();
((BlockStatement)l.EmbeddedStatement).Statements.First().Remove(); // Remove 'Enter()' call
if (!isV2) // Remove 'Enter()' call
((BlockStatement)l.EmbeddedStatement).Statements.First().Remove();
tryCatch.ReplaceWith(l);
node.Remove(); // remove flag variable
return l;

11
ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs

@ -44,10 +44,10 @@ namespace ICSharpCode.Decompiler.Tests @@ -44,10 +44,10 @@ namespace ICSharpCode.Decompiler.Tests
return CodeSampleFileParser.ConcatLines(lines.Where(l => !CodeSampleFileParser.IsCommentOrBlank(l)));
}
protected static void AssertRoundtripCode(string fileName, bool optimize = false, bool useDebug = false)
protected static void AssertRoundtripCode(string fileName, bool optimize = false, bool useDebug = false, int compilerVersion = 4)
{
var code = RemoveIgnorableLines(File.ReadLines(fileName));
AssemblyDefinition assembly = CompileLegacy(code, optimize, useDebug);
AssemblyDefinition assembly = CompileLegacy(code, optimize, useDebug, compilerVersion);
AstBuilder decompiler = new AstBuilder(new DecompilerContext(assembly.MainModule));
decompiler.AddAssembly(assembly);
@ -58,12 +58,13 @@ namespace ICSharpCode.Decompiler.Tests @@ -58,12 +58,13 @@ namespace ICSharpCode.Decompiler.Tests
CodeAssert.AreEqual(code, output.ToString());
}
protected static AssemblyDefinition CompileLegacy(string code, bool optimize, bool useDebug)
protected static AssemblyDefinition CompileLegacy(string code, bool optimize, bool useDebug, int compilerVersion)
{
CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v4.0" } });
CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v" + new Version(compilerVersion, 0) } });
CompilerParameters options = new CompilerParameters();
options.CompilerOptions = "/unsafe /o" + (optimize ? "+" : "-") + (useDebug ? " /debug" : "");
options.ReferencedAssemblies.Add("System.Core.dll");
if (compilerVersion >= 4)
options.ReferencedAssemblies.Add("System.Core.dll");
CompilerResults results = provider.CompileAssemblyFromSource(options, code);
try
{

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

@ -75,6 +75,7 @@ @@ -75,6 +75,7 @@
<Compile Include="CustomShortCircuitOperators.cs" />
<Compile Include="Helpers\CodeAssert.cs" />
<Compile Include="IncrementDecrement.cs" />
<Compile Include="Lock.cs" />
<Compile Include="PInvoke.cs" />
<Compile Include="QueryExpressions.cs" />
<Compile Include="Switch.cs" />

38
ICSharpCode.Decompiler/Tests/Lock.cs

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
// Copyright (c) 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;
public class Lock
{
public void LockThis()
{
lock (this)
{
Console.WriteLine();
}
}
public void LockOnType()
{
lock (typeof(Lock))
{
Console.WriteLine();
}
}
}

17
ICSharpCode.Decompiler/Tests/TestRunner.cs

@ -114,6 +114,13 @@ namespace ICSharpCode.Decompiler.Tests @@ -114,6 +114,13 @@ namespace ICSharpCode.Decompiler.Tests
TestFile(@"..\..\Tests\LiftedOperators.cs");
}
[Test]
public void Lock()
{
//TestFile(@"..\..\Tests\Lock.cs", compilerVersion: 2);
TestFile(@"..\..\Tests\Lock.cs", compilerVersion: 4);
}
[Test]
public void Loops()
{
@ -180,12 +187,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -180,12 +187,12 @@ namespace ICSharpCode.Decompiler.Tests
TestFile(@"..\..\Tests\TypeAnalysisTests.cs");
}
static void TestFile(string fileName, bool useDebug = false)
static void TestFile(string fileName, bool useDebug = false, int compilerVersion = 4)
{
AssertRoundtripCode(fileName, optimize: false, useDebug: useDebug);
AssertRoundtripCode(fileName, optimize: true, useDebug: useDebug);
AssertRoundtripCode(fileName, optimize: false, useDebug: useDebug);
AssertRoundtripCode(fileName, optimize: true, useDebug: useDebug);
AssertRoundtripCode(fileName, optimize: false, useDebug: useDebug, compilerVersion: compilerVersion);
AssertRoundtripCode(fileName, optimize: true, useDebug: useDebug, compilerVersion: compilerVersion);
AssertRoundtripCode(fileName, optimize: false, useDebug: useDebug, compilerVersion: compilerVersion);
AssertRoundtripCode(fileName, optimize: true, useDebug: useDebug, compilerVersion: compilerVersion);
}
}
}

Loading…
Cancel
Save