diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index 8032b437e..bff2cc6b9 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -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 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("enter").Single(); + exit = m2.Get("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("enter").Single(); + exit = m2.Get("exit").Single(); + return m1.Get("variable").Single().Identifier == m2.Get("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("variable").Single().Identifier == m2.Get("flag").Single().Identifier) { - Expression enter = m2.Get("enter").Single(); - IdentifierExpression exit = m2.Get("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 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; diff --git a/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs b/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs index 12e3a5373..659bb2be6 100644 --- a/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs +++ b/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs @@ -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 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 { { "CompilerVersion", "v4.0" } }); + CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary { { "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 { diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj index 3f1378a05..8bb4934e7 100644 --- a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj @@ -75,6 +75,7 @@ + diff --git a/ICSharpCode.Decompiler/Tests/Lock.cs b/ICSharpCode.Decompiler/Tests/Lock.cs new file mode 100644 index 000000000..da5a59c74 --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/Lock.cs @@ -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(); + } + } +} diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs index 56e0dafe4..215725b0a 100644 --- a/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -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 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); } } }