mirror of https://github.com/icsharpcode/ILSpy.git
Browse Source
Implement F# Using: Fixes #670: [Feature Request] Detect F# using patterns when decompiling to C#pull/940/merge
15 changed files with 169 additions and 355 deletions
@ -1,30 +0,0 @@ |
|||||||
using NUnit.Framework; |
|
||||||
using System; |
|
||||||
using System.Collections.Generic; |
|
||||||
using System.Linq; |
|
||||||
using System.Reflection; |
|
||||||
using System.Text; |
|
||||||
using System.Threading.Tasks; |
|
||||||
|
|
||||||
namespace ICSharpCode.Decompiler.Tests.FSharpPatterns |
|
||||||
{ |
|
||||||
[TestFixture] |
|
||||||
public class FSharpPatternTests |
|
||||||
{ |
|
||||||
[Test] |
|
||||||
public void FSharpUsingDecompilesToCSharpUsing_Debug() |
|
||||||
{ |
|
||||||
var ilCode = TestHelpers.FuzzyReadResource("FSharpUsing.fs.Debug.il"); |
|
||||||
var csharpCode = TestHelpers.FuzzyReadResource("FSharpUsing.fs.Debug.cs"); |
|
||||||
TestHelpers.RunIL(ilCode, csharpCode); |
|
||||||
} |
|
||||||
|
|
||||||
[Test] |
|
||||||
public void FSharpUsingDecompilesToCSharpUsing_Release() |
|
||||||
{ |
|
||||||
var ilCode = TestHelpers.FuzzyReadResource("FSharpUsing.fs.Release.il"); |
|
||||||
var csharpCode = TestHelpers.FuzzyReadResource("FSharpUsing.fs.Release.cs"); |
|
||||||
TestHelpers.RunIL(ilCode, csharpCode); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,64 +0,0 @@ |
|||||||
using System; |
|
||||||
using System.IO; |
|
||||||
|
|
||||||
public static class FSharpUsingPatterns |
|
||||||
{ |
|
||||||
public static void sample1() |
|
||||||
{ |
|
||||||
using (FileStream fs = File.Create("x.txt")) |
|
||||||
{ |
|
||||||
fs.WriteByte((byte)1); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public static void sample2() |
|
||||||
{ |
|
||||||
Console.WriteLine("some text"); |
|
||||||
using (FileStream fs = File.Create("x.txt")) |
|
||||||
{ |
|
||||||
fs.WriteByte((byte)2); |
|
||||||
Console.WriteLine("some text"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public static void sample3() |
|
||||||
{ |
|
||||||
Console.WriteLine("some text"); |
|
||||||
using (FileStream fs = File.Create("x.txt")) |
|
||||||
{ |
|
||||||
fs.WriteByte((byte)3); |
|
||||||
} |
|
||||||
Console.WriteLine("some text"); |
|
||||||
} |
|
||||||
|
|
||||||
public static void sample4() |
|
||||||
{ |
|
||||||
Console.WriteLine("some text"); |
|
||||||
int num; |
|
||||||
using (FileStream fs = File.OpenRead("x.txt")) |
|
||||||
{ |
|
||||||
num = fs.ReadByte(); |
|
||||||
} |
|
||||||
int firstByte = num; |
|
||||||
Console.WriteLine("read:" + firstByte.ToString()); |
|
||||||
} |
|
||||||
|
|
||||||
public static void sample5() |
|
||||||
{ |
|
||||||
Console.WriteLine("some text"); |
|
||||||
int num; |
|
||||||
using (FileStream fs = File.OpenRead("x.txt")) |
|
||||||
{ |
|
||||||
num = fs.ReadByte(); |
|
||||||
} |
|
||||||
int firstByte = num; |
|
||||||
int num3; |
|
||||||
using (FileStream fs = File.OpenRead("x.txt")) |
|
||||||
{ |
|
||||||
int num2 = fs.ReadByte(); |
|
||||||
num3 = fs.ReadByte(); |
|
||||||
} |
|
||||||
int secondByte = num3; |
|
||||||
Console.WriteLine("read: {0}, {1}", firstByte, secondByte); |
|
||||||
} |
|
||||||
} |
|
@ -1,64 +0,0 @@ |
|||||||
using System; |
|
||||||
using System.IO; |
|
||||||
|
|
||||||
public static class FSharpUsingPatterns |
|
||||||
{ |
|
||||||
public static void sample1() |
|
||||||
{ |
|
||||||
using (FileStream fs = File.Create("x.txt")) |
|
||||||
{ |
|
||||||
fs.WriteByte(1); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public static void sample2() |
|
||||||
{ |
|
||||||
Console.WriteLine("some text"); |
|
||||||
using (FileStream fs = File.Create("x.txt")) |
|
||||||
{ |
|
||||||
fs.WriteByte(2); |
|
||||||
Console.WriteLine("some text"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public static void sample3() |
|
||||||
{ |
|
||||||
Console.WriteLine("some text"); |
|
||||||
using (FileStream fs = File.Create("x.txt")) |
|
||||||
{ |
|
||||||
fs.WriteByte(3); |
|
||||||
} |
|
||||||
Console.WriteLine("some text"); |
|
||||||
} |
|
||||||
|
|
||||||
public static void sample4() |
|
||||||
{ |
|
||||||
Console.WriteLine("some text"); |
|
||||||
int num; |
|
||||||
using (FileStream fs = File.OpenRead("x.txt")) |
|
||||||
{ |
|
||||||
num = fs.ReadByte(); |
|
||||||
} |
|
||||||
int firstByte = num; |
|
||||||
Console.WriteLine("read:" + firstByte.ToString()); |
|
||||||
} |
|
||||||
|
|
||||||
public static void sample5() |
|
||||||
{ |
|
||||||
Console.WriteLine("some text"); |
|
||||||
int secondByte; |
|
||||||
using (FileStream fs = File.OpenRead("x.txt")) |
|
||||||
{ |
|
||||||
secondByte = fs.ReadByte(); |
|
||||||
} |
|
||||||
int firstByte = secondByte; |
|
||||||
int num2; |
|
||||||
using (FileStream fs = File.OpenRead("x.txt")) |
|
||||||
{ |
|
||||||
int num = fs.ReadByte(); |
|
||||||
num2 = fs.ReadByte(); |
|
||||||
} |
|
||||||
secondByte = num2; |
|
||||||
Console.WriteLine("read: {0}, {1}", firstByte, secondByte); |
|
||||||
} |
|
||||||
} |
|
@ -1,87 +0,0 @@ |
|||||||
using ICSharpCode.Decompiler.Ast; |
|
||||||
using ICSharpCode.Decompiler.Tests.Helpers; |
|
||||||
using ICSharpCode.NRefactory.CSharp; |
|
||||||
using Mono.Cecil; |
|
||||||
using NUnit.Framework; |
|
||||||
using System; |
|
||||||
using System.Collections.Generic; |
|
||||||
using System.Diagnostics; |
|
||||||
using System.IO; |
|
||||||
using System.Linq; |
|
||||||
using System.Reflection; |
|
||||||
using System.Text; |
|
||||||
using System.Threading.Tasks; |
|
||||||
|
|
||||||
namespace ICSharpCode.Decompiler.Tests.FSharpPatterns |
|
||||||
{ |
|
||||||
public class TestHelpers |
|
||||||
{ |
|
||||||
public static string FuzzyReadResource(string resourceName) |
|
||||||
{ |
|
||||||
var asm = Assembly.GetExecutingAssembly(); |
|
||||||
var allResources = asm.GetManifestResourceNames(); |
|
||||||
var fullResourceName = allResources.Single(r => r.EndsWith(resourceName, StringComparison.OrdinalIgnoreCase)); |
|
||||||
return new StreamReader(asm.GetManifestResourceStream(fullResourceName)).ReadToEnd(); |
|
||||||
} |
|
||||||
|
|
||||||
static Lazy<string> ilasm = new Lazy<string>(() => ToolLocator.FindTool("ilasm.exe")); |
|
||||||
static Lazy<string> ildasm = new Lazy<string>(() => ToolLocator.FindTool("ildasm.exe")); |
|
||||||
|
|
||||||
public static string CompileIL(string source) |
|
||||||
{ |
|
||||||
if (ilasm.Value == null) |
|
||||||
Assert.NotNull(ilasm.Value, "Could not find ILASM.exe"); |
|
||||||
var tmp = Path.GetTempFileName(); |
|
||||||
File.Delete(tmp); |
|
||||||
var sourceFile = Path.ChangeExtension(tmp, ".il"); |
|
||||||
File.WriteAllText(sourceFile, source); |
|
||||||
var asmFile = Path.ChangeExtension(sourceFile, ".dll"); |
|
||||||
|
|
||||||
var args = string.Format("{0} /dll /debug /output:{1}", sourceFile, asmFile); |
|
||||||
using (var proc = Process.Start(new ProcessStartInfo(ilasm.Value, args) { UseShellExecute = false, })) |
|
||||||
{ |
|
||||||
proc.WaitForExit(); |
|
||||||
Assert.AreEqual(0, proc.ExitCode); |
|
||||||
} |
|
||||||
|
|
||||||
File.Delete(sourceFile); |
|
||||||
Assert.True(File.Exists(asmFile), "Assembly File does not exist"); |
|
||||||
return asmFile; |
|
||||||
} |
|
||||||
|
|
||||||
public static void RunIL(string ilCode, string expectedCSharpCode) |
|
||||||
{ |
|
||||||
var asmFilePath = CompileIL(ilCode); |
|
||||||
CompareAssemblyAgainstCSharp(expectedCSharpCode, asmFilePath); |
|
||||||
} |
|
||||||
|
|
||||||
private static void CompareAssemblyAgainstCSharp(string expectedCSharpCode, string asmFilePath) |
|
||||||
{ |
|
||||||
var module = ModuleDefinition.ReadModule(asmFilePath); |
|
||||||
try |
|
||||||
{ |
|
||||||
try { module.ReadSymbols(); } catch { } |
|
||||||
AstBuilder decompiler = new AstBuilder(new DecompilerContext(module)); |
|
||||||
decompiler.AddAssembly(module); |
|
||||||
new Helpers.RemoveCompilerAttribute().Run(decompiler.SyntaxTree); |
|
||||||
StringWriter output = new StringWriter(); |
|
||||||
|
|
||||||
// the F# assembly contains a namespace `<StartupCode$tmp6D55>` where the part after tmp is randomly generated.
|
|
||||||
// remove this from the ast to simplify the diff
|
|
||||||
var startupCodeNode = decompiler.SyntaxTree.Children.OfType<NamespaceDeclaration>().SingleOrDefault(d => d.Name.StartsWith("<StartupCode$", StringComparison.Ordinal)); |
|
||||||
if (startupCodeNode != null) |
|
||||||
startupCodeNode.Remove(); |
|
||||||
|
|
||||||
decompiler.GenerateCode(new PlainTextOutput(output)); |
|
||||||
var fullCSharpCode = output.ToString(); |
|
||||||
|
|
||||||
CodeAssert.AreEqual(expectedCSharpCode, output.ToString()); |
|
||||||
} |
|
||||||
finally |
|
||||||
{ |
|
||||||
File.Delete(asmFilePath); |
|
||||||
File.Delete(Path.ChangeExtension(asmFilePath, ".pdb")); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,99 +0,0 @@ |
|||||||
using System; |
|
||||||
using System.Collections.Generic; |
|
||||||
using System.IO; |
|
||||||
using System.Linq; |
|
||||||
using System.Text; |
|
||||||
using System.Threading.Tasks; |
|
||||||
|
|
||||||
namespace ICSharpCode.Decompiler.Tests.FSharpPatterns |
|
||||||
{ |
|
||||||
public class ToolLocator |
|
||||||
{ |
|
||||||
public static string FindTool(string fileName) |
|
||||||
{ |
|
||||||
var allPaths = FindPathForDotNetFramework().Concat(FindPathForWindowsSdk()); |
|
||||||
return allPaths.Select(dir => Path.Combine(dir, fileName)).FirstOrDefault(File.Exists); |
|
||||||
} |
|
||||||
|
|
||||||
private static IEnumerable<string> FindPathForWindowsSdk() |
|
||||||
{ |
|
||||||
string[] windowsSdkPaths = new[] |
|
||||||
{ |
|
||||||
@"Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\", |
|
||||||
@"Microsoft SDKs\Windows\v8.0A\bin\", |
|
||||||
@"Microsoft SDKs\Windows\v8.0\bin\NETFX 4.0 Tools\", |
|
||||||
@"Microsoft SDKs\Windows\v8.0\bin\", |
|
||||||
@"Microsoft SDKs\Windows\v7.1A\bin\NETFX 4.0 Tools\", |
|
||||||
@"Microsoft SDKs\Windows\v7.1A\bin\", |
|
||||||
@"Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0 Tools\", |
|
||||||
@"Microsoft SDKs\Windows\v7.0A\bin\", |
|
||||||
@"Microsoft SDKs\Windows\v6.1A\bin\", |
|
||||||
@"Microsoft SDKs\Windows\v6.0A\bin\", |
|
||||||
@"Microsoft SDKs\Windows\v6.0\bin\", |
|
||||||
@"Microsoft.NET\FrameworkSDK\bin" |
|
||||||
}; |
|
||||||
|
|
||||||
foreach (var possiblePath in windowsSdkPaths) |
|
||||||
{ |
|
||||||
string fullPath = string.Empty; |
|
||||||
|
|
||||||
// Check alternate program file paths as well as 64-bit versions.
|
|
||||||
if (Environment.Is64BitProcess) |
|
||||||
{ |
|
||||||
fullPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), possiblePath, "x64"); |
|
||||||
if (Directory.Exists(fullPath)) |
|
||||||
{ |
|
||||||
yield return fullPath; |
|
||||||
} |
|
||||||
|
|
||||||
fullPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), possiblePath, "x64"); |
|
||||||
if (Directory.Exists(fullPath)) |
|
||||||
{ |
|
||||||
yield return fullPath; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
fullPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), possiblePath); |
|
||||||
if (Directory.Exists(fullPath)) |
|
||||||
{ |
|
||||||
yield return fullPath; |
|
||||||
} |
|
||||||
|
|
||||||
fullPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), possiblePath); |
|
||||||
if (Directory.Exists(fullPath)) |
|
||||||
{ |
|
||||||
yield return fullPath; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static IEnumerable<string> FindPathForDotNetFramework() |
|
||||||
{ |
|
||||||
string[] frameworkPaths = new[] |
|
||||||
{ |
|
||||||
@"Microsoft.NET\Framework\v4.0.30319", |
|
||||||
@"Microsoft.NET\Framework\v2.0.50727" |
|
||||||
}; |
|
||||||
|
|
||||||
foreach (var possiblePath in frameworkPaths) |
|
||||||
{ |
|
||||||
string fullPath = string.Empty; |
|
||||||
|
|
||||||
if (Environment.Is64BitProcess) |
|
||||||
{ |
|
||||||
fullPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), possiblePath.Replace(@"\Framework\", @"\Framework64\")); |
|
||||||
if (Directory.Exists(fullPath)) |
|
||||||
{ |
|
||||||
yield return fullPath; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
fullPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), possiblePath); |
|
||||||
if (Directory.Exists(fullPath)) |
|
||||||
{ |
|
||||||
yield return fullPath; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,58 @@ |
|||||||
|
using System; |
||||||
|
using System.IO; |
||||||
|
|
||||||
|
public static class FSharpUsingPatterns |
||||||
|
{ |
||||||
|
public static void sample1() |
||||||
|
{ |
||||||
|
using (FileStream fileStream = File.Create("x.txt")) { |
||||||
|
fileStream.WriteByte((byte)1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void sample2() |
||||||
|
{ |
||||||
|
Console.WriteLine("some text"); |
||||||
|
using (FileStream fileStream = File.Create("x.txt")) { |
||||||
|
fileStream.WriteByte((byte)2); |
||||||
|
Console.WriteLine("some text"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void sample3() |
||||||
|
{ |
||||||
|
Console.WriteLine("some text"); |
||||||
|
using (FileStream fileStream = File.Create("x.txt")) { |
||||||
|
fileStream.WriteByte((byte)3); |
||||||
|
} |
||||||
|
Console.WriteLine("some text"); |
||||||
|
} |
||||||
|
|
||||||
|
public static void sample4() |
||||||
|
{ |
||||||
|
Console.WriteLine("some text"); |
||||||
|
int num = default(int); |
||||||
|
using (FileStream fileStream = File.OpenRead("x.txt")) { |
||||||
|
num = fileStream.ReadByte(); |
||||||
|
} |
||||||
|
int num2 = num; |
||||||
|
Console.WriteLine("read:" + num2.ToString()); |
||||||
|
} |
||||||
|
|
||||||
|
public static void sample5() |
||||||
|
{ |
||||||
|
Console.WriteLine("some text"); |
||||||
|
int num = default(int); |
||||||
|
using (FileStream fileStream = File.OpenRead("x.txt")) { |
||||||
|
num = fileStream.ReadByte(); |
||||||
|
} |
||||||
|
int num2 = num; |
||||||
|
int num3 = default(int); |
||||||
|
using (FileStream fileStream = File.OpenRead("x.txt")) { |
||||||
|
fileStream.ReadByte(); |
||||||
|
num3 = fileStream.ReadByte(); |
||||||
|
} |
||||||
|
int num4 = num3; |
||||||
|
Console.WriteLine("read: {0}, {1}", num2, num4); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
using System; |
||||||
|
using System.IO; |
||||||
|
|
||||||
|
public static class FSharpUsingPatterns |
||||||
|
{ |
||||||
|
public static void sample1() |
||||||
|
{ |
||||||
|
using (FileStream fileStream = File.Create("x.txt")) { |
||||||
|
fileStream.WriteByte(1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void sample2() |
||||||
|
{ |
||||||
|
Console.WriteLine("some text"); |
||||||
|
using (FileStream fileStream = File.Create("x.txt")) { |
||||||
|
fileStream.WriteByte(2); |
||||||
|
Console.WriteLine("some text"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void sample3() |
||||||
|
{ |
||||||
|
Console.WriteLine("some text"); |
||||||
|
using (FileStream fileStream = File.Create("x.txt")) { |
||||||
|
fileStream.WriteByte(3); |
||||||
|
} |
||||||
|
Console.WriteLine("some text"); |
||||||
|
} |
||||||
|
|
||||||
|
public static void sample4() |
||||||
|
{ |
||||||
|
Console.WriteLine("some text"); |
||||||
|
int num = default(int); |
||||||
|
using (FileStream fileStream = File.OpenRead("x.txt")) { |
||||||
|
num = fileStream.ReadByte(); |
||||||
|
} |
||||||
|
int num2 = num; |
||||||
|
Console.WriteLine("read:" + num2.ToString()); |
||||||
|
} |
||||||
|
|
||||||
|
public static void sample5() |
||||||
|
{ |
||||||
|
Console.WriteLine("some text"); |
||||||
|
int num = default(int); |
||||||
|
using (FileStream fileStream = File.OpenRead("x.txt")) { |
||||||
|
num = fileStream.ReadByte(); |
||||||
|
} |
||||||
|
int num2 = num; |
||||||
|
int num3 = default(int); |
||||||
|
using (FileStream fileStream = File.OpenRead("x.txt")) { |
||||||
|
fileStream.ReadByte(); |
||||||
|
num3 = fileStream.ReadByte(); |
||||||
|
} |
||||||
|
num = num3; |
||||||
|
Console.WriteLine("read: {0}, {1}", num2, num); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue