public static List <string> GetPreprocessorSymbols(CSharpCompilerOptions flags) { var preprocessorSymbols = new List <string>(); if (flags.HasFlag(CSharpCompilerOptions.UseDebug)) { preprocessorSymbols.Add("DEBUG"); } if (flags.HasFlag(CSharpCompilerOptions.Optimize)) { preprocessorSymbols.Add("OPT"); } if (flags.HasFlag(CSharpCompilerOptions.UseRoslyn)) { preprocessorSymbols.Add("ROSLYN"); preprocessorSymbols.Add("CS60"); preprocessorSymbols.Add("CS70"); preprocessorSymbols.Add("CS71"); preprocessorSymbols.Add("CS72"); } else if (flags.HasFlag(CSharpCompilerOptions.UseMcs)) { preprocessorSymbols.Add("MCS"); } else { preprocessorSymbols.Add("LEGACY_CSC"); } return(preprocessorSymbols); }
void Run([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CSharpCompilerOptions cscOptions = CSharpCompilerOptions.None, DecompilerSettings decompilerSettings = null) { var ilFile = Path.Combine(TestCasePath, testName) + Tester.GetSuffix(cscOptions) + ".il"; var csFile = Path.Combine(TestCasePath, testName + ".cs"); if (!File.Exists(ilFile)) { // re-create .il file if necessary CompilerResults output = null; try { string outputFile = Path.ChangeExtension(ilFile, cscOptions.HasFlag(CSharpCompilerOptions.Library) ? ".dll" : ".exe"); output = Tester.CompileCSharp(csFile, cscOptions, outputFile); Tester.Disassemble(output.PathToAssembly, ilFile, asmOptions); } finally { if (output != null) { output.TempFiles.Delete(); } } } var executable = Tester.AssembleIL(ilFile, asmOptions); var decompiled = Tester.DecompileCSharp(executable, decompilerSettings ?? Tester.GetSettings(cscOptions)); CodeAssert.FilesAreEqual(csFile, decompiled, Tester.GetPreprocessorSymbols(cscOptions).ToArray()); }
public void MiniJSON([ValueSource("defaultOptions")] CSharpCompilerOptions options) { if (options.HasFlag(CSharpCompilerOptions.UseMcs)) { Assert.Ignore("Decompiler bug with mono!"); } RunCS(options: options); }
internal static DecompilerSettings GetSettings(CSharpCompilerOptions cscOptions) { if (cscOptions.HasFlag(CSharpCompilerOptions.UseRoslyn)) { return(new DecompilerSettings(CSharp.LanguageVersion.Latest)); } else { return(new DecompilerSettings(CSharp.LanguageVersion.CSharp5)); } }
void RunCS([CallerMemberName] string testName = null, CSharpCompilerOptions options = CSharpCompilerOptions.UseDebug) { string testFileName = testName + ".cs"; string testOutputFileName = testName + Tester.GetSuffix(options) + ".exe"; CompilerResults outputFile = null, decompiledOutputFile = null; try { outputFile = Tester.CompileCSharp(Path.Combine(TestCasePath, testFileName), options, outputFileName: Path.Combine(TestCasePath, testOutputFileName)); string decompiledCodeFile = Tester.DecompileCSharp(outputFile.PathToAssembly, Tester.GetSettings(options)); if (options.HasFlag(CSharpCompilerOptions.UseMcs)) { // For second pass, use roslyn instead of mcs. // mcs has some compiler bugs that cause it to not accept ILSpy-generated code, // for example when there's unreachable code due to other compiler bugs in the first mcs run. options &= ~CSharpCompilerOptions.UseMcs; options |= CSharpCompilerOptions.UseRoslyn; // Also, add an .exe.config so that we consistently use the .NET 4.x runtime. File.WriteAllText(outputFile.PathToAssembly + ".config", @"<?xml version=""1.0"" encoding=""utf-8""?> <configuration> <startup> <supportedRuntime version=""v4.0"" sku="".NETFramework,Version=v4.0,Profile=Client"" /> </startup> </configuration>"); } decompiledOutputFile = Tester.CompileCSharp(decompiledCodeFile, options); Tester.RunAndCompareOutput(testFileName, outputFile.PathToAssembly, decompiledOutputFile.PathToAssembly, decompiledCodeFile); File.Delete(decompiledCodeFile); File.Delete(decompiledOutputFile.PathToAssembly); } finally { if (outputFile != null) { outputFile.TempFiles.Delete(); } if (decompiledOutputFile != null) { decompiledOutputFile.TempFiles.Delete(); } } }
public static CompilerResults CompileCSharp(string sourceFileName, CSharpCompilerOptions flags = CSharpCompilerOptions.UseDebug, string outputFileName = null) { List <string> sourceFileNames = new List <string> { sourceFileName }; foreach (Match match in Regex.Matches(File.ReadAllText(sourceFileName), @"#include ""([\w\d./]+)""")) { sourceFileNames.Add(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(sourceFileName), match.Groups[1].Value))); } var preprocessorSymbols = GetPreprocessorSymbols(flags); if (flags.HasFlag(CSharpCompilerOptions.UseRoslyn)) { var parseOptions = new CSharpParseOptions(preprocessorSymbols: preprocessorSymbols.ToArray(), languageVersion: Microsoft.CodeAnalysis.CSharp.LanguageVersion.Latest); var syntaxTrees = sourceFileNames.Select(f => SyntaxFactory.ParseSyntaxTree(File.ReadAllText(f), parseOptions, path: f)); var compilation = CSharpCompilation.Create(Path.GetFileNameWithoutExtension(sourceFileName), syntaxTrees, defaultReferences.Value, new CSharpCompilationOptions( flags.HasFlag(CSharpCompilerOptions.Library) ? OutputKind.DynamicallyLinkedLibrary : OutputKind.ConsoleApplication, platform: flags.HasFlag(CSharpCompilerOptions.Force32Bit) ? Platform.X86 : Platform.AnyCpu, optimizationLevel: flags.HasFlag(CSharpCompilerOptions.Optimize) ? OptimizationLevel.Release : OptimizationLevel.Debug, allowUnsafe: true, deterministic: true )); CompilerResults results = new CompilerResults(new TempFileCollection()); results.PathToAssembly = outputFileName ?? Path.GetTempFileName(); var emitResult = compilation.Emit(results.PathToAssembly); if (!emitResult.Success) { StringBuilder b = new StringBuilder("Compiler error:"); foreach (var diag in emitResult.Diagnostics) { b.AppendLine(diag.ToString()); } throw new Exception(b.ToString()); } return(results); } else if (flags.HasFlag(CSharpCompilerOptions.UseMcs)) { CompilerResults results = new CompilerResults(new TempFileCollection()); results.PathToAssembly = outputFileName ?? Path.GetTempFileName(); string testBasePath = RoundtripAssembly.TestDir; if (!Directory.Exists(testBasePath)) { Assert.Ignore($"Compilation with mcs ignored: test directory '{testBasePath}' needs to be checked out separately." + Environment.NewLine + $"git clone https://github.com/icsharpcode/ILSpy-tests \"{testBasePath}\""); } string mcsPath = Path.Combine(testBasePath, @"mcs\2.6.4\bin\gmcs.bat"); string otherOptions = " -unsafe -o" + (flags.HasFlag(CSharpCompilerOptions.Optimize) ? "+ " : "- "); if (flags.HasFlag(CSharpCompilerOptions.Library)) { otherOptions += "-t:library "; } else { otherOptions += "-t:exe "; } if (flags.HasFlag(CSharpCompilerOptions.UseDebug)) { otherOptions += "-g "; } if (flags.HasFlag(CSharpCompilerOptions.Force32Bit)) { otherOptions += "-platform:x86 "; } else { otherOptions += "-platform:anycpu "; } if (preprocessorSymbols.Count > 0) { otherOptions += " \"-d:" + string.Join(";", preprocessorSymbols) + "\" "; } ProcessStartInfo info = new ProcessStartInfo(mcsPath); info.Arguments = $"{otherOptions}-out:\"{Path.GetFullPath(results.PathToAssembly)}\" {string.Join(" ", sourceFileNames.Select(fn => '"' + Path.GetFullPath(fn) + '"'))}"; info.RedirectStandardError = true; info.RedirectStandardOutput = true; info.UseShellExecute = false; Console.WriteLine($"\"{info.FileName}\" {info.Arguments}"); Process process = Process.Start(info); var outputTask = process.StandardOutput.ReadToEndAsync(); var errorTask = process.StandardError.ReadToEndAsync(); Task.WaitAll(outputTask, errorTask); process.WaitForExit(); Console.WriteLine("output: " + outputTask.Result); Console.WriteLine("errors: " + errorTask.Result); Assert.AreEqual(0, process.ExitCode, "mcs failed"); return(results); } else { var provider = new CSharpCodeProvider(new Dictionary <string, string> { { "CompilerVersion", "v4.0" } }); CompilerParameters options = new CompilerParameters(); options.GenerateExecutable = !flags.HasFlag(CSharpCompilerOptions.Library); options.CompilerOptions = "/unsafe /o" + (flags.HasFlag(CSharpCompilerOptions.Optimize) ? "+" : "-"); options.CompilerOptions += (flags.HasFlag(CSharpCompilerOptions.UseDebug) ? " /debug" : ""); options.CompilerOptions += (flags.HasFlag(CSharpCompilerOptions.Force32Bit) ? " /platform:anycpu32bitpreferred" : ""); if (preprocessorSymbols.Count > 0) { options.CompilerOptions += " /d:" + string.Join(";", preprocessorSymbols); } if (outputFileName != null) { options.OutputAssembly = outputFileName; } options.ReferencedAssemblies.Add("System.dll"); options.ReferencedAssemblies.Add("System.Core.dll"); options.ReferencedAssemblies.Add("System.Xml.dll"); options.ReferencedAssemblies.Add("Microsoft.CSharp.dll"); CompilerResults results = provider.CompileAssemblyFromFile(options, sourceFileNames.ToArray()); if (results.Errors.Cast <CompilerError>().Any(e => !e.IsWarning)) { StringBuilder b = new StringBuilder("Compiler error:"); foreach (var error in results.Errors) { b.AppendLine(error.ToString()); } throw new Exception(b.ToString()); } return(results); } }