diff --git a/BuildTools/Automated.proj b/BuildTools/Automated.proj index 3cce65247..d0b3a5c2f 100644 --- a/BuildTools/Automated.proj +++ b/BuildTools/Automated.proj @@ -9,6 +9,7 @@ $(MSBuildProjectDirectory)\.. $(ProjectDir)\ILSpy\bin\Release + $(ProjectDir)\ILSpy.AddIn\bin\Release $(ProjectDir)\BuildTools\MSBuildCommunityTasks ILSpy_ @@ -17,7 +18,7 @@ properties available for the projects inside the solution (but this works using the command line) --> - "$(MSBuildBinPath)\..\v4.0.30319\msbuild.exe" + "$(MSBuildBinPath)\msbuild.exe" /p:Configuration=Release "/p:Platform=Any CPU" $(BuildProperties) "/p:BranchName=$(BranchName)" $(BuildProperties) $(AdditionalBuildProperties) @@ -92,6 +93,8 @@ ZipLevel="9" WorkingDirectory="$(ILSpyBin)" ZipFileName="$(ArtefactsOutputDir)\$(ArtefactPrefix)$(BuildNumber)_Binaries.zip"/> + diff --git a/BuildTools/buildAndCreatePackage.bat b/BuildTools/buildAndCreatePackage.bat index 3624a6f0c..e5974726b 100644 --- a/BuildTools/buildAndCreatePackage.bat +++ b/BuildTools/buildAndCreatePackage.bat @@ -1,6 +1,6 @@ @echo This script simulates what the build server is doing @rem /p:AdditionalBuildProperties="/v:d /p:MSBuildTargetsVerbose=true" -%windir%\microsoft.net\framework\v4.0.30319\msbuild Automated.proj /p:ArtefactsOutputDir="%CD%\build" /p:TestReportsDir="%CD%\build" +"%ProgramFiles(x86)%\MSBuild\14.0\Bin\msbuild.exe" Automated.proj /p:ArtefactsOutputDir="%CD%\build" /p:TestReportsDir="%CD%\build" @IF %ERRORLEVEL% NEQ 0 GOTO err @exit /B 0 :err diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index aa97dc9bf..8dbf82cd3 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -1521,7 +1521,8 @@ namespace ICSharpCode.Decompiler.Ast } var type = argument.Type.Resolve(); if (type != null && type.IsEnum) { - return MakePrimitive(Convert.ToInt64(argument.Value), type); + long val = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, argument.Value, false); + return MakePrimitive(val, type); } else if (argument.Value is TypeReference) { return CreateTypeOfExpression((TypeReference)argument.Value); } else { diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index b5b7d57ff..a0bb65095 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -754,26 +754,28 @@ namespace ICSharpCode.Decompiler.Ast return ace; } } - if (declaringType.IsAnonymousType()) { - MethodDefinition ctor = ((MethodReference)operand).Resolve(); - if (methodDef != null) { - AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression(); - if (CanInferAnonymousTypePropertyNamesFromArguments(args, ctor.Parameters)) { - atce.Initializers.AddRange(args); - } else { - for (int i = 0; i < args.Count; i++) { - atce.Initializers.Add( - new NamedExpression { - Name = ctor.Parameters[i].Name, - Expression = args[i] - }); - } + MethodDefinition ctor = ((MethodReference)operand).Resolve(); + if (declaringType.IsAnonymousType() && methodDef != null) { + AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression(); + if (CanInferAnonymousTypePropertyNamesFromArguments(args, ctor.Parameters)) { + atce.Initializers.AddRange(args); + } else { + for (int i = 0; i < args.Count; i++) { + atce.Initializers.Add( + new NamedExpression { + Name = ctor.Parameters[i].Name, + Expression = args[i] + }); } - return atce; } + return atce; } var oce = new Ast.ObjectCreateExpression(); oce.Type = AstBuilder.ConvertType(declaringType); + // seems like IsIn/IsOut information for parameters is only correct on the ctor's MethodDefinition + if (ctor != null) { + AdjustArgumentsForMethodCall(ctor, args); + } oce.Arguments.AddRange(args); return oce.WithAnnotation(operand); } diff --git a/ICSharpCode.Decompiler/Ast/DecompilerContext.cs b/ICSharpCode.Decompiler/Ast/DecompilerContext.cs index 624b1b33f..b8cc2e149 100644 --- a/ICSharpCode.Decompiler/Ast/DecompilerContext.cs +++ b/ICSharpCode.Decompiler/Ast/DecompilerContext.cs @@ -34,26 +34,12 @@ namespace ICSharpCode.Decompiler public MethodDefinition CurrentMethod; public DecompilerSettings Settings = new DecompilerSettings(); public bool CurrentMethodIsAsync; - -// public ITypeResolveContext TypeResolveContext; -// public IProjectContent ProjectContent; - + public DecompilerContext(ModuleDefinition currentModule) { if (currentModule == null) - throw new ArgumentNullException("currentModule"); + throw new ArgumentNullException(nameof(currentModule)); this.CurrentModule = currentModule; - -// this.ProjectContent = new CecilTypeResolveContext(currentModule); -// List resolveContexts = new List(); -// resolveContexts.Add(this.ProjectContent); -// foreach (AssemblyNameReference r in currentModule.AssemblyReferences) { -// AssemblyDefinition d = currentModule.AssemblyResolver.Resolve(r); -// if (d != null) { -// resolveContexts.Add(new CecilTypeResolveContext(d.MainModule)); -// } -// } -// this.TypeResolveContext = new CompositeTypeResolveContext(resolveContexts); } /// diff --git a/ICSharpCode.Decompiler/Ast/NameVariables.cs b/ICSharpCode.Decompiler/Ast/NameVariables.cs index 3da808cbe..3dc30e2dc 100644 --- a/ICSharpCode.Decompiler/Ast/NameVariables.cs +++ b/ICSharpCode.Decompiler/Ast/NameVariables.cs @@ -335,7 +335,7 @@ namespace ICSharpCode.Decompiler.Ast // remove field prefix: if (name.Length > 2 && name.StartsWith("m_", StringComparison.Ordinal)) name = name.Substring(2); - else if (name.Length > 1 && name[0] == '_') + else if (name.Length > 1 && name[0] == '_' && (char.IsLetter(name[1]) || name[1] == '_')) name = name.Substring(1); if (name.Length == 0) diff --git a/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs b/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs index 2cfff7d96..d7e8fcfb9 100644 --- a/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs +++ b/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs @@ -188,6 +188,15 @@ namespace ICSharpCode.Decompiler.Ast public override void WriteKeyword(Role role, string keyword) { + //To make reference for 'this' and 'base' keywords in the ClassName():this() expression + if (role == ConstructorInitializer.ThisKeywordRole || role == ConstructorInitializer.BaseKeywordRole) { + var reference = GetCurrentMemberReference(); + if (reference != null) { + output.WriteReference(keyword, reference); + return; + } + } + output.Write(keyword); } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index 0741466f9..d76e029a7 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -194,7 +194,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms ame.Body = body; replacement = ame; } - var expectedType = objectCreateExpression.Annotation().ExpectedType.Resolve(); + var expectedType = objectCreateExpression.Annotation()?.ExpectedType?.Resolve(); if (expectedType != null && !expectedType.IsDelegate()) { var simplifiedDelegateCreation = (ObjectCreateExpression)objectCreateExpression.Clone(); simplifiedDelegateCreation.Arguments.Clear(); diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index bff2cc6b9..d38116f11 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -58,11 +58,12 @@ namespace ICSharpCode.Decompiler.Ast.Transforms public override AstNode VisitExpressionStatement(ExpressionStatement expressionStatement, object data) { AstNode result; - if (context.Settings.UsingStatement) { - result = TransformUsings(expressionStatement); + if (context.Settings.UsingStatement) + { + result = TransformNonGenericForEach(expressionStatement); if (result != null) return result; - result = TransformNonGenericForEach(expressionStatement); + result = TransformUsings(expressionStatement); if (result != null) return result; } @@ -156,7 +157,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms }; } - static readonly AstNode usingTryCatchPattern = new TryCatchStatement { + static readonly AstNode usingTryCatchPattern = new Choice { + { "c#/vb", + new TryCatchStatement { TryBlock = new AnyNode(), FinallyBlock = new BlockStatement { new Choice { @@ -177,6 +180,33 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } }.ToStatement() } + } + }, + { "f#", + new TryCatchStatement { + TryBlock = new AnyNode(), + FinallyBlock = + new BlockStatement { + new ExpressionStatement( + new AssignmentExpression(left: new NamedNode("disposable", new IdentifierExpression(Pattern.AnyString)), + right: new AsExpression(expression: new NamedNode("ident", new IdentifierExpression(Pattern.AnyString)), + type: new TypePattern(typeof(IDisposable)) + ) + ) + ), + new IfElseStatement { + Condition = new BinaryOperatorExpression( + new Backreference("disposable"), + BinaryOperatorType.InEquality, + new NullReferenceExpression() + ), + TrueStatement = new BlockStatement { + new ExpressionStatement(InvokeDispose(new Backreference("disposable"))) + } + } + } + } + } }; public UsingStatement TransformUsings(ExpressionStatement node) @@ -213,9 +243,20 @@ namespace ICSharpCode.Decompiler.Ast.Transforms // Validate that the variable is not used after the using statement: if (!IsVariableValueUnused(varDecl, tryCatch)) return null; - + + if (m2.Has("f#")) { + string variableNameDisposable = m2.Get("disposable").Single().Identifier; + VariableDeclarationStatement varDeclDisposable = FindVariableDeclaration(node, variableNameDisposable); + if (varDeclDisposable == null || !(varDeclDisposable.Parent is BlockStatement)) + return null; + + // Validate that the variable is not used after the using statement: + if (!IsVariableValueUnused(varDeclDisposable, tryCatch)) + return null; + } + node.Remove(); - + UsingStatement usingStatement = new UsingStatement(); usingStatement.EmbeddedStatement = tryCatch.TryBlock.Detach(); tryCatch.ReplaceWith(usingStatement); @@ -378,6 +419,13 @@ namespace ICSharpCode.Decompiler.Ast.Transforms // We just care that we can move it in front of the loop: if (declarationPoint != loop) return null; + + // Make sure that the enumerator variable is not used inside the body + var enumeratorId = Identifier.Create(enumeratorVar.Name); + foreach (Statement stmt in m.Get("statement")) { + if (stmt.Descendants.OfType().Any(id => enumeratorId.IsMatch(id))) + return null; + } BlockStatement newBody = new BlockStatement(); foreach (Statement stmt in m.Get("variablesInsideLoop")) diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 2d8cb71d4..99b6693f0 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -139,16 +139,16 @@ - - {D68133BD-1E63-496E-9EDE-4FBDBF77B486} + + {d68133bd-1e63-496e-9ede-4fbdbf77b486} Mono.Cecil - - {53DCA265-3C3C-42F9-B647-F72BA678122B} + + {53dca265-3c3c-42f9-b647-f72ba678122b} ICSharpCode.NRefactory.CSharp - {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} + {3b2a5653-ec97-4001-bb9b-d90f1af2c371} ICSharpCode.NRefactory diff --git a/ICSharpCode.Decompiler/ILAst/StateRange.cs b/ICSharpCode.Decompiler/ILAst/StateRange.cs index 3a3dfc2f3..a25af05ce 100644 --- a/ICSharpCode.Decompiler/ILAst/StateRange.cs +++ b/ICSharpCode.Decompiler/ILAst/StateRange.cs @@ -273,14 +273,23 @@ namespace ICSharpCode.Decompiler.ILAst { if (pos > 0 && body[pos - 1] is ILLabel) { pos--; + return; // label found + } + + // ensure that the first element at body[pos] is a label: + ILLabel newLabel = new ILLabel(); + newLabel.Name = "YieldReturnEntryPoint"; + + ILExpression expr = pos == 1 && body.Count == 1 ? body[0] as ILExpression : null; + if (expr != null && expr.Code == ILCode.Leave && expr.Operand is ILLabel) { + ranges[newLabel] = ranges[(ILLabel)expr.Operand]; + pos = 0; } else { - // ensure that the first element at body[pos] is a label: - ILLabel newLabel = new ILLabel(); - newLabel.Name = "YieldReturnEntryPoint"; ranges[newLabel] = ranges[body[pos]]; // give the label the range of the instruction at body[pos] - body.Insert(pos, newLabel); - bodyLength++; } + + body.Insert(pos, newLabel); + bodyLength++; } public LabelRangeMapping CreateLabelRangeMapping(List body, int pos, int bodyLength) diff --git a/ICSharpCode.Decompiler/Tests/Async.cs b/ICSharpCode.Decompiler/Tests/Async.cs index 7629dd5e6..910da5aeb 100644 --- a/ICSharpCode.Decompiler/Tests/Async.cs +++ b/ICSharpCode.Decompiler/Tests/Async.cs @@ -37,6 +37,10 @@ public class Async Console.WriteLine("No Await"); } + public async void EmptyVoidMethod() + { + } + public async void AwaitYield() { await Task.Yield(); diff --git a/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributes.cs b/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributes.cs index 37c1c0d71..67052909a 100644 --- a/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributes.cs +++ b/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributes.cs @@ -39,6 +39,11 @@ namespace aa { } } + [CustomAttributes.MyAttribute(CustomAttributes.ULongEnum.MaxUInt64)] + public enum ULongEnum : ulong + { + MaxUInt64 = 18446744073709551615uL + } [CustomAttributes.MyAttribute(CustomAttributes.EnumWithFlag.Item1 | CustomAttributes.EnumWithFlag.Item2)] private static int field; [CustomAttributes.MyAttribute(CustomAttributes.EnumWithFlag.All)] diff --git a/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpPatternTests.cs b/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpPatternTests.cs new file mode 100644 index 000000000..7980d6579 --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpPatternTests.cs @@ -0,0 +1,30 @@ +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); + } + } +} diff --git a/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpUsing.fs b/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpUsing.fs new file mode 100644 index 000000000..5db5939da --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpUsing.fs @@ -0,0 +1,38 @@ +module FSharpUsingPatterns + +open System +open System.IO + +let sample1() = + use fs = File.Create("x.txt") + fs.WriteByte(byte 1) + +let sample2() = + Console.WriteLine("some text") + use fs = File.Create("x.txt") + fs.WriteByte(byte 2) + Console.WriteLine("some text") + +let sample3() = + Console.WriteLine("some text") + do use fs = File.Create("x.txt") + fs.WriteByte(byte 3) + Console.WriteLine("some text") + +let sample4() = + Console.WriteLine("some text") + let firstByte = + use fs = File.OpenRead("x.txt") + fs.ReadByte() + Console.WriteLine("read:" + firstByte.ToString()) + +let sample5() = + Console.WriteLine("some text") + let firstByte = + use fs = File.OpenRead("x.txt") + fs.ReadByte() + let secondByte = + use fs = File.OpenRead("x.txt") + fs.ReadByte() |> ignore + fs.ReadByte() + Console.WriteLine("read: {0}, {1}", firstByte, secondByte) diff --git a/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpUsing.fs.Debug.cs b/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpUsing.fs.Debug.cs new file mode 100644 index 000000000..3c62c0cac --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpUsing.fs.Debug.cs @@ -0,0 +1,64 @@ +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); + } +} diff --git a/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpUsing.fs.Debug.il b/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpUsing.fs.Debug.il new file mode 100644 index 000000000..3661238ac --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpUsing.fs.Debug.il @@ -0,0 +1,344 @@ +.class public auto ansi abstract sealed FSharpUsingPatterns + extends [mscorlib]System.Object +{ + // Methods + .method public static + void sample1 () cil managed + { + // Method begins at RVA 0x2050 + // Code size 53 (0x35) + .maxstack 4 + .locals init ( + [0] class [mscorlib]System.IO.FileStream fs, + [1] class [mscorlib]System.Object, + [2] class [mscorlib]System.IDisposable + ) + + IL_0000: nop + IL_0001: ldstr "x.txt" + IL_0006: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::Create(string) + IL_000b: stloc.0 + .try + { + IL_000c: ldloc.0 + IL_000d: ldc.i4.1 + IL_000e: conv.u1 + IL_000f: callvirt instance void [mscorlib]System.IO.Stream::WriteByte(uint8) + IL_0014: ldnull + IL_0015: stloc.1 + IL_0016: leave.s IL_0032 + } // end .try + finally + { + IL_0018: ldloc.0 + IL_0019: isinst [mscorlib]System.IDisposable + IL_001e: stloc.2 + IL_001f: ldloc.2 + IL_0020: brfalse.s IL_0024 + + IL_0022: br.s IL_0026 + + IL_0024: br.s IL_002f + + IL_0026: ldloc.2 + IL_0027: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_002c: ldnull + IL_002d: pop + IL_002e: endfinally + + IL_002f: ldnull + IL_0030: pop + IL_0031: endfinally + } // end handler + + IL_0032: ldloc.1 + IL_0033: pop + IL_0034: ret + } // end of method FSharpUsingPatterns::sample1 + + .method public static + void sample2 () cil managed + { + // Method begins at RVA 0x20a4 + // Code size 73 (0x49) + .maxstack 4 + .locals init ( + [0] class [mscorlib]System.IO.FileStream fs, + [1] class [mscorlib]System.Object, + [2] class [mscorlib]System.IDisposable + ) + + IL_0000: nop + IL_0001: ldstr "some text" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: ldstr "x.txt" + IL_0010: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::Create(string) + IL_0015: stloc.0 + .try + { + IL_0016: ldloc.0 + IL_0017: ldc.i4.2 + IL_0018: conv.u1 + IL_0019: callvirt instance void [mscorlib]System.IO.Stream::WriteByte(uint8) + IL_001e: ldstr "some text" + IL_0023: call void [mscorlib]System.Console::WriteLine(string) + IL_0028: ldnull + IL_0029: stloc.1 + IL_002a: leave.s IL_0046 + } // end .try + finally + { + IL_002c: ldloc.0 + IL_002d: isinst [mscorlib]System.IDisposable + IL_0032: stloc.2 + IL_0033: ldloc.2 + IL_0034: brfalse.s IL_0038 + + IL_0036: br.s IL_003a + + IL_0038: br.s IL_0043 + + IL_003a: ldloc.2 + IL_003b: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0040: ldnull + IL_0041: pop + IL_0042: endfinally + + IL_0043: ldnull + IL_0044: pop + IL_0045: endfinally + } // end handler + + IL_0046: ldloc.1 + IL_0047: pop + IL_0048: ret + } // end of method FSharpUsingPatterns::sample2 + + .method public static + void sample3 () cil managed + { + // Method begins at RVA 0x210c + // Code size 73 (0x49) + .maxstack 4 + .locals init ( + [0] class [mscorlib]System.IO.FileStream fs, + [1] class [mscorlib]System.Object, + [2] class [mscorlib]System.IDisposable + ) + + IL_0000: nop + IL_0001: ldstr "some text" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: ldstr "x.txt" + IL_0010: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::Create(string) + IL_0015: stloc.0 + .try + { + IL_0016: ldloc.0 + IL_0017: ldc.i4.3 + IL_0018: conv.u1 + IL_0019: callvirt instance void [mscorlib]System.IO.Stream::WriteByte(uint8) + IL_001e: ldnull + IL_001f: stloc.1 + IL_0020: leave.s IL_003c + } // end .try + finally + { + IL_0022: ldloc.0 + IL_0023: isinst [mscorlib]System.IDisposable + IL_0028: stloc.2 + IL_0029: ldloc.2 + IL_002a: brfalse.s IL_002e + + IL_002c: br.s IL_0030 + + IL_002e: br.s IL_0039 + + IL_0030: ldloc.2 + IL_0031: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0036: ldnull + IL_0037: pop + IL_0038: endfinally + + IL_0039: ldnull + IL_003a: pop + IL_003b: endfinally + } // end handler + + IL_003c: ldloc.1 + IL_003d: pop + IL_003e: ldstr "some text" + IL_0043: call void [mscorlib]System.Console::WriteLine(string) + IL_0048: ret + } // end of method FSharpUsingPatterns::sample3 + + .method public static + void sample4 () cil managed + { + // Method begins at RVA 0x2174 + // Code size 89 (0x59) + .maxstack 4 + .locals init ( + [0] int32 firstByte, + [1] class [mscorlib]System.IO.FileStream fs, + [2] int32, + [3] class [mscorlib]System.IDisposable + ) + + IL_0000: nop + IL_0001: ldstr "some text" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldstr "x.txt" + IL_0011: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::OpenRead(string) + IL_0016: stloc.1 + .try + { + IL_0017: ldloc.1 + IL_0018: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte() + IL_001d: stloc.2 + IL_001e: leave.s IL_003a + } // end .try + finally + { + IL_0020: ldloc.1 + IL_0021: isinst [mscorlib]System.IDisposable + IL_0026: stloc.3 + IL_0027: ldloc.3 + IL_0028: brfalse.s IL_002c + + IL_002a: br.s IL_002e + + IL_002c: br.s IL_0037 + + IL_002e: ldloc.3 + IL_002f: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0034: ldnull + IL_0035: pop + IL_0036: endfinally + + IL_0037: ldnull + IL_0038: pop + IL_0039: endfinally + } // end handler + + IL_003a: ldloc.2 + IL_003b: stloc.0 + IL_003c: ldstr "read:" + IL_0041: ldloca.s firstByte + IL_0043: constrained. [mscorlib]System.Int32 + IL_0049: callvirt instance string [mscorlib]System.Object::ToString() + IL_004e: call string [mscorlib]System.String::Concat(string, string) + IL_0053: call void [mscorlib]System.Console::WriteLine(string) + IL_0058: ret + } // end of method FSharpUsingPatterns::sample4 + + .method public static + void sample5 () cil managed + { + // Method begins at RVA 0x21ec + // Code size 155 (0x9b) + .maxstack 5 + .locals init ( + [0] int32 firstByte, + [1] class [mscorlib]System.IO.FileStream fs, + [2] int32, + [3] class [mscorlib]System.IDisposable, + [4] int32 secondByte, + [5] class [mscorlib]System.IO.FileStream fs, + [6] int32, + [7] int32, + [8] int32, + [9] class [mscorlib]System.IDisposable + ) + + IL_0000: nop + IL_0001: ldstr "some text" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldstr "x.txt" + IL_0011: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::OpenRead(string) + IL_0016: stloc.1 + .try + { + IL_0017: ldloc.1 + IL_0018: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte() + IL_001d: stloc.2 + IL_001e: leave.s IL_003a + } // end .try + finally + { + IL_0020: ldloc.1 + IL_0021: isinst [mscorlib]System.IDisposable + IL_0026: stloc.3 + IL_0027: ldloc.3 + IL_0028: brfalse.s IL_002c + + IL_002a: br.s IL_002e + + IL_002c: br.s IL_0037 + + IL_002e: ldloc.3 + IL_002f: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0034: ldnull + IL_0035: pop + IL_0036: endfinally + + IL_0037: ldnull + IL_0038: pop + IL_0039: endfinally + } // end handler + + IL_003a: ldloc.2 + IL_003b: stloc.0 + IL_003c: nop + IL_003d: ldstr "x.txt" + IL_0042: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::OpenRead(string) + IL_0047: stloc.s fs + .try + { + IL_0049: ldloc.s fs + IL_004b: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte() + IL_0050: stloc.s 7 + IL_0052: ldloc.s 7 + IL_0054: stloc.s 8 + IL_0056: ldloc.s fs + IL_0058: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte() + IL_005d: stloc.s 6 + IL_005f: leave.s IL_007f + } // end .try + finally + { + IL_0061: ldloc.s fs + IL_0063: isinst [mscorlib]System.IDisposable + IL_0068: stloc.s 9 + IL_006a: ldloc.s 9 + IL_006c: brfalse.s IL_0070 + + IL_006e: br.s IL_0072 + + IL_0070: br.s IL_007c + + IL_0072: ldloc.s 9 + IL_0074: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0079: ldnull + IL_007a: pop + IL_007b: endfinally + + IL_007c: ldnull + IL_007d: pop + IL_007e: endfinally + } // end handler + + IL_007f: ldloc.s 6 + IL_0081: stloc.s secondByte + IL_0083: ldstr "read: {0}, {1}" + IL_0088: ldloc.0 + IL_0089: box [mscorlib]System.Int32 + IL_008e: ldloc.s secondByte + IL_0090: box [mscorlib]System.Int32 + IL_0095: call void [mscorlib]System.Console::WriteLine(string, object, object) + IL_009a: ret + } // end of method FSharpUsingPatterns::sample5 + +} // end of class FSharpUsingPatterns diff --git a/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpUsing.fs.Release.cs b/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpUsing.fs.Release.cs new file mode 100644 index 000000000..c9d091847 --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpUsing.fs.Release.cs @@ -0,0 +1,64 @@ +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); + } +} diff --git a/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpUsing.fs.Release.il b/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpUsing.fs.Release.il new file mode 100644 index 000000000..6299c9687 --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/FSharpPatterns/FSharpUsing.fs.Release.il @@ -0,0 +1,311 @@ +.class public auto ansi abstract sealed FSharpUsingPatterns + extends [mscorlib]System.Object +{ + // Methods + .method public static + void sample1 () cil managed + { + // Method begins at RVA 0x2050 + // Code size 48 (0x30) + .maxstack 4 + .locals init ( + [0] class [mscorlib]System.IO.FileStream fs, + [1] class [mscorlib]System.Object, + [2] class [mscorlib]System.IDisposable + ) + + IL_0000: nop + IL_0001: ldstr "x.txt" + IL_0006: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::Create(string) + IL_000b: stloc.0 + .try + { + IL_000c: ldloc.0 + IL_000d: ldc.i4.1 + IL_000e: callvirt instance void [mscorlib]System.IO.Stream::WriteByte(uint8) + IL_0013: ldnull + IL_0014: stloc.1 + IL_0015: leave.s IL_002d + } // end .try + finally + { + IL_0017: ldloc.0 + IL_0018: isinst [mscorlib]System.IDisposable + IL_001d: stloc.2 + IL_001e: ldloc.2 + IL_001f: brfalse.s IL_002a + + IL_0021: ldloc.2 + IL_0022: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0027: ldnull + IL_0028: pop + IL_0029: endfinally + + IL_002a: ldnull + IL_002b: pop + IL_002c: endfinally + } // end handler + + IL_002d: ldloc.1 + IL_002e: pop + IL_002f: ret + } // end of method FSharpUsingPatterns::sample1 + + .method public static + void sample2 () cil managed + { + // Method begins at RVA 0x209c + // Code size 68 (0x44) + .maxstack 4 + .locals init ( + [0] class [mscorlib]System.IO.FileStream fs, + [1] class [mscorlib]System.Object, + [2] class [mscorlib]System.IDisposable + ) + + IL_0000: nop + IL_0001: ldstr "some text" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: ldstr "x.txt" + IL_0010: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::Create(string) + IL_0015: stloc.0 + .try + { + IL_0016: ldloc.0 + IL_0017: ldc.i4.2 + IL_0018: callvirt instance void [mscorlib]System.IO.Stream::WriteByte(uint8) + IL_001d: ldstr "some text" + IL_0022: call void [mscorlib]System.Console::WriteLine(string) + IL_0027: ldnull + IL_0028: stloc.1 + IL_0029: leave.s IL_0041 + } // end .try + finally + { + IL_002b: ldloc.0 + IL_002c: isinst [mscorlib]System.IDisposable + IL_0031: stloc.2 + IL_0032: ldloc.2 + IL_0033: brfalse.s IL_003e + + IL_0035: ldloc.2 + IL_0036: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_003b: ldnull + IL_003c: pop + IL_003d: endfinally + + IL_003e: ldnull + IL_003f: pop + IL_0040: endfinally + } // end handler + + IL_0041: ldloc.1 + IL_0042: pop + IL_0043: ret + } // end of method FSharpUsingPatterns::sample2 + + .method public static + void sample3 () cil managed + { + // Method begins at RVA 0x20fc + // Code size 68 (0x44) + .maxstack 4 + .locals init ( + [0] class [mscorlib]System.IO.FileStream fs, + [1] class [mscorlib]System.Object, + [2] class [mscorlib]System.IDisposable + ) + + IL_0000: nop + IL_0001: ldstr "some text" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: ldstr "x.txt" + IL_0010: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::Create(string) + IL_0015: stloc.0 + .try + { + IL_0016: ldloc.0 + IL_0017: ldc.i4.3 + IL_0018: callvirt instance void [mscorlib]System.IO.Stream::WriteByte(uint8) + IL_001d: ldnull + IL_001e: stloc.1 + IL_001f: leave.s IL_0037 + } // end .try + finally + { + IL_0021: ldloc.0 + IL_0022: isinst [mscorlib]System.IDisposable + IL_0027: stloc.2 + IL_0028: ldloc.2 + IL_0029: brfalse.s IL_0034 + + IL_002b: ldloc.2 + IL_002c: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0031: ldnull + IL_0032: pop + IL_0033: endfinally + + IL_0034: ldnull + IL_0035: pop + IL_0036: endfinally + } // end handler + + IL_0037: ldloc.1 + IL_0038: pop + IL_0039: ldstr "some text" + IL_003e: call void [mscorlib]System.Console::WriteLine(string) + IL_0043: ret + } // end of method FSharpUsingPatterns::sample3 + + .method public static + void sample4 () cil managed + { + // Method begins at RVA 0x215c + // Code size 85 (0x55) + .maxstack 4 + .locals init ( + [0] int32 firstByte, + [1] class [mscorlib]System.IO.FileStream fs, + [2] int32, + [3] class [mscorlib]System.IDisposable + ) + + IL_0000: nop + IL_0001: ldstr "some text" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldstr "x.txt" + IL_0011: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::OpenRead(string) + IL_0016: stloc.1 + .try + { + IL_0017: ldloc.1 + IL_0018: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte() + IL_001d: stloc.2 + IL_001e: leave.s IL_0036 + } // end .try + finally + { + IL_0020: ldloc.1 + IL_0021: isinst [mscorlib]System.IDisposable + IL_0026: stloc.3 + IL_0027: ldloc.3 + IL_0028: brfalse.s IL_0033 + + IL_002a: ldloc.3 + IL_002b: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0030: ldnull + IL_0031: pop + IL_0032: endfinally + + IL_0033: ldnull + IL_0034: pop + IL_0035: endfinally + } // end handler + + IL_0036: ldloc.2 + IL_0037: stloc.0 + IL_0038: ldstr "read:" + IL_003d: ldloca.s firstByte + IL_003f: constrained. [mscorlib]System.Int32 + IL_0045: callvirt instance string [mscorlib]System.Object::ToString() + IL_004a: call string [mscorlib]System.String::Concat(string, string) + IL_004f: call void [mscorlib]System.Console::WriteLine(string) + IL_0054: ret + } // end of method FSharpUsingPatterns::sample4 + + .method public static + void sample5 () cil managed + { + // Method begins at RVA 0x21d0 + // Code size 134 (0x86) + .maxstack 5 + .locals init ( + [0] int32 firstByte, + [1] class [mscorlib]System.IO.FileStream fs, + [2] int32 secondByte, + [3] class [mscorlib]System.IDisposable, + [4] int32, + [5] int32 + ) + + IL_0000: nop + IL_0001: ldstr "some text" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldstr "x.txt" + IL_0011: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::OpenRead(string) + IL_0016: stloc.1 + .try + { + IL_0017: ldloc.1 + IL_0018: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte() + IL_001d: stloc.2 + IL_001e: leave.s IL_0036 + } // end .try + finally + { + IL_0020: ldloc.1 + IL_0021: isinst [mscorlib]System.IDisposable + IL_0026: stloc.3 + IL_0027: ldloc.3 + IL_0028: brfalse.s IL_0033 + + IL_002a: ldloc.3 + IL_002b: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0030: ldnull + IL_0031: pop + IL_0032: endfinally + + IL_0033: ldnull + IL_0034: pop + IL_0035: endfinally + } // end handler + + IL_0036: ldloc.2 + IL_0037: stloc.0 + IL_0038: nop + IL_0039: ldstr "x.txt" + IL_003e: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::OpenRead(string) + IL_0043: stloc.1 + .try + { + IL_0044: ldloc.1 + IL_0045: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte() + IL_004a: stloc.s 5 + IL_004c: ldloc.1 + IL_004d: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte() + IL_0052: stloc.s 4 + IL_0054: leave.s IL_006c + } // end .try + finally + { + IL_0056: ldloc.1 + IL_0057: isinst [mscorlib]System.IDisposable + IL_005c: stloc.3 + IL_005d: ldloc.3 + IL_005e: brfalse.s IL_0069 + + IL_0060: ldloc.3 + IL_0061: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0066: ldnull + IL_0067: pop + IL_0068: endfinally + + IL_0069: ldnull + IL_006a: pop + IL_006b: endfinally + } // end handler + + IL_006c: ldloc.s 4 + IL_006e: stloc.2 + IL_006f: ldstr "read: {0}, {1}" + IL_0074: ldloc.0 + IL_0075: box [mscorlib]System.Int32 + IL_007a: ldloc.2 + IL_007b: box [mscorlib]System.Int32 + IL_0080: call void [mscorlib]System.Console::WriteLine(string, object, object) + IL_0085: ret + } // end of method FSharpUsingPatterns::sample5 + +} // end of class FSharpUsingPatterns diff --git a/ICSharpCode.Decompiler/Tests/FSharpPatterns/TestHelpers.cs b/ICSharpCode.Decompiler/Tests/FSharpPatterns/TestHelpers.cs new file mode 100644 index 000000000..dc6cd0bc4 --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/FSharpPatterns/TestHelpers.cs @@ -0,0 +1,87 @@ +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 ilasm = new Lazy(() => ToolLocator.FindTool("ilasm.exe")); + static Lazy ildasm = new Lazy(() => 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 `` where the part after tmp is randomly generated. + // remove this from the ast to simplify the diff + var startupCodeNode = decompiler.SyntaxTree.Children.OfType().SingleOrDefault(d => d.Name.StartsWith(" Path.Combine(dir, fileName)).FirstOrDefault(File.Exists); + } + + private static IEnumerable 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 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; + } + } + } + } +} diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj index adf4d5607..4212a76b6 100644 --- a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj @@ -49,10 +49,26 @@ ..\..\packages\DiffLib.1.0.0.55\lib\net35-Client\DiffLib.dll + + ..\..\packages\NUnitTestAdapter.2.0.0\lib\nunit.core.dll + False + + + ..\..\packages\NUnitTestAdapter.2.0.0\lib\nunit.core.interfaces.dll + False + False .\nunit.framework.dll + + ..\..\packages\NUnitTestAdapter.2.0.0\lib\nunit.util.dll + False + + + ..\..\packages\NUnitTestAdapter.2.0.0\lib\NUnit.VisualStudio.TestAdapter.dll + False + 3.5 @@ -69,13 +85,19 @@ + + + + + + @@ -112,28 +134,40 @@ - {D68133BD-1E63-496E-9EDE-4FBDBF77B486} + {d68133bd-1e63-496e-9ede-4fbdbf77b486} Mono.Cecil + + {63e6915c-7ea4-4d76-ab28-0d7191eea626} + Mono.Cecil.Pdb + - {53DCA265-3C3C-42F9-B647-F72BA678122B} + {53dca265-3c3c-42f9-b647-f72ba678122b} ICSharpCode.NRefactory.CSharp - {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} + {3b2a5653-ec97-4001-bb9b-d90f1af2c371} ICSharpCode.NRefactory - {984CC812-9470-4A13-AFF9-CC44068D666C} + {984cc812-9470-4a13-aff9-cc44068d666c} ICSharpCode.Decompiler - + + + + + + + + + \ No newline at end of file diff --git a/ICSharpCode.Decompiler/Tests/NotUsingBlock.cs b/ICSharpCode.Decompiler/Tests/NotUsingBlock.cs new file mode 100644 index 000000000..b20750ac8 --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/NotUsingBlock.cs @@ -0,0 +1,28 @@ +using System; +using System.IO; + +namespace ICSharpCode.Decompiler.Tests +{ + public class NotUsingBlock + { + public void ThisIsNotAUsingBlock() + { + object obj = File.OpenRead("..."); + IDisposable disposable; + try + { + (obj as FileStream).WriteByte(2); + Console.WriteLine("some text"); + } + finally + { + disposable = (obj as IDisposable); + if (disposable != null) + { + disposable.Dispose(); + } + } + Console.WriteLine(disposable); + } + } +} diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs index 215725b0a..1af5bec0c 100644 --- a/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -180,13 +180,20 @@ namespace ICSharpCode.Decompiler.Tests { TestFile(@"..\..\Tests\YieldReturn.cs"); } - + [Test] public void TypeAnalysis() { TestFile(@"..\..\Tests\TypeAnalysisTests.cs"); } - + + // see https://github.com/icsharpcode/ILSpy/pull/671 + [Test] + public void NotUsingBlock() + { + TestFile(@"..\..\Tests\NotUsingBlock.cs"); + } + static void TestFile(string fileName, bool useDebug = false, int compilerVersion = 4) { AssertRoundtripCode(fileName, optimize: false, useDebug: useDebug, compilerVersion: compilerVersion); diff --git a/ICSharpCode.Decompiler/Tests/packages.config b/ICSharpCode.Decompiler/Tests/packages.config index 4d992b424..44cab4495 100644 --- a/ICSharpCode.Decompiler/Tests/packages.config +++ b/ICSharpCode.Decompiler/Tests/packages.config @@ -1,4 +1,5 @@  + \ No newline at end of file diff --git a/ILSpy.AddIn/CodeElementXmlDocKeyProvider.cs b/ILSpy.AddIn/CodeElementXmlDocKeyProvider.cs new file mode 100644 index 000000000..b7210e918 --- /dev/null +++ b/ILSpy.AddIn/CodeElementXmlDocKeyProvider.cs @@ -0,0 +1,269 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ICSharpCode.ILSpy.AddIn +{ + /// + /// Provides XML documentation tags for Visual Studio CodeElements. + /// + /// + /// Used to support the "/navigateTo" command line option when opening ILSpy. Must match + /// the logic of ICSharpCode.ILSpy.XmlDoc.XmlDocKeyProvider, which does the same thing for + /// a Mono.Cecil.MemberReference. See "ID string format" in Appendix A of the C# language + /// specification for formatting requirements, and Samples/ILSpyAddInSamples.cs for examples. + /// + public static class CodeElementXmlDocKeyProvider + { + #region GetKey + public static string GetKey(EnvDTE.CodeElement member) + { + StringBuilder b = new StringBuilder(); + if ((member.Kind == EnvDTE.vsCMElement.vsCMElementDelegate) || + (member.Kind == EnvDTE.vsCMElement.vsCMElementEnum) || + (member.Kind == EnvDTE.vsCMElement.vsCMElementInterface) || + (member.Kind == EnvDTE.vsCMElement.vsCMElementStruct) || + (member.Kind == EnvDTE.vsCMElement.vsCMElementClass)) { + b.Append("T:"); + AppendTypeName(b, member.FullName, true, false); + } + else if (member.Kind == EnvDTE.vsCMElement.vsCMElementNamespace){ + b.Append("N:"); + b.Append(member.FullName); + } + else { + if (member.Kind == EnvDTE.vsCMElement.vsCMElementVariable) + b.Append("F:"); + else if (member.Kind == EnvDTE.vsCMElement.vsCMElementProperty) + b.Append("P:"); + else if (member.Kind == EnvDTE.vsCMElement.vsCMElementEvent) + b.Append("E:"); + else if (member.Kind == EnvDTE.vsCMElement.vsCMElementFunction) + b.Append("M:"); + + int nameIndex = member.FullName.LastIndexOf(member.Name); + string typeName = member.FullName.Substring(0, nameIndex - 1); + string memberName = member.FullName.Substring(nameIndex); + + // Name substitutions for special cases. + if (member.Kind == EnvDTE.vsCMElement.vsCMElementFunction) { + EnvDTE80.CodeFunction2 mr = (EnvDTE80.CodeFunction2)member; + if (mr.FunctionKind == EnvDTE.vsCMFunction.vsCMFunctionConstructor) { + memberName = memberName.Replace(member.Name, "#ctor"); + } + else if (mr.FunctionKind == EnvDTE.vsCMFunction.vsCMFunctionDestructor) { + memberName = memberName.Replace(member.Name, "Finalize"); + } + else if (mr.FunctionKind == EnvDTE.vsCMFunction.vsCMFunctionOperator) { + if (memberName.StartsWith("implicit operator")) { + memberName = "op_Implicit"; + } + else if (memberName.StartsWith("explicit operator")) { + memberName = "op_Explicit"; + } + else { + // NRefactory has a handy mapping we can make use of, just need to extract the operator sybol first. + string[] memberNameWords = member.Name.Split(' '); + if (memberNameWords.Length >= 2) { + string operatorSymbol = memberNameWords[1]; + string operatorName = ICSharpCode.NRefactory.MonoCSharp.Operator.GetMetadataName(operatorSymbol); + if (operatorName != null) { + memberName = memberName.Replace(member.Name, operatorName); + } + } + } + } + } + else if (member.Kind == EnvDTE.vsCMElement.vsCMElementProperty) { + if (member.Name == "this") { + memberName = memberName.Replace(member.Name, "Item"); + } + } + + string[] genericTypeParameters = AppendTypeName(b, typeName, true, false); + b.Append('.'); + string[] genericMethodParameters = AppendTypeName(b, memberName.Replace('.', '#'), true, true); + EnvDTE.CodeElements parameters; + EnvDTE.CodeTypeRef explicitReturnType = null; + if (member.Kind == EnvDTE.vsCMElement.vsCMElementProperty) { + parameters = ((EnvDTE.CodeProperty)member).Getter.Parameters; + } + else if (member.Kind == EnvDTE.vsCMElement.vsCMElementFunction) { + EnvDTE80.CodeFunction2 mr = (EnvDTE80.CodeFunction2)member; + parameters = mr.Parameters; + if (memberName == "op_Implicit" || memberName == "op_Explicit") { + explicitReturnType = mr.Type; + } + } + else { + parameters = null; + } + if (parameters != null && parameters.Count > 0) { + b.Append('('); + int i = 0; + foreach (EnvDTE80.CodeParameter2 parameter in parameters) { + if (i > 0) b.Append(','); + AppendParameterTypeName(b, parameter, genericTypeParameters, genericMethodParameters); + ++i; + } + b.Append(')'); + } + if (explicitReturnType != null) { + b.Append('~'); + AppendTypeName(b, explicitReturnType.AsFullName, true, false); + } + } + return b.ToString(); + } + + static string[] AppendTypeName(StringBuilder b, string typeName, bool appendGenericParameterCount, bool isMethod) + { + List allGenericParameters = new List(); + StringBuilder genericParameterName = new StringBuilder(); + + bool inGenericParameters = false; + int genericParameterCount = 0; + foreach (char ch in typeName) { + if (inGenericParameters) { + switch (ch) { + case ',': + ++genericParameterCount; + allGenericParameters.Add(genericParameterName.ToString()); + genericParameterName.Clear(); + break; + case '>': + ++genericParameterCount; + allGenericParameters.Add(genericParameterName.ToString()); + genericParameterName.Clear(); + if (appendGenericParameterCount) { + b.Append(genericParameterCount); + } + inGenericParameters = false; + break; + case ' ': + break; + default: + genericParameterName.Append(ch); + break; + } + } + else { + switch (ch) { + case '<': + if (appendGenericParameterCount) { + b.Append('`'); + if (isMethod) { + b.Append('`'); + } + } + inGenericParameters = true; + genericParameterCount = 0; + break; + case '[': + case ']': + break; + default: + b.Append(ch); + break; + } + } + } + + return allGenericParameters.ToArray(); + } + + private static void AppendParameterTypeName(StringBuilder b, EnvDTE80.CodeParameter2 parameter, string[] genericTypeParameters, string[] genericMethodParameters) + { + EnvDTE80.CodeTypeRef2 parameterTypeRef = (EnvDTE80.CodeTypeRef2)parameter.Type; + string parameterTypeString = parameterTypeRef.AsFullName; + + int substringStart = 0; + for (int i = 0; i < parameterTypeString.Length; ++i) { + char ch = parameterTypeString[i]; + switch (ch) { + case '<': + AppendParameterTypeSubstring(b, parameterTypeString, substringStart, i, genericTypeParameters, genericMethodParameters); + substringStart = i + 1; + b.Append('{'); + break; + case '>': + AppendParameterTypeSubstring(b, parameterTypeString, substringStart, i, genericTypeParameters, genericMethodParameters); + substringStart = i + 1; + b.Append('}'); + break; + + case '[': + AppendParameterTypeSubstring(b, parameterTypeString, substringStart, i, genericTypeParameters, genericMethodParameters); + b.Append('['); + + // Skip ahead to the closing bracket, counting commas to determine array rank. + int rank = 1; + do { + ++i; + ch = parameterTypeString[i]; + if (ch == ',') { + ++rank; + } + } + while (ch != ']'); + substringStart = i + 1; + + // For multi-dimensional arrays, add "0:" default array bounds. Note that non-standard bounds are not possible via C# declaration. + if (rank > 1) { + for (int r = 0; r < rank; ++r) { + if (r != 0) { + b.Append(','); + } + b.Append("0:"); + } + } + + b.Append(']'); + break; + + case ',': + AppendParameterTypeSubstring(b, parameterTypeString, substringStart, i, genericTypeParameters, genericMethodParameters); + substringStart = i + 1; + // Skip space after comma if present. (e.g. System.Collections.Generic.KeyValuePair`2{System.String,System.String}.) + if (parameterTypeString[substringStart] == ' ') { + ++substringStart; + } + b.Append(','); + break; + } + } + + AppendParameterTypeSubstring(b, parameterTypeString, substringStart, parameterTypeString.Length, genericTypeParameters, genericMethodParameters); + + // Append ref / out indicator if needed. + if ((parameter.ParameterKind == EnvDTE80.vsCMParameterKind.vsCMParameterKindRef) || + (parameter.ParameterKind == EnvDTE80.vsCMParameterKind.vsCMParameterKindOut)) { + b.Append('@'); + } + + // Note there is no need to append a '*' for pointers, as this is included in the full name of the type. + // Multi-dimensional and jagged arrays are handled above during string parsing. + } + + private static void AppendParameterTypeSubstring(StringBuilder b, string parameterTypeString, int substringStart, int substringStop, string[] genericTypeParameters, string[] genericMethodParameters) + { + if (substringStart < substringStop) { + string substring = parameterTypeString.Substring(substringStart, substringStop - substringStart); + int indexOfGenericTypeParameter = Array.IndexOf(genericTypeParameters, substring); + int indexOfGenericMethodParameter = Array.IndexOf(genericMethodParameters, substring); + if (indexOfGenericTypeParameter >= 0) { + b.Append("`"); + b.Append(indexOfGenericTypeParameter); + } + else if (indexOfGenericMethodParameter >= 0) { + b.Append("``"); + b.Append(indexOfGenericMethodParameter); + } + else { + b.Append(substring); + } + } + } + #endregion + } +} \ No newline at end of file diff --git a/ILSpy.AddIn/ILSpy.AddIn.csproj b/ILSpy.AddIn/ILSpy.AddIn.csproj index b2cc7f4d6..b56546a98 100644 --- a/ILSpy.AddIn/ILSpy.AddIn.csproj +++ b/ILSpy.AddIn/ILSpy.AddIn.csproj @@ -138,6 +138,7 @@ + True True @@ -147,6 +148,7 @@ + diff --git a/ILSpy.AddIn/ILSpyAddIn.vsct b/ILSpy.AddIn/ILSpyAddIn.vsct index 69af34274..0105c1a53 100644 --- a/ILSpy.AddIn/ILSpyAddIn.vsct +++ b/ILSpy.AddIn/ILSpyAddIn.vsct @@ -47,6 +47,10 @@ + + + + @@ -77,6 +81,14 @@ + + + - A new ILSpy version is available. - + A new ILSpy version is available. + diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 493a55d70..1cdfe40ed 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -257,7 +257,15 @@ namespace ICSharpCode.ILSpy void HandleCommandLineArgumentsAfterShowList(CommandLineArguments args) { - if (args.NavigateTo != null) { + // if a SaveDirectory is given, do not start a second concurrent decompilation + // by executing JumpoToReference (leads to https://github.com/icsharpcode/ILSpy/issues/710) + if (!string.IsNullOrEmpty(args.SaveDirectory)) { + foreach (var x in commandLineLoadedAssemblies) { + x.ContinueWhenLoaded((Task moduleTask) => { + OnExportAssembly(moduleTask, args.SaveDirectory); + }, TaskScheduler.FromCurrentSynchronizationContext()); + } + } else if (args.NavigateTo != null) { bool found = false; if (args.NavigateTo.StartsWith("N:", StringComparison.Ordinal)) { string namespaceName = args.NavigateTo.Substring(2); @@ -300,13 +308,6 @@ namespace ICSharpCode.ILSpy SearchPane.Instance.SearchTerm = args.Search; SearchPane.Instance.Show(); } - if (!string.IsNullOrEmpty(args.SaveDirectory)) { - foreach (var x in commandLineLoadedAssemblies) { - x.ContinueWhenLoaded( (Task moduleTask) => { - OnExportAssembly(moduleTask, args.SaveDirectory); - }, TaskScheduler.FromCurrentSynchronizationContext()); - } - } commandLineLoadedAssemblies.Clear(); // clear references once we don't need them anymore } @@ -398,27 +399,44 @@ namespace ICSharpCode.ILSpy #region Update Check string updateAvailableDownloadUrl; - void ShowMessageIfUpdatesAvailableAsync(ILSpySettings spySettings) + public void ShowMessageIfUpdatesAvailableAsync(ILSpySettings spySettings, bool forceCheck = false) { - AboutPage.CheckForUpdatesIfEnabledAsync(spySettings).ContinueWith( - delegate (Task task) { - if (task.Result != null) { - updateAvailableDownloadUrl = task.Result; - updateAvailablePanel.Visibility = Visibility.Visible; - } - }, - TaskScheduler.FromCurrentSynchronizationContext() - ); + Task result; + if (forceCheck) { + result = AboutPage.CheckForUpdatesAsync(spySettings); + } else { + result = AboutPage.CheckForUpdatesIfEnabledAsync(spySettings); + } + result.ContinueWith(task => AdjustUpdateUIAfterCheck(task, forceCheck), TaskScheduler.FromCurrentSynchronizationContext()); } - void updateAvailablePanelCloseButtonClick(object sender, RoutedEventArgs e) + void updatePanelCloseButtonClick(object sender, RoutedEventArgs e) { - updateAvailablePanel.Visibility = Visibility.Collapsed; + updatePanel.Visibility = Visibility.Collapsed; } - void downloadUpdateButtonClick(object sender, RoutedEventArgs e) + void downloadOrCheckUpdateButtonClick(object sender, RoutedEventArgs e) { - Process.Start(updateAvailableDownloadUrl); + if (updateAvailableDownloadUrl != null) { + Process.Start(updateAvailableDownloadUrl); + } else { + updatePanel.Visibility = Visibility.Collapsed; + AboutPage.CheckForUpdatesAsync(spySettings ?? ILSpySettings.Load()) + .ContinueWith(task => AdjustUpdateUIAfterCheck(task, true), TaskScheduler.FromCurrentSynchronizationContext()); + } + } + + void AdjustUpdateUIAfterCheck(Task task, bool displayMessage) + { + updateAvailableDownloadUrl = task.Result; + updatePanel.Visibility = displayMessage ? Visibility.Visible : Visibility.Collapsed; + if (task.Result != null) { + updatePanelMessage.Text = "A new ILSpy version is available."; + downloadOrCheckUpdateButton.Content = "Download"; + } else { + updatePanelMessage.Text = "No update for ILSpy found."; + downloadOrCheckUpdateButton.Content = "Check again"; + } } #endregion @@ -531,6 +549,14 @@ namespace ICSharpCode.ILSpy } } } + + public void SelectNodes(IEnumerable nodes) + { + if (nodes.Any() && nodes.All(n => !n.AncestorsAndSelf().Any(a => a.IsHidden))) { + treeView.FocusNode(nodes.First()); + treeView.SetSelectedNodes(nodes); + } + } /// /// Retrieves a node using the .ToString() representations of its ancestors. @@ -557,7 +583,7 @@ namespace ICSharpCode.ILSpy /// /// Gets the .ToString() representation of the node's ancestors. /// - public string[] GetPathForNode(SharpTreeNode node) + public static string[] GetPathForNode(SharpTreeNode node) { if (node == null) return null; diff --git a/ILSpy/Options/OptionsDialog.xaml.cs b/ILSpy/Options/OptionsDialog.xaml.cs index 40043fac5..8bd96661f 100644 --- a/ILSpy/Options/OptionsDialog.xaml.cs +++ b/ILSpy/Options/OptionsDialog.xaml.cs @@ -89,7 +89,7 @@ namespace ICSharpCode.ILSpy.Options public int Order { get; set; } } - [ExportMainMenuCommand(Menu = "_View", Header = "_Options", MenuCategory = "Options", MenuOrder = 999)] + [ExportMainMenuCommand(Menu = "_View", Header = "_Options...", MenuCategory = "Options", MenuOrder = 999)] sealed class ShowOptionsCommand : SimpleCommand { public override void Execute(object parameter) diff --git a/ILSpy/Properties/AssemblyInfo.template.cs b/ILSpy/Properties/AssemblyInfo.template.cs index c4be7cef5..821a569e7 100644 --- a/ILSpy/Properties/AssemblyInfo.template.cs +++ b/ILSpy/Properties/AssemblyInfo.template.cs @@ -15,7 +15,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription(".NET assembly inspector and decompiler")] [assembly: AssemblyCompany("ic#code")] [assembly: AssemblyProduct("ILSpy")] -[assembly: AssemblyCopyright("Copyright 2011-2014 AlphaSierraPapa for the SharpDevelop Team")] +[assembly: AssemblyCopyright("Copyright 2011-2016 AlphaSierraPapa for the SharpDevelop Team")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -35,8 +35,8 @@ using System.Runtime.InteropServices; internal static class RevisionClass { public const string Major = "2"; - public const string Minor = "3"; - public const string Build = "1"; + public const string Minor = "4"; + public const string Build = "0"; public const string Revision = "$INSERTREVISION$"; public const string VersionName = null; diff --git a/ILSpy/SearchPane.cs b/ILSpy/SearchPane.cs index 27177f7e7..20ebec022 100644 --- a/ILSpy/SearchPane.cs +++ b/ILSpy/SearchPane.cs @@ -58,10 +58,15 @@ namespace ICSharpCode.ILSpy private SearchPane() { InitializeComponent(); + searchModeComboBox.Items.Add(new { Image = Images.Library, Name = "Types and Members" }); searchModeComboBox.Items.Add(new { Image = Images.Class, Name = "Type" }); searchModeComboBox.Items.Add(new { Image = Images.Property, Name = "Member" }); + searchModeComboBox.Items.Add(new { Image = Images.Method, Name = "Method" }); + searchModeComboBox.Items.Add(new { Image = Images.Field, Name = "Field" }); + searchModeComboBox.Items.Add(new { Image = Images.Property, Name = "Property" }); + searchModeComboBox.Items.Add(new { Image = Images.Event, Name = "Event" }); searchModeComboBox.Items.Add(new { Image = Images.Literal, Name = "Constant" }); - searchModeComboBox.SelectedIndex = (int)SearchMode.Type; + searchModeComboBox.SelectedIndex = (int)SearchMode.TypeAndMember; ContextMenuProvider.Add(listBox); MainWindow.Instance.CurrentAssemblyListChanged += MainWindow_Instance_CurrentAssemblyListChanged; @@ -249,23 +254,49 @@ namespace ICSharpCode.ILSpy AbstractSearchStrategy GetSearchStrategy(SearchMode mode, string[] terms) { if (terms.Length == 1) { - if (terms[0].StartsWith("t:")) + if (terms[0].StartsWith("tm:", StringComparison.Ordinal)) + return new TypeAndMemberSearchStrategy(terms[0].Substring(3)); + + if (terms[0].StartsWith("t:", StringComparison.Ordinal)) return new TypeSearchStrategy(terms[0].Substring(2)); - if (terms[0].StartsWith("m:")) + if (terms[0].StartsWith("m:", StringComparison.Ordinal)) return new MemberSearchStrategy(terms[0].Substring(2)); - if (terms[0].StartsWith("c:")) + if (terms[0].StartsWith("md:", StringComparison.Ordinal)) + return new MemberSearchStrategy(terms[0].Substring(3), MemberSearchKind.Method); + + if (terms[0].StartsWith("f:", StringComparison.Ordinal)) + return new MemberSearchStrategy(terms[0].Substring(2), MemberSearchKind.Field); + + if (terms[0].StartsWith("p:", StringComparison.Ordinal)) + return new MemberSearchStrategy(terms[0].Substring(2), MemberSearchKind.Property); + + if (terms[0].StartsWith("e:", StringComparison.Ordinal)) + return new MemberSearchStrategy(terms[0].Substring(2), MemberSearchKind.Event); + + if (terms[0].StartsWith("c:", StringComparison.Ordinal)) return new LiteralSearchStrategy(terms[0].Substring(2)); } - switch (mode) { + switch (mode) + { + case SearchMode.TypeAndMember: + return new TypeAndMemberSearchStrategy(terms); case SearchMode.Type: return new TypeSearchStrategy(terms); case SearchMode.Member: return new MemberSearchStrategy(terms); case SearchMode.Literal: return new LiteralSearchStrategy(terms); + case SearchMode.Method: + return new MemberSearchStrategy(terms, MemberSearchKind.Method); + case SearchMode.Field: + return new MemberSearchStrategy(terms, MemberSearchKind.Field); + case SearchMode.Property: + return new MemberSearchStrategy(terms, MemberSearchKind.Property); + case SearchMode.Event: + return new MemberSearchStrategy(terms, MemberSearchKind.Event); } return null; @@ -293,7 +324,7 @@ namespace ICSharpCode.ILSpy } } - [ExportMainMenuCommand(Menu = "_View", Header = "_Search", MenuIcon = "Images/Find.png", MenuCategory = "ShowPane", MenuOrder = 100)] + [ExportMainMenuCommand(Menu = "_View", Header = "_Search...", MenuIcon = "Images/Find.png", MenuCategory = "View", MenuOrder = 100)] [ExportToolbarCommand(ToolTip = "Search (Ctrl+Shift+F or Ctrl+E)", ToolbarIcon = "Images/Find.png", ToolbarCategory = "View", ToolbarOrder = 100)] sealed class ShowSearchCommand : CommandWrapper { @@ -308,8 +339,13 @@ namespace ICSharpCode.ILSpy public enum SearchMode { + TypeAndMember, Type, Member, + Method, + Field, + Property, + Event, Literal } } \ No newline at end of file diff --git a/ILSpy/SearchPane.xaml b/ILSpy/SearchPane.xaml index 37a6ff210..756ee5e92 100644 --- a/ILSpy/SearchPane.xaml +++ b/ILSpy/SearchPane.xaml @@ -19,7 +19,7 @@ Text="{Binding SearchTerm, ElementName=self}" ToolTip="Search" UpdateDelay="0:0:0.1" WatermarkColor="Gray" WatermarkText="Search for t:TypeName, m:Member or c:Constant; use /reg(ular)?Ex(pressions)?/ or both - t:/Type(Name)?/..." /> - + diff --git a/ILSpy/SearchStrategies.cs b/ILSpy/SearchStrategies.cs index 302f261db..7d0912a0b 100644 --- a/ILSpy/SearchStrategies.cs +++ b/ILSpy/SearchStrategies.cs @@ -21,7 +21,7 @@ namespace ICSharpCode.ILSpy { if (terms.Length == 1 && terms[0].Length > 2) { var search = terms[0]; - if (search.StartsWith("/") && search.EndsWith("/") && search.Length > 4) + if (search.StartsWith("/", StringComparison.Ordinal) && search.EndsWith("/", StringComparison.Ordinal) && search.Length > 4) regex = SafeNewRegex(search.Substring(1, search.Length - 2)); terms[0] = search; @@ -38,6 +38,7 @@ namespace ICSharpCode.ILSpy for (int i = 0; i < searchTerm.Length; ++i) { // How to handle overlapping matches? var term = searchTerm[i]; + if (string.IsNullOrEmpty(term)) continue; switch (term[0]) { case '+': // must contain @@ -302,31 +303,48 @@ namespace ICSharpCode.ILSpy } } + enum MemberSearchKind + { + All, + Field, + Property, + Event, + Method + } + class MemberSearchStrategy : AbstractSearchStrategy { - public MemberSearchStrategy(params string[] terms) + MemberSearchKind searchKind; + + public MemberSearchStrategy(string term, MemberSearchKind searchKind = MemberSearchKind.All) + : this(new[] { term }, searchKind) + { + } + + public MemberSearchStrategy(string[] terms, MemberSearchKind searchKind = MemberSearchKind.All) : base(terms) { + this.searchKind = searchKind; } protected override bool IsMatch(FieldDefinition field) { - return IsMatch(field.Name); + return (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Field) && IsMatch(field.Name); } protected override bool IsMatch(PropertyDefinition property) { - return IsMatch(property.Name); + return (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Property) && IsMatch(property.Name); } protected override bool IsMatch(EventDefinition ev) { - return IsMatch(ev.Name); + return (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Event) && IsMatch(ev.Name); } protected override bool IsMatch(MethodDefinition m) { - return IsMatch(m.Name); + return (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Method) && IsMatch(m.Name); } } @@ -355,4 +373,53 @@ namespace ICSharpCode.ILSpy } } + class TypeAndMemberSearchStrategy : AbstractSearchStrategy + { + public TypeAndMemberSearchStrategy(params string[] terms) + : base(terms) + { + } + + public override void Search(TypeDefinition type, Language language, Action addResult) + { + if (IsMatch(type.Name) || IsMatch(type.FullName)) + { + addResult(new SearchResult + { + Member = type, + Image = TypeTreeNode.GetIcon(type), + Name = language.TypeToString(type, includeNamespace: false), + LocationImage = type.DeclaringType != null ? TypeTreeNode.GetIcon(type.DeclaringType) : Images.Namespace, + Location = type.DeclaringType != null ? language.TypeToString(type.DeclaringType, includeNamespace: true) : type.Namespace + }); + } + + foreach (TypeDefinition nestedType in type.NestedTypes) + { + Search(nestedType, language, addResult); + } + + base.Search(type, language, addResult); + } + + protected override bool IsMatch(FieldDefinition field) + { + return IsMatch(field.Name); + } + + protected override bool IsMatch(PropertyDefinition property) + { + return IsMatch(property.Name); + } + + protected override bool IsMatch(EventDefinition ev) + { + return IsMatch(ev.Name); + } + + protected override bool IsMatch(MethodDefinition m) + { + return IsMatch(m.Name); + } + } } diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index e7a502094..b4a57b0c1 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -233,6 +233,9 @@ namespace ICSharpCode.ILSpy.TextView { if (waitAdorner.Visibility != Visibility.Visible) { waitAdorner.Visibility = Visibility.Visible; + // Work around a WPF bug by setting IsIndeterminate only while the progress bar is visible. + // https://github.com/icsharpcode/ILSpy/issues/593 + progressBar.IsIndeterminate = true; waitAdorner.BeginAnimation(OpacityProperty, new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(0.5)), FillBehavior.Stop)); var taskBar = MainWindow.Instance.TaskbarItemInfo; if (taskBar != null) { @@ -260,6 +263,7 @@ namespace ICSharpCode.ILSpy.TextView if (currentCancellationTokenSource == myCancellationTokenSource) { currentCancellationTokenSource = null; waitAdorner.Visibility = Visibility.Collapsed; + progressBar.IsIndeterminate = false; var taskBar = MainWindow.Instance.TaskbarItemInfo; if (taskBar != null) { taskBar.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None; diff --git a/ILSpy/TextView/DecompilerTextView.xaml b/ILSpy/TextView/DecompilerTextView.xaml index 022ab422d..722784b87 100644 --- a/ILSpy/TextView/DecompilerTextView.xaml +++ b/ILSpy/TextView/DecompilerTextView.xaml @@ -19,7 +19,7 @@ Decompiling... - + diff --git a/ILSpy/TextView/EditorCommands.cs b/ILSpy/TextView/EditorCommands.cs index 8615ee896..2907453d2 100644 --- a/ILSpy/TextView/EditorCommands.cs +++ b/ILSpy/TextView/EditorCommands.cs @@ -16,15 +16,10 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - namespace ICSharpCode.ILSpy.TextView { - [ExportContextMenuEntryAttribute(Header = "Copy", Category = "Editor")] - internal sealed class CopyContextMenuEntry : IContextMenuEntry + [ExportContextMenuEntry(Header = "Copy", Category = "Editor")] + sealed class CopyContextMenuEntry : IContextMenuEntry { public bool IsVisible(TextViewContext context) { @@ -41,4 +36,23 @@ namespace ICSharpCode.ILSpy.TextView context.TextView.textEditor.Copy(); } } + + [ExportContextMenuEntry(Header = "Select All", Category = "Editor")] + sealed class SelectAllContextMenuEntry : IContextMenuEntry + { + public bool IsVisible(TextViewContext context) + { + return context.TextView != null; + } + + public bool IsEnabled(TextViewContext context) + { + return context.TextView != null; + } + + public void Execute(TextViewContext context) + { + context.TextView.textEditor.SelectAll(); + } + } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs b/ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs index b9154ead7..23e5dceb2 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs @@ -24,7 +24,7 @@ using Mono.Cecil; namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { - [ExportContextMenuEntryAttribute(Header = "Analyze", Icon = "images/Search.png")] + [ExportContextMenuEntry(Header = "Analyze", Icon = "images/Search.png", Category = "Analyze", Order = 100)] internal sealed class AnalyzeContextMenuEntry : IContextMenuEntry { public bool IsVisible(TextViewContext context) diff --git a/ILSpy/TreeNodes/Analyzer/RemoveAnalyzeContextMenuEntry.cs b/ILSpy/TreeNodes/Analyzer/RemoveAnalyzeContextMenuEntry.cs index 3bcbe782f..908c95888 100644 --- a/ILSpy/TreeNodes/Analyzer/RemoveAnalyzeContextMenuEntry.cs +++ b/ILSpy/TreeNodes/Analyzer/RemoveAnalyzeContextMenuEntry.cs @@ -20,7 +20,7 @@ using System.Linq; namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { - [ExportContextMenuEntryAttribute(Header = "Remove", Icon = "images/Delete.png")] + [ExportContextMenuEntry(Header = "Remove", Icon = "images/Delete.png", Category = "Analyze", Order = 200)] internal sealed class RemoveAnalyzeContextMenuEntry : IContextMenuEntry { public bool IsVisible(TextViewContext context) diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index 89a17eae6..b7c256507 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -293,7 +293,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - [ExportContextMenuEntryAttribute(Header = "_Remove", Icon = "images/Delete.png")] + [ExportContextMenuEntry(Header = "_Remove", Icon = "images/Delete.png")] sealed class RemoveAssembly : IContextMenuEntry { public bool IsVisible(TextViewContext context) @@ -318,7 +318,39 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - [ExportContextMenuEntryAttribute(Header = "_Load Dependencies")] + [ExportContextMenuEntry(Header = "_Reload", Icon = "images/Refresh.png")] + sealed class ReloadAssembly : IContextMenuEntry + { + public bool IsVisible(TextViewContext context) + { + if (context.SelectedTreeNodes == null) + return false; + return context.SelectedTreeNodes.All(n => n is AssemblyTreeNode); + } + + public bool IsEnabled(TextViewContext context) + { + return true; + } + + public void Execute(TextViewContext context) + { + if (context.SelectedTreeNodes == null) + return; + var paths = new List(); + using (context.TreeView.LockUpdates()) { + foreach (var node in context.SelectedTreeNodes) { + paths.Add(MainWindow.GetPathForNode(node)); + var la = ((AssemblyTreeNode)node).LoadedAssembly; + la.AssemblyList.ReloadAssembly(la.FileName); + } + } + MainWindow.Instance.SelectNodes(paths.Select(p => MainWindow.Instance.FindNodeByPath(p, true)).ToArray()); + MainWindow.Instance.RefreshDecompiledView(); + } + } + + [ExportContextMenuEntry(Header = "_Load Dependencies", Category = "Dependencies")] sealed class LoadDependencies : IContextMenuEntry { public bool IsVisible(TextViewContext context) @@ -349,7 +381,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - [ExportContextMenuEntryAttribute(Header = "_Add To Main List")] + [ExportContextMenuEntry(Header = "_Add To Main List", Category = "Dependencies")] sealed class AddToMainList : IContextMenuEntry { public bool IsVisible(TextViewContext context) diff --git a/ILSpy/TreeNodes/MethodTreeNode.cs b/ILSpy/TreeNodes/MethodTreeNode.cs index d6b33bcb5..19cdb6586 100644 --- a/ILSpy/TreeNodes/MethodTreeNode.cs +++ b/ILSpy/TreeNodes/MethodTreeNode.cs @@ -69,7 +69,7 @@ namespace ICSharpCode.ILSpy.TreeNodes b.Append(") : "); b.Append(language.TypeToString(method.ReturnType, false, method.MethodReturnType)); b.Append(method.MetadataToken.ToSuffixString()); - return HighlightSearchMatch(method.Name, b.ToString()); + return HighlightSearchMatch(language.FormatMethodName(method), b.ToString()); } public override object Icon @@ -93,7 +93,7 @@ namespace ICSharpCode.ILSpy.TreeNodes if (method.IsSpecialName && (method.Name == ".ctor" || method.Name == ".cctor")) { - return Images.GetIcon(MemberIcon.Constructor, GetOverlayIcon(method.Attributes), false); + return Images.GetIcon(MemberIcon.Constructor, GetOverlayIcon(method.Attributes), method.IsStatic); } if (method.HasPInvokeInfo) diff --git a/ILSpy/TreeNodes/PropertyTreeNode.cs b/ILSpy/TreeNodes/PropertyTreeNode.cs index 76cd12dd5..f33efc206 100644 --- a/ILSpy/TreeNodes/PropertyTreeNode.cs +++ b/ILSpy/TreeNodes/PropertyTreeNode.cs @@ -62,7 +62,35 @@ namespace ICSharpCode.ILSpy.TreeNodes public static object GetText(PropertyDefinition property, Language language, bool? isIndexer = null) { - return HighlightSearchMatch(language.FormatPropertyName(property, isIndexer), " : " + language.TypeToString(property.PropertyType, false, property)); + string name = language.FormatPropertyName(property, isIndexer); + + var b = new System.Text.StringBuilder(); + if (property.HasParameters) + { + b.Append('('); + for (int i = 0; i < property.Parameters.Count; i++) + { + if (i > 0) + b.Append(", "); + b.Append(language.TypeToString(property.Parameters[i].ParameterType, false, property.Parameters[i])); + } + var method = property.GetMethod ?? property.SetMethod; + if (method.CallingConvention == MethodCallingConvention.VarArg) + { + if (property.HasParameters) + b.Append(", "); + b.Append("..."); + } + b.Append(") : "); + } + else + { + b.Append(" : "); + } + b.Append(language.TypeToString(property.PropertyType, false, property)); + b.Append(property.MetadataToken.ToSuffixString()); + + return HighlightSearchMatch(name, b.ToString()); } public override object Icon diff --git a/ILSpy/TreeNodes/SearchMsdnContextMenuEntry.cs b/ILSpy/TreeNodes/SearchMsdnContextMenuEntry.cs index 924c90d09..8632ee213 100644 --- a/ILSpy/TreeNodes/SearchMsdnContextMenuEntry.cs +++ b/ILSpy/TreeNodes/SearchMsdnContextMenuEntry.cs @@ -26,7 +26,7 @@ using ICSharpCode.ILSpy.TreeNodes.Analyzer; namespace ICSharpCode.ILSpy.TreeNodes { - [ExportContextMenuEntryAttribute(Header = "Search MSDN...", Icon = "images/Search.png")] + [ExportContextMenuEntry(Header = "Search MSDN...", Icon = "images/SearchMsdn.png", Order = 9999)] internal sealed class SearchMsdnContextMenuEntry : IContextMenuEntry { private static string msdnAddress = "http://msdn.microsoft.com/en-us/library/{0}"; diff --git a/ILSpy/VB/ILSpyEnvironmentProvider.cs b/ILSpy/VB/ILSpyEnvironmentProvider.cs index 78c46da12..725a26d9b 100644 --- a/ILSpy/VB/ILSpyEnvironmentProvider.cs +++ b/ILSpy/VB/ILSpyEnvironmentProvider.cs @@ -38,7 +38,7 @@ namespace ICSharpCode.ILSpy.VB //readonly CecilLoader loader = new CecilLoader(); - public string GetTypeNameForAttribute(ICSharpCode.NRefactory.CSharp.Attribute attribute) + public string GetTypeNameForAttribute(NRefactory.CSharp.Attribute attribute) { return attribute.Type.Annotations .OfType() @@ -63,7 +63,7 @@ namespace ICSharpCode.ILSpy.VB return SpecialType.UnknownType; } - public TypeKind GetTypeKindForAstType(ICSharpCode.NRefactory.CSharp.AstType type) + public TypeKind GetTypeKindForAstType(NRefactory.CSharp.AstType type) { var annotation = type.Annotation(); if (annotation == null) @@ -84,7 +84,7 @@ namespace ICSharpCode.ILSpy.VB return TypeKind.Unknown; } - public TypeCode ResolveExpression(ICSharpCode.NRefactory.CSharp.Expression expression) + public TypeCode ResolveExpression(NRefactory.CSharp.Expression expression) { var annotation = expression.Annotations.OfType().FirstOrDefault(); @@ -106,9 +106,9 @@ namespace ICSharpCode.ILSpy.VB return TypeCode.Object; } - public Nullable IsReferenceType(ICSharpCode.NRefactory.CSharp.Expression expression) + public bool? IsReferenceType(NRefactory.CSharp.Expression expression) { - if (expression is ICSharpCode.NRefactory.CSharp.NullReferenceExpression) + if (expression is NRefactory.CSharp.NullReferenceExpression) return true; var annotation = expression.Annotations.OfType().FirstOrDefault(); @@ -129,7 +129,7 @@ namespace ICSharpCode.ILSpy.VB foreach (var type in interfaces) { var def = type.Annotation().Resolve(); if (def == null) continue; - foreach (var method in def.Methods.Where(m => !m.Name.StartsWith("get_") && !m.Name.StartsWith("set_"))) { + foreach (var method in def.Methods.Where(m => !m.Name.StartsWith("get_", StringComparison.Ordinal) && !m.Name.StartsWith("set_", StringComparison.Ordinal))) { yield return new NRefactory.VB.Ast.InterfaceMemberSpecifier((NRefactory.VB.Ast.AstType)type.Clone(), method.Name); } @@ -144,14 +144,34 @@ namespace ICSharpCode.ILSpy.VB return expression.Annotation() != null; } - public bool IsMethodGroup(ICSharpCode.NRefactory.CSharp.Expression expression) + public bool IsMethodGroup(NRefactory.CSharp.Expression expression) { - var annotation = expression.Annotation(); - if (annotation != null) { - return true; + var methodInfo = expression.Annotation()?.Resolve(); + if (methodInfo != null) { + return !methodInfo.IsGetter && !methodInfo.IsSetter && !methodInfo.IsAddOn && !methodInfo.IsRemoveOn; } return false; } + + public NRefactory.CSharp.ParameterDeclaration[] GetParametersForProperty(NRefactory.CSharp.PropertyDeclaration property) + { + var propInfo = property.Annotation(); + + if (propInfo == null) + return new NRefactory.CSharp.ParameterDeclaration[0]; + + return propInfo.Parameters.Select(p => new NRefactory.CSharp.ParameterDeclaration(AstBuilder.ConvertType(p.ParameterType), p.Name, GetModifiers(p))).ToArray(); + } + + NRefactory.CSharp.ParameterModifier GetModifiers(ParameterDefinition p) + { + if (p.IsOut && p.IsIn) + return NRefactory.CSharp.ParameterModifier.Ref; + if (p.IsOut) + return NRefactory.CSharp.ParameterModifier.Out; + + return NRefactory.CSharp.ParameterModifier.None; + } } } diff --git a/ILSpy/VB/VBLanguage.cs b/ILSpy/VB/VBLanguage.cs index 4aad4a870..246dbd375 100644 --- a/ILSpy/VB/VBLanguage.cs +++ b/ILSpy/VB/VBLanguage.cs @@ -17,12 +17,10 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections; using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; using System.Linq; -using System.Resources; using System.Threading.Tasks; using System.Xml; @@ -30,12 +28,10 @@ using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Ast; using ICSharpCode.Decompiler.Ast.Transforms; using ICSharpCode.ILSpy.XmlDoc; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.TypeSystem.Implementation; -using CSharp = ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.VB; using ICSharpCode.NRefactory.VB.Visitors; using Mono.Cecil; +using CSharp = ICSharpCode.NRefactory.CSharp; namespace ICSharpCode.ILSpy.VB { @@ -321,84 +317,6 @@ namespace ICSharpCode.ILSpy.VB } #endregion - #region WriteResourceFilesInProject - IEnumerable> WriteResourceFilesInProject(LoadedAssembly assembly, DecompilationOptions options, HashSet directories) - { - //AppDomain bamlDecompilerAppDomain = null; - //try { - foreach (EmbeddedResource r in assembly.ModuleDefinition.Resources.OfType()) { - string fileName; - Stream s = r.GetResourceStream(); - s.Position = 0; - if (r.Name.EndsWith(".g.resources", StringComparison.OrdinalIgnoreCase)) { - IEnumerable rs = null; - try { - rs = new ResourceSet(s).Cast(); - } - catch (ArgumentException) { - } - if (rs != null && rs.All(e => e.Value is Stream)) { - foreach (var pair in rs) { - fileName = Path.Combine(((string)pair.Key).Split('/').Select(p => TextView.DecompilerTextView.CleanUpName(p)).ToArray()); - string dirName = Path.GetDirectoryName(fileName); - if (!string.IsNullOrEmpty(dirName) && directories.Add(dirName)) { - Directory.CreateDirectory(Path.Combine(options.SaveAsProjectDirectory, dirName)); - } - Stream entryStream = (Stream)pair.Value; - entryStream.Position = 0; - if (fileName.EndsWith(".baml", StringComparison.OrdinalIgnoreCase)) { - //MemoryStream ms = new MemoryStream(); - //entryStream.CopyTo(ms); - // TODO implement extension point -// var decompiler = Baml.BamlResourceEntryNode.CreateBamlDecompilerInAppDomain(ref bamlDecompilerAppDomain, assembly.FileName); -// string xaml = null; -// try { -// xaml = decompiler.DecompileBaml(ms, assembly.FileName, new ConnectMethodDecompiler(assembly), new AssemblyResolver(assembly)); -// } -// catch (XamlXmlWriterException) { } // ignore XAML writer exceptions -// if (xaml != null) { -// File.WriteAllText(Path.Combine(options.SaveAsProjectDirectory, Path.ChangeExtension(fileName, ".xaml")), xaml); -// yield return Tuple.Create("Page", Path.ChangeExtension(fileName, ".xaml")); -// continue; -// } - } - using (FileStream fs = new FileStream(Path.Combine(options.SaveAsProjectDirectory, fileName), FileMode.Create, FileAccess.Write)) { - entryStream.CopyTo(fs); - } - yield return Tuple.Create("Resource", fileName); - } - continue; - } - } - fileName = GetFileNameForResource(r.Name, directories); - using (FileStream fs = new FileStream(Path.Combine(options.SaveAsProjectDirectory, fileName), FileMode.Create, FileAccess.Write)) { - s.CopyTo(fs); - } - yield return Tuple.Create("EmbeddedResource", fileName); - } - //} - //finally { - // if (bamlDecompilerAppDomain != null) - // AppDomain.Unload(bamlDecompilerAppDomain); - //} - } - - string GetFileNameForResource(string fullName, HashSet directories) - { - string[] splitName = fullName.Split('.'); - string fileName = TextView.DecompilerTextView.CleanUpName(fullName); - for (int i = splitName.Length - 1; i > 0; i--) { - string ns = string.Join(".", splitName, 0, i); - if (directories.Contains(ns)) { - string name = string.Join(".", splitName, i, splitName.Length - i); - fileName = Path.Combine(ns, TextView.DecompilerTextView.CleanUpName(name)); - break; - } - } - return fileName; - } - #endregion - public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(method.DeclaringType, includeNamespace: true)); @@ -433,7 +351,7 @@ namespace ICSharpCode.ILSpy.VB public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: type); + AstBuilder codeDomBuilder = CreateAstBuilder(options, currentModule: type.Module); codeDomBuilder.AddType(type); RunTransformsAndGenerateCode(codeDomBuilder, output, options, type.Module); } @@ -483,7 +401,15 @@ namespace ICSharpCode.ILSpy.VB Settings = settings }); } - + + public override string FormatMethodName(MethodDefinition method) + { + if (method == null) + throw new ArgumentNullException("method"); + + return (method.IsConstructor) ? method.DeclaringType.Name : method.Name; + } + public override string FormatTypeName(TypeDefinition type) { if (type == null) diff --git a/ILSpy/VB/VBTextOutputFormatter.cs b/ILSpy/VB/VBTextOutputFormatter.cs index 38f89d7ef..ef43e1247 100644 --- a/ILSpy/VB/VBTextOutputFormatter.cs +++ b/ILSpy/VB/VBTextOutputFormatter.cs @@ -33,6 +33,8 @@ namespace ICSharpCode.ILSpy.VB { readonly ITextOutput output; readonly Stack nodeStack = new Stack(); + + bool firstImport, lastImport; public VBTextOutputFormatter(ITextOutput output) { @@ -43,28 +45,36 @@ namespace ICSharpCode.ILSpy.VB public void StartNode(AstNode node) { -// var ranges = node.Annotation>(); -// if (ranges != null && ranges.Count > 0) -// { -// // find the ancestor that has method mapping as annotation -// if (node.Ancestors != null && node.Ancestors.Count() > 0) -// { -// var n = node.Ancestors.FirstOrDefault(a => a.Annotation() != null); -// if (n != null) { -// MemberMapping mapping = n.Annotation(); -// -// // add all ranges -// foreach (var range in ranges) { -// mapping.MemberCodeMappings.Add(new SourceCodeMapping { -// ILInstructionOffset = range, -// SourceCodeLine = output.CurrentLine, -// MemberMapping = mapping -// }); -// } -// } -// } -// } - + // var ranges = node.Annotation>(); + // if (ranges != null && ranges.Count > 0) + // { + // // find the ancestor that has method mapping as annotation + // if (node.Ancestors != null && node.Ancestors.Count() > 0) + // { + // var n = node.Ancestors.FirstOrDefault(a => a.Annotation() != null); + // if (n != null) { + // MemberMapping mapping = n.Annotation(); + // + // // add all ranges + // foreach (var range in ranges) { + // mapping.MemberCodeMappings.Add(new SourceCodeMapping { + // ILInstructionOffset = range, + // SourceCodeLine = output.CurrentLine, + // MemberMapping = mapping + // }); + // } + // } + // } + // } + if (nodeStack.Count == 0) { + if (node is ImportsStatement) { + firstImport = !(node.PrevSibling is ImportsStatement); + lastImport = !(node.NextSibling is ImportsStatement); + } else { + firstImport = false; + lastImport = false; + } + } nodeStack.Push(node); } @@ -100,6 +110,11 @@ namespace ICSharpCode.ILSpy.VB return; } + if (firstImport) { + output.MarkFoldStart(defaultCollapsed: true); + firstImport = false; + } + output.Write(identifier); } @@ -198,6 +213,10 @@ namespace ICSharpCode.ILSpy.VB public void NewLine() { + if (lastImport) { + output.MarkFoldEnd(); + lastImport = false; + } output.WriteLine(); } diff --git a/NRefactory.VB/ICSharpCode.NRefactory.VB.Tests/ICSharpCode.NRefactory.VB.Tests.csproj b/NRefactory.VB/ICSharpCode.NRefactory.VB.Tests/ICSharpCode.NRefactory.VB.Tests.csproj index 583a8acae..ee1d38229 100644 --- a/NRefactory.VB/ICSharpCode.NRefactory.VB.Tests/ICSharpCode.NRefactory.VB.Tests.csproj +++ b/NRefactory.VB/ICSharpCode.NRefactory.VB.Tests/ICSharpCode.NRefactory.VB.Tests.csproj @@ -94,13 +94,16 @@ - {7B82B671-419F-45F4-B778-D9286F996EFA} + {7b82b671-419f-45f4-b778-d9286f996efa} ICSharpCode.NRefactory.VB - {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} + {3b2a5653-ec97-4001-bb9b-d90f1af2c371} ICSharpCode.NRefactory + + + \ No newline at end of file diff --git a/NRefactory.VB/ICSharpCode.NRefactory.VB.Tests/Lexer/LexerContextTests.cs b/NRefactory.VB/ICSharpCode.NRefactory.VB.Tests/Lexer/LexerContextTests.cs index f04ab8469..770584a0b 100644 --- a/NRefactory.VB/ICSharpCode.NRefactory.VB.Tests/Lexer/LexerContextTests.cs +++ b/NRefactory.VB/ICSharpCode.NRefactory.VB.Tests/Lexer/LexerContextTests.cs @@ -568,7 +568,8 @@ exit Global Assert.IsEmpty(p.Errors); - Assert.AreEqual(expectedOutput, p.Output); + Assert.AreEqual(expectedOutput.Replace("\r", ""), + p.Output.Replace("\r", "")); } } } diff --git a/NRefactory.VB/ICSharpCode.NRefactory.VB.Tests/Parser/GlobalScope/ImportsStatementTests.cs b/NRefactory.VB/ICSharpCode.NRefactory.VB.Tests/Parser/GlobalScope/ImportsStatementTests.cs index 971805090..1067561e3 100644 --- a/NRefactory.VB/ICSharpCode.NRefactory.VB.Tests/Parser/GlobalScope/ImportsStatementTests.cs +++ b/NRefactory.VB/ICSharpCode.NRefactory.VB.Tests/Parser/GlobalScope/ImportsStatementTests.cs @@ -27,6 +27,7 @@ namespace ICSharpCode.NRefactory.VB.Tests.Ast } [Test] + [Ignore("Collection pattern matching is not implemented")] public void SimpleImportsStatement() { string program = "Imports System\n"; @@ -42,6 +43,7 @@ namespace ICSharpCode.NRefactory.VB.Tests.Ast } [Test] + [Ignore("Collection pattern matching is not implemented")] public void QualifiedTypeImportsStatement() { string program = "Imports My.Name.Space\n"; diff --git a/NRefactory.VB/ICSharpCode.NRefactory.VB/Ast/AstNodeCollection.cs b/NRefactory.VB/ICSharpCode.NRefactory.VB/Ast/AstNodeCollection.cs index aa91bb5c9..fd1265220 100644 --- a/NRefactory.VB/ICSharpCode.NRefactory.VB/Ast/AstNodeCollection.cs +++ b/NRefactory.VB/ICSharpCode.NRefactory.VB/Ast/AstNodeCollection.cs @@ -182,7 +182,8 @@ namespace ICSharpCode.NRefactory.VB // if (success && cur2 == null) // return true; // } - return false; +// return false; + throw new NotImplementedException(); } public void InsertAfter(T existingItem, T newItem) diff --git a/NRefactory.VB/ICSharpCode.NRefactory.VB/ICSharpCode.NRefactory.VB.csproj b/NRefactory.VB/ICSharpCode.NRefactory.VB/ICSharpCode.NRefactory.VB.csproj index 5c75126ba..44e599d12 100644 --- a/NRefactory.VB/ICSharpCode.NRefactory.VB/ICSharpCode.NRefactory.VB.csproj +++ b/NRefactory.VB/ICSharpCode.NRefactory.VB/ICSharpCode.NRefactory.VB.csproj @@ -211,11 +211,11 @@ - {53DCA265-3C3C-42F9-B647-F72BA678122B} + {53dca265-3c3c-42f9-b647-f72ba678122b} ICSharpCode.NRefactory.CSharp - {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} + {3b2a5653-ec97-4001-bb9b-d90f1af2c371} ICSharpCode.NRefactory diff --git a/NRefactory.VB/ICSharpCode.NRefactory.VB/OutputVisitor/OutputVisitor.cs b/NRefactory.VB/ICSharpCode.NRefactory.VB/OutputVisitor/OutputVisitor.cs index 12e2d5dcf..495768131 100644 --- a/NRefactory.VB/ICSharpCode.NRefactory.VB/OutputVisitor/OutputVisitor.cs +++ b/NRefactory.VB/ICSharpCode.NRefactory.VB/OutputVisitor/OutputVisitor.cs @@ -67,11 +67,17 @@ namespace ICSharpCode.NRefactory.VB public object VisitBlockStatement(BlockStatement blockStatement, object data) { + // prepare new block + NewLine(); + Indent(); + StartNode(blockStatement); foreach (var stmt in blockStatement) { stmt.AcceptVisitor(this, data); NewLine(); } + // finish block + Unindent(); return EndNode(blockStatement); } @@ -238,6 +244,7 @@ namespace ICSharpCode.NRefactory.VB WriteModifiers(typeDeclaration.ModifierTokens); WriteClassTypeKeyword(typeDeclaration); WriteIdentifier(typeDeclaration.Name.Name); + WriteTypeParameters(typeDeclaration.TypeParameters); MarkFoldStart(); NewLine(); @@ -519,12 +526,7 @@ namespace ICSharpCode.NRefactory.VB WriteKeyword("New"); WriteCommaSeparatedListInParenthesis(constructorDeclaration.Parameters, false); MarkFoldStart(); - NewLine(); - - Indent(); WriteBlock(constructorDeclaration.Body); - Unindent(); - WriteKeyword("End"); WriteKeyword("Sub"); MarkFoldEnd(); @@ -556,10 +558,7 @@ namespace ICSharpCode.NRefactory.VB WriteImplementsClause(methodDeclaration.ImplementsClause); if (!methodDeclaration.Body.IsNull) { MarkFoldStart(); - NewLine(); - Indent(); WriteBlock(methodDeclaration.Body); - Unindent(); WriteKeyword("End"); if (methodDeclaration.IsSub) WriteKeyword("Sub"); @@ -854,17 +853,17 @@ namespace ICSharpCode.NRefactory.VB void WriteIdentifier(string identifier, Role identifierRole = null) { WriteSpecialsUpToRole(identifierRole ?? AstNode.Roles.Identifier); - if (IsKeyword(identifier, containerStack.Peek())) { - if (lastWritten == LastWritten.KeywordOrIdentifier) - Space(); // this space is not strictly required, so we call Space() + + if (lastWritten == LastWritten.KeywordOrIdentifier) + Space(); // this space is not strictly required, so we call Space() + + if (IsKeyword(identifier, containerStack.Peek())) formatter.WriteToken("["); - } else if (lastWritten == LastWritten.KeywordOrIdentifier) { - formatter.Space(); // this space is strictly required, so we directly call the formatter - } + formatter.WriteIdentifier(identifier); - if (IsKeyword(identifier, containerStack.Peek())) { + if (IsKeyword(identifier, containerStack.Peek())) formatter.WriteToken("]"); - } + lastWritten = LastWritten.KeywordOrIdentifier; } @@ -1013,7 +1012,7 @@ namespace ICSharpCode.NRefactory.VB "GetType", "GetXmlNamespace", "Global", "GoSub", "GoTo", "Handles", "If", "Implements", "Imports", "In", "Inherits", "Integer", "Interface", "Is", "IsNot", "Let", "Lib", "Like", "Long", "Loop", "Me", "Mod", "Module", "MustInherit", "MustOverride", "MyBase", "MyClass", - "Namespace", "Narrowing", "New", "Next", "Not", "Nothing", "NotInheritable", "NotOverridable", + "Namespace", "Narrowing", "Next", "Not", "Nothing", "NotInheritable", "NotOverridable", "Object", "Of", "On", "Operator", "Option", "Optional", "Or", "OrElse", "Overloads", "Overridable", "Overrides", "ParamArray", "Partial", "Private", "Property", "Protected", "Public", "RaiseEvent", "ReadOnly", "ReDim", "REM", "RemoveHandler", "Resume", "Return", @@ -1032,6 +1031,11 @@ namespace ICSharpCode.NRefactory.VB /// public static bool IsKeyword(string identifier, AstNode context) { + if (identifier == "New") { + if (context.PrevSibling is InstanceExpression) + return false; + return true; + } if (unconditionalKeywords.Contains(identifier)) return true; // if (context.Ancestors.Any(a => a is QueryExpression)) { @@ -1109,9 +1113,12 @@ namespace ICSharpCode.NRefactory.VB void WriteBlock(BlockStatement body) { - if (body.IsNull) + if (body.IsNull) { NewLine(); - else + Indent(); + NewLine(); + Unindent(); + } else VisitBlockStatement(body, null); } @@ -1236,16 +1243,15 @@ namespace ICSharpCode.NRefactory.VB lastWritten = LastWritten.KeywordOrIdentifier; } else if (val is IFormattable) { StringBuilder b = new StringBuilder(); -// if (primitiveExpression.LiteralFormat == LiteralFormat.HexadecimalNumber) { -// b.Append("0x"); -// b.Append(((IFormattable)val).ToString("x", NumberFormatInfo.InvariantInfo)); -// } else { b.Append(((IFormattable)val).ToString(null, NumberFormatInfo.InvariantInfo)); -// } - if (val is uint || val is ulong) { + if (val is ushort || val is ulong) { b.Append("U"); } - if (val is long || val is ulong) { + if (val is short || val is ushort) { + b.Append("S"); + } else if (val is uint) { + b.Append("UI"); + } else if (val is long || val is ulong) { b.Append("L"); } formatter.WriteToken(b.ToString()); @@ -1307,10 +1313,7 @@ namespace ICSharpCode.NRefactory.VB } if (accessor.Parameters.Any()) WriteCommaSeparatedListInParenthesis(accessor.Parameters, false); - NewLine(); - Indent(); WriteBlock(accessor.Body); - Unindent(); WriteKeyword("End"); if (accessor.Role == PropertyDeclaration.GetterRole) { @@ -1356,10 +1359,7 @@ namespace ICSharpCode.NRefactory.VB StartNode(withStatement); WriteKeyword("With"); withStatement.Expression.AcceptVisitor(this, data); - NewLine(); - Indent(); withStatement.Body.AcceptVisitor(this, data); - Unindent(); WriteKeyword("End"); WriteKeyword("With"); return EndNode(withStatement); @@ -1370,10 +1370,7 @@ namespace ICSharpCode.NRefactory.VB StartNode(syncLockStatement); WriteKeyword("SyncLock"); syncLockStatement.Expression.AcceptVisitor(this, data); - NewLine(); - Indent(); syncLockStatement.Body.AcceptVisitor(this, data); - Unindent(); WriteKeyword("End"); WriteKeyword("SyncLock"); return EndNode(syncLockStatement); @@ -1383,19 +1380,13 @@ namespace ICSharpCode.NRefactory.VB { StartNode(tryStatement); WriteKeyword("Try"); - NewLine(); - Indent(); tryStatement.Body.AcceptVisitor(this, data); - Unindent(); foreach (var clause in tryStatement.CatchBlocks) { clause.AcceptVisitor(this, data); } if (!tryStatement.FinallyBlock.IsNull) { WriteKeyword("Finally"); - NewLine(); - Indent(); tryStatement.FinallyBlock.AcceptVisitor(this, data); - Unindent(); } WriteKeyword("End"); WriteKeyword("Try"); @@ -1445,19 +1436,17 @@ namespace ICSharpCode.NRefactory.VB ifElseStatement.Condition.AcceptVisitor(this, data); Space(); WriteKeyword("Then"); - NewLine(); - Indent(); + bool needsEndIf = ifElseStatement.Body is BlockStatement; ifElseStatement.Body.AcceptVisitor(this, data); - Unindent(); if (!ifElseStatement.ElseBlock.IsNull) { WriteKeyword("Else"); - NewLine(); - Indent(); + needsEndIf = ifElseStatement.ElseBlock is BlockStatement; ifElseStatement.ElseBlock.AcceptVisitor(this, data); - Unindent(); } - WriteKeyword("End"); - WriteKeyword("If"); + if (needsEndIf) { + WriteKeyword("End"); + WriteKeyword("If"); + } return EndNode(ifElseStatement); } @@ -1635,7 +1624,8 @@ namespace ICSharpCode.NRefactory.VB WriteKeyword("New"); Space(); arrayCreateExpression.Type.AcceptVisitor(this, data); - WriteCommaSeparatedListInParenthesis(arrayCreateExpression.Arguments, false); + if (arrayCreateExpression.Arguments.Any()) + WriteCommaSeparatedListInParenthesis(arrayCreateExpression.Arguments, false); foreach (var specifier in arrayCreateExpression.AdditionalArraySpecifiers) { specifier.AcceptVisitor(this, data); } @@ -1881,10 +1871,7 @@ namespace ICSharpCode.NRefactory.VB WriteKeyword("While"); Space(); whileStatement.Condition.AcceptVisitor(this, data); - NewLine(); - Indent(); whileStatement.Body.AcceptVisitor(this, data); - Unindent(); WriteKeyword("End"); WriteKeyword("While"); @@ -1942,10 +1929,7 @@ namespace ICSharpCode.NRefactory.VB Space(); forStatement.StepExpression.AcceptVisitor(this, data); } - NewLine(); - Indent(); forStatement.Body.AcceptVisitor(this, data); - Unindent(); WriteKeyword("Next"); return EndNode(forStatement); @@ -1961,10 +1945,7 @@ namespace ICSharpCode.NRefactory.VB Space(); WriteKeyword("In"); forEachStatement.InExpression.AcceptVisitor(this, data); - NewLine(); - Indent(); forEachStatement.Body.AcceptVisitor(this, data); - Unindent(); WriteKeyword("Next"); return EndNode(forEachStatement); @@ -2064,10 +2045,7 @@ namespace ICSharpCode.NRefactory.VB } if (!operatorDeclaration.Body.IsNull) { MarkFoldStart(); - NewLine(); - Indent(); WriteBlock(operatorDeclaration.Body); - Unindent(); WriteKeyword("End"); WriteKeyword("Operator"); MarkFoldEnd(); @@ -2109,10 +2087,7 @@ namespace ICSharpCode.NRefactory.VB Space(); WriteCommaSeparatedList(caseStatement.Clauses); } - NewLine(); - Indent(); caseStatement.Body.AcceptVisitor(this, data); - Unindent(); return EndNode(caseStatement); } @@ -2238,10 +2213,7 @@ namespace ICSharpCode.NRefactory.VB WriteKeyword("While"); doLoopStatement.Expression.AcceptVisitor(this, data); } - NewLine(); - Indent(); doLoopStatement.Body.AcceptVisitor(this, data); - Unindent(); WriteKeyword("Loop"); if (doLoopStatement.ConditionType == ConditionType.LoopUntil) { WriteKeyword("Until"); @@ -2261,10 +2233,7 @@ namespace ICSharpCode.NRefactory.VB WriteKeyword("Using"); WriteCommaSeparatedList(usingStatement.Resources); - NewLine(); - Indent(); usingStatement.Body.AcceptVisitor(this, data); - Unindent(); WriteKeyword("End"); WriteKeyword("Using"); @@ -2317,10 +2286,7 @@ namespace ICSharpCode.NRefactory.VB else WriteKeyword("Function"); WriteCommaSeparatedListInParenthesis(multiLineLambdaExpression.Parameters, false); - NewLine(); - Indent(); multiLineLambdaExpression.Body.AcceptVisitor(this, data); - Unindent(); WriteKeyword("End"); if (multiLineLambdaExpression.IsSub) WriteKeyword("Sub"); diff --git a/NRefactory.VB/ICSharpCode.NRefactory.VB/Visitors/CSharpToVBConverterVisitor.cs b/NRefactory.VB/ICSharpCode.NRefactory.VB/Visitors/CSharpToVBConverterVisitor.cs index f9230402a..a38d17e23 100644 --- a/NRefactory.VB/ICSharpCode.NRefactory.VB/Visitors/CSharpToVBConverterVisitor.cs +++ b/NRefactory.VB/ICSharpCode.NRefactory.VB/Visitors/CSharpToVBConverterVisitor.cs @@ -22,6 +22,7 @@ namespace ICSharpCode.NRefactory.VB.Visitors IType ResolveType(AstType type, TypeDeclaration entity = null); bool IsMethodGroup(CSharp.Expression expression); bool HasEvent(Expression expression); + CSharp.ParameterDeclaration[] GetParametersForProperty(CSharp.PropertyDeclaration property); } /// @@ -98,12 +99,23 @@ namespace ICSharpCode.NRefactory.VB.Visitors Type = (AstType)arrayCreateExpression.Type.AcceptVisitor(this, data), Initializer = (ArrayInitializerExpression)arrayCreateExpression.Initializer.AcceptVisitor(this, data) }; - ConvertNodes(arrayCreateExpression.Arguments, expr.Arguments, - n => new BinaryOperatorExpression(n, BinaryOperatorType.Subtract, new PrimitiveExpression(1))); + ConvertNodes(arrayCreateExpression.Arguments, expr.Arguments, ReduceArrayUpperBoundExpression); ConvertNodes(arrayCreateExpression.AdditionalArraySpecifiers, expr.AdditionalArraySpecifiers); return EndNode(arrayCreateExpression, expr); } + + Expression ReduceArrayUpperBoundExpression(Expression expression) + { + if (expression is PrimitiveExpression) + { + var numericLiteral = expression as PrimitiveExpression; + int? upperBound = numericLiteral.Value as int?; + if (upperBound.HasValue) + return new PrimitiveExpression(upperBound.Value - 1); + } + return new BinaryOperatorExpression(expression, BinaryOperatorType.Subtract, new PrimitiveExpression(1)); + } public AstNode VisitArrayInitializerExpression(CSharp.ArrayInitializerExpression arrayInitializerExpression, object data) { @@ -125,10 +137,10 @@ namespace ICSharpCode.NRefactory.VB.Visitors var right = (Expression)assignmentExpression.Right.AcceptVisitor(this, data); switch (assignmentExpression.Operator) { - case ICSharpCode.NRefactory.CSharp.AssignmentOperatorType.Assign: + case CSharp.AssignmentOperatorType.Assign: op = AssignmentOperatorType.Assign; break; - case ICSharpCode.NRefactory.CSharp.AssignmentOperatorType.Add: + case CSharp.AssignmentOperatorType.Add: if (provider.HasEvent(left)) { var addHandler = new AddRemoveHandlerStatement { IsAddHandler = true }; addHandler.EventExpression = left; @@ -137,7 +149,7 @@ namespace ICSharpCode.NRefactory.VB.Visitors } op = AssignmentOperatorType.Add; break; - case ICSharpCode.NRefactory.CSharp.AssignmentOperatorType.Subtract: + case CSharp.AssignmentOperatorType.Subtract: if (provider.HasEvent(left)) { var addHandler = new AddRemoveHandlerStatement { IsAddHandler = false }; addHandler.EventExpression = left; @@ -146,31 +158,31 @@ namespace ICSharpCode.NRefactory.VB.Visitors } op = AssignmentOperatorType.Subtract; break; - case ICSharpCode.NRefactory.CSharp.AssignmentOperatorType.Multiply: + case CSharp.AssignmentOperatorType.Multiply: op = AssignmentOperatorType.Multiply; break; - case ICSharpCode.NRefactory.CSharp.AssignmentOperatorType.Divide: + case CSharp.AssignmentOperatorType.Divide: op = AssignmentOperatorType.Divide; break; - case ICSharpCode.NRefactory.CSharp.AssignmentOperatorType.Modulus: + case CSharp.AssignmentOperatorType.Modulus: op = AssignmentOperatorType.Assign; right = new BinaryOperatorExpression((Expression)left.Clone(), BinaryOperatorType.Modulus, right); break; - case ICSharpCode.NRefactory.CSharp.AssignmentOperatorType.ShiftLeft: + case CSharp.AssignmentOperatorType.ShiftLeft: op = AssignmentOperatorType.ShiftLeft; break; - case ICSharpCode.NRefactory.CSharp.AssignmentOperatorType.ShiftRight: + case CSharp.AssignmentOperatorType.ShiftRight: op = AssignmentOperatorType.ShiftRight; break; - case ICSharpCode.NRefactory.CSharp.AssignmentOperatorType.BitwiseAnd: + case CSharp.AssignmentOperatorType.BitwiseAnd: op = AssignmentOperatorType.Assign; right = new BinaryOperatorExpression((Expression)left.Clone(), BinaryOperatorType.BitwiseAnd, right); break; - case ICSharpCode.NRefactory.CSharp.AssignmentOperatorType.BitwiseOr: + case CSharp.AssignmentOperatorType.BitwiseOr: op = AssignmentOperatorType.Assign; right = new BinaryOperatorExpression((Expression)left.Clone(), BinaryOperatorType.BitwiseOr, right); break; - case ICSharpCode.NRefactory.CSharp.AssignmentOperatorType.ExclusiveOr: + case CSharp.AssignmentOperatorType.ExclusiveOr: op = AssignmentOperatorType.Assign; right = new BinaryOperatorExpression((Expression)left.Clone(), BinaryOperatorType.ExclusiveOr, right); break; @@ -196,68 +208,68 @@ namespace ICSharpCode.NRefactory.VB.Visitors var right = (Expression)binaryOperatorExpression.Right.AcceptVisitor(this, data); switch (binaryOperatorExpression.Operator) { - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.BitwiseAnd: + case CSharp.BinaryOperatorType.BitwiseAnd: op = BinaryOperatorType.BitwiseAnd; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.BitwiseOr: + case CSharp.BinaryOperatorType.BitwiseOr: op = BinaryOperatorType.BitwiseOr; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.ConditionalAnd: + case CSharp.BinaryOperatorType.ConditionalAnd: op = BinaryOperatorType.LogicalAnd; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.ConditionalOr: + case CSharp.BinaryOperatorType.ConditionalOr: op = BinaryOperatorType.LogicalOr; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.ExclusiveOr: + case CSharp.BinaryOperatorType.ExclusiveOr: op = BinaryOperatorType.ExclusiveOr; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.GreaterThan: + case CSharp.BinaryOperatorType.GreaterThan: op = BinaryOperatorType.GreaterThan; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.GreaterThanOrEqual: + case CSharp.BinaryOperatorType.GreaterThanOrEqual: op = BinaryOperatorType.GreaterThanOrEqual; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.Equality: + case CSharp.BinaryOperatorType.Equality: if (IsReferentialEquality(binaryOperatorExpression)) op = BinaryOperatorType.ReferenceEquality; else op = BinaryOperatorType.Equality; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.InEquality: + case CSharp.BinaryOperatorType.InEquality: if (IsReferentialEquality(binaryOperatorExpression)) op = BinaryOperatorType.ReferenceInequality; else op = BinaryOperatorType.InEquality; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.LessThan: + case CSharp.BinaryOperatorType.LessThan: op = BinaryOperatorType.LessThan; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.LessThanOrEqual: + case CSharp.BinaryOperatorType.LessThanOrEqual: op = BinaryOperatorType.LessThanOrEqual; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.Add: + case CSharp.BinaryOperatorType.Add: // TODO might be string concatenation op = BinaryOperatorType.Add; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.Subtract: + case CSharp.BinaryOperatorType.Subtract: op = BinaryOperatorType.Subtract; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.Multiply: + case CSharp.BinaryOperatorType.Multiply: op = BinaryOperatorType.Multiply; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.Divide: + case CSharp.BinaryOperatorType.Divide: op = BinaryOperatorType.Divide; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.Modulus: + case CSharp.BinaryOperatorType.Modulus: op = BinaryOperatorType.Modulus; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.ShiftLeft: + case CSharp.BinaryOperatorType.ShiftLeft: op = BinaryOperatorType.ShiftLeft; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.ShiftRight: + case CSharp.BinaryOperatorType.ShiftRight: op = BinaryOperatorType.ShiftRight; break; - case ICSharpCode.NRefactory.CSharp.BinaryOperatorType.NullCoalescing: + case CSharp.BinaryOperatorType.NullCoalescing: var nullCoalescing = new ConditionalExpression { ConditionExpression = left, FalseExpression = right @@ -502,8 +514,10 @@ namespace ICSharpCode.NRefactory.VB.Visitors { Expression expr; - if (!string.IsNullOrEmpty(primitiveExpression.Value as string) || primitiveExpression.Value is char) + if (!string.IsNullOrEmpty(primitiveExpression.Value as string)) expr = ConvertToConcat(primitiveExpression.Value.ToString()); + else if (primitiveExpression.Value is char) + expr = ConvertToSpecialChar((char)primitiveExpression.Value); else expr = new PrimitiveExpression(primitiveExpression.Value); @@ -519,24 +533,15 @@ namespace ICSharpCode.NRefactory.VB.Visitors string part; switch (literal[i]) { case '\0': - part = literal.Substring(start, i - start); - if (!string.IsNullOrEmpty(part)) - parts.Push(new PrimitiveExpression(part)); - parts.Push(new IdentifierExpression("vbNullChar")); - start = i + 1; - break; case '\b': - part = literal.Substring(start, i - start); - if (!string.IsNullOrEmpty(part)) - parts.Push(new PrimitiveExpression(part)); - parts.Push(new IdentifierExpression("vbBack")); - start = i + 1; - break; case '\f': + case '\n': + case '\t': + case '\v': part = literal.Substring(start, i - start); if (!string.IsNullOrEmpty(part)) parts.Push(new PrimitiveExpression(part)); - parts.Push(new IdentifierExpression("vbFormFeed")); + parts.Push(ConvertToSpecialChar(literal[i])); start = i + 1; break; case '\r': @@ -550,27 +555,6 @@ namespace ICSharpCode.NRefactory.VB.Visitors parts.Push(new IdentifierExpression("vbCr")); start = i + 1; break; - case '\n': - part = literal.Substring(start, i - start); - if (!string.IsNullOrEmpty(part)) - parts.Push(new PrimitiveExpression(part)); - parts.Push(new IdentifierExpression("vbLf")); - start = i + 1; - break; - case '\t': - part = literal.Substring(start, i - start); - if (!string.IsNullOrEmpty(part)) - parts.Push(new PrimitiveExpression(part)); - parts.Push(new IdentifierExpression("vbTab")); - start = i + 1; - break; - case '\v': - part = literal.Substring(start, i - start); - if (!string.IsNullOrEmpty(part)) - parts.Push(new PrimitiveExpression(part)); - parts.Push(new IdentifierExpression("vbVerticalTab")); - start = i + 1; - break; default: if ((int)literal[i] > 255) { part = literal.Substring(start, i - start); @@ -597,6 +581,30 @@ namespace ICSharpCode.NRefactory.VB.Visitors return current; } + + Expression ConvertToSpecialChar(char ch) + { + switch (ch) { + case '\0': + return new IdentifierExpression("vbNullChar"); + case '\b': + return new IdentifierExpression("vbBack"); + case '\f': + return new IdentifierExpression("vbFormFeed"); + case '\r': + return new IdentifierExpression("vbCr"); + case '\n': + return new IdentifierExpression("vbLf"); + case '\t': + return new IdentifierExpression("vbTab"); + case '\v': + return new IdentifierExpression("vbVerticalTab"); + default: + if (ch > 255) + return new InvocationExpression(new IdentifierExpression("ChrW"), new PrimitiveExpression((int)ch)); + return new PrimitiveExpression(ch); + } + } public AstNode VisitSizeOfExpression(CSharp.SizeOfExpression sizeOfExpression, object data) { @@ -645,57 +653,57 @@ namespace ICSharpCode.NRefactory.VB.Visitors Expression expr; switch (unaryOperatorExpression.Operator) { - case ICSharpCode.NRefactory.CSharp.UnaryOperatorType.Not: - case ICSharpCode.NRefactory.CSharp.UnaryOperatorType.BitNot: + case CSharp.UnaryOperatorType.Not: + case CSharp.UnaryOperatorType.BitNot: expr = new UnaryOperatorExpression() { Expression = (Expression)unaryOperatorExpression.Expression.AcceptVisitor(this, data), Operator = UnaryOperatorType.Not }; break; - case ICSharpCode.NRefactory.CSharp.UnaryOperatorType.Minus: + case CSharp.UnaryOperatorType.Minus: expr = new UnaryOperatorExpression() { Expression = (Expression)unaryOperatorExpression.Expression.AcceptVisitor(this, data), Operator = UnaryOperatorType.Minus }; break; - case ICSharpCode.NRefactory.CSharp.UnaryOperatorType.Plus: + case CSharp.UnaryOperatorType.Plus: expr = new UnaryOperatorExpression() { Expression = (Expression)unaryOperatorExpression.Expression.AcceptVisitor(this, data), Operator = UnaryOperatorType.Plus }; break; - case ICSharpCode.NRefactory.CSharp.UnaryOperatorType.Increment: + case CSharp.UnaryOperatorType.Increment: expr = new InvocationExpression(); ((InvocationExpression)expr).Target = new IdentifierExpression() { Identifier = "__Increment" }; ((InvocationExpression)expr).Arguments.Add((Expression)unaryOperatorExpression.Expression.AcceptVisitor(this, data)); break; - case ICSharpCode.NRefactory.CSharp.UnaryOperatorType.PostIncrement: + case CSharp.UnaryOperatorType.PostIncrement: expr = new InvocationExpression(); ((InvocationExpression)expr).Target = new IdentifierExpression() { Identifier = "__PostIncrement" }; ((InvocationExpression)expr).Arguments.Add((Expression)unaryOperatorExpression.Expression.AcceptVisitor(this, data)); break; - case ICSharpCode.NRefactory.CSharp.UnaryOperatorType.Decrement: + case CSharp.UnaryOperatorType.Decrement: expr = new InvocationExpression(); ((InvocationExpression)expr).Target = new IdentifierExpression() { Identifier = "__Decrement" }; ((InvocationExpression)expr).Arguments.Add((Expression)unaryOperatorExpression.Expression.AcceptVisitor(this, data)); break; - case ICSharpCode.NRefactory.CSharp.UnaryOperatorType.PostDecrement: + case CSharp.UnaryOperatorType.PostDecrement: expr = new InvocationExpression(); ((InvocationExpression)expr).Target = new IdentifierExpression() { Identifier = "__PostDecrement" }; ((InvocationExpression)expr).Arguments.Add((Expression)unaryOperatorExpression.Expression.AcceptVisitor(this, data)); break; - case ICSharpCode.NRefactory.CSharp.UnaryOperatorType.AddressOf: + case CSharp.UnaryOperatorType.AddressOf: expr = new UnaryOperatorExpression() { Expression = (Expression)unaryOperatorExpression.Expression.AcceptVisitor(this, data), Operator = UnaryOperatorType.AddressOf }; break; - case ICSharpCode.NRefactory.CSharp.UnaryOperatorType.Dereference: + case CSharp.UnaryOperatorType.Dereference: expr = new InvocationExpression(); ((InvocationExpression)expr).Target = new IdentifierExpression() { Identifier = "__Dereference" }; ((InvocationExpression)expr).Arguments.Add((Expression)unaryOperatorExpression.Expression.AcceptVisitor(this, data)); break; - case ICSharpCode.NRefactory.CSharp.UnaryOperatorType.Await: + case CSharp.UnaryOperatorType.Await: expr = new UnaryOperatorExpression() { Expression = (Expression)unaryOperatorExpression.Expression.AcceptVisitor(this, data), Operator = UnaryOperatorType.Await @@ -840,7 +848,7 @@ namespace ICSharpCode.NRefactory.VB.Visitors foreach (string id in namespaceDeclaration.Identifiers) { newNamespace.Identifiers.Add(new Identifier(id, TextLocation.Empty)); } - ConvertNodes(namespaceDeclaration.Members, newNamespace.Members); + ConvertMembers(namespaceDeclaration, newNamespace, CSharp.NamespaceDeclaration.MemberRole, NamespaceDeclaration.MemberRole); return EndNode(namespaceDeclaration, newNamespace); } @@ -864,7 +872,7 @@ namespace ICSharpCode.NRefactory.VB.Visitors type.Name = new Identifier(typeDeclaration.Name, TextLocation.Empty); - ConvertNodes(typeDeclaration.Members, type.Members); + ConvertMembers(typeDeclaration, type, CSharp.Roles.TypeMemberRole, EnumDeclaration.MemberRole); return EndNode(typeDeclaration, type); } else { @@ -904,6 +912,7 @@ namespace ICSharpCode.NRefactory.VB.Visitors ConvertNodes(typeDeclaration.Attributes, type.Attributes); ConvertNodes(typeDeclaration.ModifierTokens, type.ModifierTokens); + ConvertNodes(typeDeclaration.TypeParameters, type.TypeParameters); if (typeDeclaration.BaseTypes.Any()) { var first = typeDeclaration.BaseTypes.First(); @@ -918,7 +927,7 @@ namespace ICSharpCode.NRefactory.VB.Visitors type.Name = typeDeclaration.Name; types.Push(type); - ConvertNodes(typeDeclaration.Members, type.Members); + ConvertMembers(typeDeclaration, type, CSharp.Roles.TypeMemberRole, TypeDeclaration.MemberRole); types.Pop(); return EndNode(typeDeclaration, type); @@ -1473,15 +1482,16 @@ namespace ICSharpCode.NRefactory.VB.Visitors var result = new EventDeclaration(); ConvertNodes(eventDeclaration.Attributes, result.Attributes); - if (types.Any() && types.Peek().ClassType == ClassType.Module) - eventDeclaration.Modifiers &= ~CSharp.Modifiers.Static; + if (types.Any()) { + if (types.Peek().ClassType == ClassType.Module) + eventDeclaration.Modifiers &= ~CSharp.Modifiers.Static; + types.Peek().Members.Add(result); + } result.Modifiers = ConvertModifiers(eventDeclaration.Modifiers, eventDeclaration); result.Name = evt.Name; result.ReturnType = (AstType)eventDeclaration.ReturnType.AcceptVisitor(this, data); // CreateImplementsClausesForEvent(result); - - types.Peek().Members.Add(result); } members.Pop(); @@ -1759,76 +1769,76 @@ namespace ICSharpCode.NRefactory.VB.Visitors ConvertNodes(operatorDeclaration.Attributes.Where(section => section.AttributeTarget != "return"), op.Attributes); ConvertNodes(operatorDeclaration.ModifierTokens, op.ModifierTokens); switch (operatorDeclaration.OperatorType) { - case ICSharpCode.NRefactory.CSharp.OperatorType.LogicalNot: - case ICSharpCode.NRefactory.CSharp.OperatorType.OnesComplement: + case CSharp.OperatorType.LogicalNot: + case CSharp.OperatorType.OnesComplement: op.Operator = OverloadableOperatorType.Not; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.True: + case CSharp.OperatorType.True: op.Operator = OverloadableOperatorType.IsTrue; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.False: + case CSharp.OperatorType.False: op.Operator = OverloadableOperatorType.IsFalse; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.Implicit: + case CSharp.OperatorType.Implicit: op.Modifiers |= Modifiers.Widening; op.Operator = OverloadableOperatorType.CType; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.Explicit: + case CSharp.OperatorType.Explicit: op.Modifiers |= Modifiers.Narrowing; op.Operator = OverloadableOperatorType.CType; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.Addition: + case CSharp.OperatorType.Addition: op.Operator = OverloadableOperatorType.Add; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.Subtraction: + case CSharp.OperatorType.Subtraction: op.Operator = OverloadableOperatorType.Subtract; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.UnaryPlus: + case CSharp.OperatorType.UnaryPlus: op.Operator = OverloadableOperatorType.UnaryPlus; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.UnaryNegation: + case CSharp.OperatorType.UnaryNegation: op.Operator = OverloadableOperatorType.UnaryMinus; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.Multiply: + case CSharp.OperatorType.Multiply: op.Operator = OverloadableOperatorType.Multiply; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.Division: + case CSharp.OperatorType.Division: op.Operator = OverloadableOperatorType.Divide; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.Modulus: + case CSharp.OperatorType.Modulus: op.Operator = OverloadableOperatorType.Modulus; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.BitwiseAnd: + case CSharp.OperatorType.BitwiseAnd: op.Operator = OverloadableOperatorType.BitwiseAnd; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.BitwiseOr: + case CSharp.OperatorType.BitwiseOr: op.Operator = OverloadableOperatorType.BitwiseOr; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.ExclusiveOr: + case CSharp.OperatorType.ExclusiveOr: op.Operator = OverloadableOperatorType.ExclusiveOr; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.LeftShift: + case CSharp.OperatorType.LeftShift: op.Operator = OverloadableOperatorType.ShiftLeft; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.RightShift: + case CSharp.OperatorType.RightShift: op.Operator = OverloadableOperatorType.ShiftRight; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.Equality: + case CSharp.OperatorType.Equality: op.Operator = OverloadableOperatorType.Equality; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.Inequality: + case CSharp.OperatorType.Inequality: op.Operator = OverloadableOperatorType.InEquality; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.GreaterThan: + case CSharp.OperatorType.GreaterThan: op.Operator = OverloadableOperatorType.GreaterThan; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.LessThan: + case CSharp.OperatorType.LessThan: op.Operator = OverloadableOperatorType.LessThan; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.GreaterThanOrEqual: + case CSharp.OperatorType.GreaterThanOrEqual: op.Operator = OverloadableOperatorType.GreaterThanOrEqual; break; - case ICSharpCode.NRefactory.CSharp.OperatorType.LessThanOrEqual: + case CSharp.OperatorType.LessThanOrEqual: op.Operator = OverloadableOperatorType.LessThanOrEqual; break; default: @@ -1852,7 +1862,7 @@ namespace ICSharpCode.NRefactory.VB.Visitors ConvertNodes(parameterDeclaration.Attributes, param.Attributes); param.Modifiers = ConvertParamModifiers(parameterDeclaration.ParameterModifier); - if ((parameterDeclaration.ParameterModifier & ICSharpCode.NRefactory.CSharp.ParameterModifier.Out) == ICSharpCode.NRefactory.CSharp.ParameterModifier.Out) { + if ((parameterDeclaration.ParameterModifier & CSharp.ParameterModifier.Out) == CSharp.ParameterModifier.Out) { AttributeBlock block = new AttributeBlock(); block.Attributes.Add(new Ast.Attribute() { Type = new SimpleType("Out") }); param.Attributes.Add(block); @@ -1869,13 +1879,13 @@ namespace ICSharpCode.NRefactory.VB.Visitors Modifiers ConvertParamModifiers(CSharp.ParameterModifier mods) { switch (mods) { - case ICSharpCode.NRefactory.CSharp.ParameterModifier.None: - case ICSharpCode.NRefactory.CSharp.ParameterModifier.This: + case CSharp.ParameterModifier.None: + case CSharp.ParameterModifier.This: return Modifiers.None; - case ICSharpCode.NRefactory.CSharp.ParameterModifier.Ref: - case ICSharpCode.NRefactory.CSharp.ParameterModifier.Out: + case CSharp.ParameterModifier.Ref: + case CSharp.ParameterModifier.Out: return Modifiers.ByRef; - case ICSharpCode.NRefactory.CSharp.ParameterModifier.Params: + case CSharp.ParameterModifier.Params: return Modifiers.ParamArray; default: throw new Exception("Invalid value for ParameterModifier"); @@ -1913,6 +1923,8 @@ namespace ICSharpCode.NRefactory.VB.Visitors if (members.Pop().inIterator) { decl.Modifiers |= Modifiers.Iterator; } + + ConvertNodes(provider.GetParametersForProperty(propertyDeclaration), decl.Parameters); return EndNode(propertyDeclaration, decl); } @@ -1923,7 +1935,7 @@ namespace ICSharpCode.NRefactory.VB.Visitors // look for type in parent decl.Type = (AstType)variableInitializer.Parent - .GetChildByRole(ICSharpCode.NRefactory.CSharp.Roles.Type) + .GetChildByRole(CSharp.Roles.Type) .AcceptVisitor(this, data); decl.Identifiers.Add(new VariableIdentifier() { Name = variableInitializer.Name }); decl.Initializer = (Expression)variableInitializer.Initializer.AcceptVisitor(this, data); @@ -2091,7 +2103,7 @@ namespace ICSharpCode.NRefactory.VB.Visitors }; var constraint = typeParameterDeclaration.Parent - .GetChildrenByRole(ICSharpCode.NRefactory.CSharp.Roles.Constraint) + .GetChildrenByRole(CSharp.Roles.Constraint) .SingleOrDefault(c => c.TypeParameter.Identifier == typeParameterDeclaration.Name); if (constraint != null) @@ -2221,7 +2233,24 @@ namespace ICSharpCode.NRefactory.VB.Visitors result.Add(n); } } - + + void ConvertMembers(CSharp.AstNode parent, T result, Role sourceRole, Role targetRole) where T : VB.AstNode where S : CSharp.AstNode where M : VB.AstNode + { + foreach (var node in parent.Children) { + if (node.Role == CSharp.Roles.Comment) { + var n = (Comment)node.AcceptVisitor(this, null); + if (n != null) + result.AddChild(n, AstNode.Roles.Comment); + } + + if (node.Role == sourceRole) { + var n = (M)node.AcceptVisitor(this, null); + if (n != null) + result.AddChild(n, targetRole); + } + } + } + T EndNode(CSharp.AstNode node, T result) where T : VB.AstNode { if (result != null) { diff --git a/README.md b/README.md new file mode 100644 index 000000000..3f839dac1 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# ILSpy + +[![Join the chat at https://gitter.im/icsharpcode/ILSpy](https://badges.gitter.im/icsharpcode/ILSpy.svg)](https://gitter.im/icsharpcode/ILSpy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +[@ilspy](https://twitter.com/ilspy) | [ilspy.net](http://ilspy.net/) + +Downloads +------- + +Xcopy-deployable binaries via [ilspy.net](http://ilspy.net/) + +Visual Studio extension via [VS Gallery](https://visualstudiogallery.msdn.microsoft.com/8ef1d688-f80c-4380-8004-2ec7f814e7de) + +Decompiler NuGet package via [NuGet](https://www.nuget.org/packages/ICSharpCode.Decompiler/) + +Get a recent nightly build from [here](http://build.sharpdevelop.net/BuildArtefacts/#ILSpyMaster) + +License +------- + +ILSpy is distributed under the MIT License. + +Included open-source libraries: + * Mono.Cecil: MIT License (thanks to Jb Evain) + * AvalonEdit: LGPL + * SharpTreeView: LGPL + * ICSharpCode.Decompiler: MIT License (developed as part of ILSpy) + * Ricciolo.StylesExplorer: MS-PL (part of ILSpy.BamlDecompiler.Plugin) diff --git a/README.txt b/README.txt index 56ef9c3bb..76cdfb52f 100644 --- a/README.txt +++ b/README.txt @@ -1,7 +1,7 @@ ILSpy is the open-source .NET assembly browser and decompiler. Website: http://www.ilspy.net/ -Copyright 2011-2014 AlphaSierraPapa for the SharpDevelop team +Copyright 2011-2016 AlphaSierraPapa for the SharpDevelop team License: ILSpy is distributed under the MIT License. Included open-source libraries: diff --git a/Rebracer.xml b/Rebracer.xml index bd0a77e01..f0cfda200 100644 --- a/Rebracer.xml +++ b/Rebracer.xml @@ -10,13 +10,13 @@ - TODO:2 - HACK:2 + HACK:2 + TODO:2 UNDONE:2 UnresolvedMergeConflict:3 - true - true + false + false false @@ -24,9 +24,14 @@ 1 1 + 1 1 2 80 + 0 + 1 + 0 + 1 1 1 1 @@ -34,11 +39,12 @@ 1 0 1 - 0 + 1 0 2 0 1 + 0 0 0 0 @@ -47,6 +53,7 @@ 0 1 1 + 1 1 0 0 @@ -54,6 +61,7 @@ 1 1 1 + 1 1 1 1 @@ -84,6 +92,10 @@ 0 0 0 + 1 + 1 + 0 + 1 1 1 0 diff --git a/SharpTreeView/SharpTreeView.cs b/SharpTreeView/SharpTreeView.cs index 83b82cc12..093e942ba 100644 --- a/SharpTreeView/SharpTreeView.cs +++ b/SharpTreeView/SharpTreeView.cs @@ -122,6 +122,27 @@ namespace ICSharpCode.TreeView TreeFlattener flattener; bool updatesLocked; + public IDisposable LockUpdates() + { + return new UpdateLock(this); + } + + class UpdateLock : IDisposable + { + SharpTreeView instance; + + public UpdateLock(SharpTreeView instance) + { + this.instance = instance; + this.instance.updatesLocked = true; + } + + public void Dispose() + { + this.instance.updatesLocked = false; + } + } + void Reload() { if (flattener != null) { diff --git a/TestPlugin/TestPlugin.csproj b/TestPlugin/TestPlugin.csproj index dc2e78f56..38230e7a8 100644 --- a/TestPlugin/TestPlugin.csproj +++ b/TestPlugin/TestPlugin.csproj @@ -83,27 +83,27 @@ - {984CC812-9470-4A13-AFF9-CC44068D666C} + {984cc812-9470-4a13-aff9-cc44068d666c} ICSharpCode.Decompiler - {1E85EFF9-E370-4683-83E4-8A3D063FF791} + {1e85eff9-e370-4683-83e4-8a3d063ff791} ILSpy - {D68133BD-1E63-496E-9EDE-4FBDBF77B486} + {d68133bd-1e63-496e-9ede-4fbdbf77b486} Mono.Cecil - {53DCA265-3C3C-42F9-B647-F72BA678122B} + {53dca265-3c3c-42f9-b647-f72ba678122b} ICSharpCode.NRefactory.CSharp - {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} + {3b2a5653-ec97-4001-bb9b-d90f1af2c371} ICSharpCode.NRefactory - {DDE2A481-8271-4EAC-A330-8FA6A38D13D1} + {dde2a481-8271-4eac-a330-8fa6a38d13d1} ICSharpCode.TreeView diff --git a/appveyor.yml b/appveyor.yml index 4a1154b81..310fc2803 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,4 +10,4 @@ build: parallel: true verbosity: minimal after_test: -- python ICSharpCode.AvalonEdit.Tests\tidy.py +- python BuildTools\tidy.py diff --git a/debugbuild.bat b/debugbuild.bat index 1dfe9ceca..9227bc776 100644 --- a/debugbuild.bat +++ b/debugbuild.bat @@ -1,7 +1,7 @@ @if not exist "AvalonEdit\ICSharpCode.AvalonEdit.sln" ( git submodule update --init || exit /b 1 ) -%windir%\microsoft.net\framework\v4.0.30319\msbuild /m ILSpy.sln /p:Configuration=Debug "/p:Platform=Any CPU" +"%ProgramFiles(x86)%\MSBuild\14.0\Bin\msbuild.exe" /m ILSpy.sln /p:Configuration=Debug "/p:Platform=Any CPU" @IF %ERRORLEVEL% NEQ 0 GOTO err @exit /B 0 :err diff --git a/releasebuild.bat b/releasebuild.bat index 11f863dab..57d174305 100644 --- a/releasebuild.bat +++ b/releasebuild.bat @@ -1,7 +1,7 @@ @if not exist "AvalonEdit\ICSharpCode.AvalonEdit.sln" ( git submodule update --init || exit /b 1 ) -%windir%\microsoft.net\framework\v4.0.30319\msbuild /m ILSpy.sln /p:Configuration=Release "/p:Platform=Any CPU" +"%ProgramFiles(x86)%\MSBuild\14.0\Bin\msbuild.exe" /m ILSpy.sln /p:Configuration=Release "/p:Platform=Any CPU" @IF %ERRORLEVEL% NEQ 0 GOTO err @exit /B 0 :err