From 55a6f9184f7ed6b12222ad91310bbde2ec69f2d3 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Mon, 2 Mar 2020 14:00:15 +0100 Subject: [PATCH 01/30] Use PowerShell Core for running update-assemblyinfo.ps1 --- ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index ec313b5d4..d7cd56cf4 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -625,7 +625,7 @@ - powershell -NoProfile -ExecutionPolicy Bypass -File BuildTools/update-assemblyinfo.ps1 $(Configuration) + pwsh -NoProfile -ExecutionPolicy Bypass -File BuildTools/update-assemblyinfo.ps1 $(Configuration) From 1ec05ed5ff31b6fab3d5e94faf3ced4e614ef7bf Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Mon, 2 Mar 2020 14:01:53 +0100 Subject: [PATCH 02/30] Azure DevOps pwsh instead of powershell --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c3399d8bb..d3f6bb173 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -45,9 +45,9 @@ jobs: version: '3.1.100' installationPath: $(Agent.ToolsDirectory)/dotnet - - powershell: .\BuildTools\pipelines-install.ps1 + - script: pwsh .\BuildTools\pipelines-install.ps1 displayName: Install - + - task: MSBuild@1 displayName: Restore ILSpy inputs: From 1577b7553e8834241f823501ee029aeaa672af90 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Mon, 2 Mar 2020 14:03:01 +0100 Subject: [PATCH 03/30] AppVeyor switch from powershell to pwsh --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index bfb05a141..2500014d2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,7 +8,7 @@ image: Visual Studio 2019 install: - git submodule update --init --recursive -- ps: .\BuildTools\appveyor-install.ps1 +- pwsh .\BuildTools\appveyor-install.ps1 nuget: account_feed: false From 5e9b59380f3f7f5d91e4d9953c0d6f6c70eefdfc Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Mon, 2 Mar 2020 14:08:12 +0100 Subject: [PATCH 04/30] Update docs that pwsh is required (instead of PS 5) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 90dbac521..0a2486442 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ Windows: - Workload "Visual Studio extension development" (ILSpy.sln contains a VS extension project) - Individual Component "MSVC v142 - VS 2019 C++ x64/x86 build tools (v14.23)" (or similar) - The VC++ toolset is optional; if present it is used for `editbin.exe` to modify the stack size used by ILSpy.exe from 1MB to 16MB, because the decompiler makes heavy use of recursion, where small stack sizes lead to problems in very complex methods. +- Make sure [PowerShell](https://github.com/PowerShell/PowerShell) is installed (formerly known as PowerShell Core) - recommended way of installation via [choco install powershell-core](https://chocolatey.org/packages/powershell-core) - Check out the ILSpy repository using git. - Execute `git submodule update --init --recursive` to download the ILSpy-Tests submodule (used by some test cases). - Open ILSpy.sln in Visual Studio. @@ -59,6 +60,7 @@ If this problem occurs, please manually install the .NET Core 3.1 SDK from [here Unix / Mac: - Make sure .NET Core 2.1 LTS Runtime is installed (you can get it here: https://get.dot.net). - Make sure [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download/dotnet-core/3.1) is installed. +- Make sure [PowerShell](https://github.com/PowerShell/PowerShell) is installed (formerly known as PowerShell Core) - Check out the repository using git. - Execute `git submodule update --init --recursive` to download the ILSpy-Tests submodule (used by some test cases). - Use `dotnet build Frontends.sln` to build the non-Windows flavors of ILSpy (.NET Core Global Tool and PowerShell Core). From 95beaddc80c9913e03cb76dc65920786e8c93bf8 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 8 Mar 2020 10:13:34 +0100 Subject: [PATCH 05/30] IntroduceQueryExpressions: Inline variable declarations and use pattern matching syntax where possible. --- .../Transforms/IntroduceQueryExpressions.cs | 54 ++++++------------- 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs index 7084083f1..742ee42d7 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs @@ -76,7 +76,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms node.ReplaceWith(query); AstNode next; for (AstNode child = (query ?? node).FirstChild; child != null; child = next) { - // store referece to next child before transformation + // store reference to next child before transformation next = child.NextSibling; DecompileQueries(child); } @@ -95,10 +95,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return null; if (!IsComplexQuery(mre)) return null; - ParameterDeclaration parameter; - Expression body; Expression expr = invocation.Arguments.Single(); - if (MatchSimpleLambda(expr, out parameter, out body)) { + if (MatchSimpleLambda(expr, out ParameterDeclaration parameter, out Expression body)) { QueryExpression query = new QueryExpression(); query.Clauses.Add(MakeFromClause(parameter, mre.Target.Detach())); query.Clauses.Add(new QuerySelectClause { Expression = WrapExpressionInParenthesesIfNecessary(body.Detach(), parameter.Name) }.CopyAnnotationsFrom(expr)); @@ -109,21 +107,16 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms case "GroupBy": { if (invocation.Arguments.Count == 2) { - ParameterDeclaration parameter1, parameter2; - Expression keySelector, elementSelector; - if (MatchSimpleLambda(invocation.Arguments.ElementAt(0), out parameter1, out keySelector) - && MatchSimpleLambda(invocation.Arguments.ElementAt(1), out parameter2, out elementSelector) - && parameter1.Name == parameter2.Name) - { + if (MatchSimpleLambda(invocation.Arguments.ElementAt(0), out ParameterDeclaration parameter1, out Expression keySelector) + && MatchSimpleLambda(invocation.Arguments.ElementAt(1), out ParameterDeclaration parameter2, out Expression elementSelector) + && parameter1.Name == parameter2.Name) { QueryExpression query = new QueryExpression(); query.Clauses.Add(MakeFromClause(parameter1, mre.Target.Detach())); query.Clauses.Add(new QueryGroupClause { Projection = elementSelector.Detach(), Key = keySelector.Detach() }); return query; } } else if (invocation.Arguments.Count == 1) { - ParameterDeclaration parameter; - Expression keySelector; - if (MatchSimpleLambda(invocation.Arguments.Single(), out parameter, out keySelector)) { + if (MatchSimpleLambda(invocation.Arguments.Single(), out ParameterDeclaration parameter, out Expression keySelector)) { QueryExpression query = new QueryExpression(); query.Clauses.Add(MakeFromClause(parameter, mre.Target.Detach())); query.Clauses.Add(new QueryGroupClause { Projection = new IdentifierExpression(parameter.Name).CopyAnnotationsFrom(parameter), Key = keySelector.Detach() }); @@ -136,9 +129,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { if (invocation.Arguments.Count != 2) return null; - ParameterDeclaration parameter; - Expression collectionSelector; - if (!MatchSimpleLambda(invocation.Arguments.ElementAt(0), out parameter, out collectionSelector)) + if (!MatchSimpleLambda(invocation.Arguments.ElementAt(0), out ParameterDeclaration parameter, out Expression collectionSelector)) return null; if (IsNullConditional(collectionSelector)) return null; @@ -162,10 +153,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return null; if (!IsComplexQuery(mre)) return null; - ParameterDeclaration parameter; - Expression body; Expression expr = invocation.Arguments.Single(); - if (MatchSimpleLambda(expr, out parameter, out body)) { + if (MatchSimpleLambda(expr, out ParameterDeclaration parameter, out Expression body)) { QueryExpression query = new QueryExpression(); query.Clauses.Add(MakeFromClause(parameter, mre.Target.Detach())); query.Clauses.Add(new QueryWhereClause { Condition = body.Detach() }.CopyAnnotationsFrom(expr)); @@ -182,12 +171,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return null; if (!IsComplexQuery(mre)) return null; - ParameterDeclaration parameter; - Expression orderExpression; - if (MatchSimpleLambda(invocation.Arguments.Single(), out parameter, out orderExpression)) { + if (MatchSimpleLambda(invocation.Arguments.Single(), out ParameterDeclaration parameter, out Expression orderExpression)) { if (ValidateThenByChain(invocation, parameter.Name)) { QueryOrderClause orderClause = new QueryOrderClause(); - InvocationExpression tmp = invocation; while (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending") { // insert new ordering at beginning orderClause.Orderings.InsertAfter( @@ -195,8 +181,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms Expression = orderExpression.Detach(), Direction = (mre.MemberName == "ThenBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending) }); - - tmp = (InvocationExpression)mre.Target; + + InvocationExpression tmp = (InvocationExpression)mre.Target; mre = (MemberReferenceExpression)tmp.Target; MatchSimpleLambda(tmp.Arguments.Single(), out parameter, out orderExpression); } @@ -206,7 +192,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms Expression = orderExpression.Detach(), Direction = (mre.MemberName == "OrderBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending) }); - + QueryExpression query = new QueryExpression(); query.Clauses.Add(MakeFromClause(parameter, mre.Target.Detach())); query.Clauses.Add(orderClause); @@ -224,11 +210,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms Expression source2 = invocation.Arguments.ElementAt(0); if (IsNullConditional(source2)) return null; - ParameterDeclaration element1, element2; - Expression key1, key2; - if (!MatchSimpleLambda(invocation.Arguments.ElementAt(1), out element1, out key1)) + if (!MatchSimpleLambda(invocation.Arguments.ElementAt(1), out ParameterDeclaration element1, out Expression key1)) return null; - if (!MatchSimpleLambda(invocation.Arguments.ElementAt(2), out element2, out key2)) + if (!MatchSimpleLambda(invocation.Arguments.ElementAt(2), out ParameterDeclaration element2, out Expression key2)) return null; LambdaExpression lambda = invocation.Arguments.ElementAt(3) as LambdaExpression; if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression) { @@ -316,12 +300,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { if (invocation == null || invocation.Arguments.Count != 1) return false; - MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression; - if (mre == null) + if (!(invocation.Target is MemberReferenceExpression mre)) return false; - ParameterDeclaration parameter; - Expression body; - if (!MatchSimpleLambda(invocation.Arguments.Single(), out parameter, out body)) + if (!MatchSimpleLambda(invocation.Arguments.Single(), out ParameterDeclaration parameter, out _)) return false; if (parameter.Name != expectedParameterName) return false; @@ -337,8 +318,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms /// Matches simple lambdas of the form "a => b" bool MatchSimpleLambda(Expression expr, out ParameterDeclaration parameter, out Expression body) { - var lambda = expr as LambdaExpression; - if (lambda != null && lambda.Parameters.Count == 1 && lambda.Body is Expression) { + if (expr is LambdaExpression lambda && lambda.Parameters.Count == 1 && lambda.Body is Expression) { ParameterDeclaration p = lambda.Parameters.Single(); if (p.ParameterModifier == ParameterModifier.None) { parameter = p; From 2638af9fd63e4873f9c06fe684483758fe062264 Mon Sep 17 00:00:00 2001 From: SimonCropp Date: Tue, 10 Mar 2020 20:51:32 +1100 Subject: [PATCH 06/30] redundant variables --- ILSpy/Analyzers/AnalyzerScope.cs | 1 - ILSpy/DebugInfo/DebugInfoUtils.cs | 1 - ILSpy/Languages/ILAstLanguage.cs | 1 - ILSpy/Languages/Language.cs | 3 --- ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs | 1 - ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs | 1 - ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs | 1 - ILSpy/Metadata/CorTables/ParamTableTreeNode.cs | 1 - ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs | 1 - ILSpy/TreeNodes/AssemblyTreeNode.cs | 1 - 10 files changed, 12 deletions(-) diff --git a/ILSpy/Analyzers/AnalyzerScope.cs b/ILSpy/Analyzers/AnalyzerScope.cs index 68eac7f9d..8048cc1d4 100644 --- a/ILSpy/Analyzers/AnalyzerScope.cs +++ b/ILSpy/Analyzers/AnalyzerScope.cs @@ -84,7 +84,6 @@ namespace ICSharpCode.ILSpy.Analyzers public IEnumerable GetTypesInScope(CancellationToken ct) { if (IsLocal) { - var typeSystem = new DecompilerTypeSystem(TypeScope.ParentModule.PEFile, TypeScope.ParentModule.PEFile.GetAssemblyResolver()); foreach (var type in TreeTraversal.PreOrder(typeScope, t => t.NestedTypes)) { yield return type; } diff --git a/ILSpy/DebugInfo/DebugInfoUtils.cs b/ILSpy/DebugInfo/DebugInfoUtils.cs index f52a790d7..652db992e 100644 --- a/ILSpy/DebugInfo/DebugInfoUtils.cs +++ b/ILSpy/DebugInfo/DebugInfoUtils.cs @@ -32,7 +32,6 @@ namespace ICSharpCode.Decompiler.PdbProvider public static IDebugInfoProvider LoadSymbols(PEFile module) { try { - var reader = module.Reader; // try to open portable pdb file/embedded pdb info: if (TryOpenPortablePdb(module, out var provider, out var pdbFileName)) { return new PortableDebugInfoProvider(pdbFileName, provider); diff --git a/ILSpy/Languages/ILAstLanguage.cs b/ILSpy/Languages/ILAstLanguage.cs index 5aca29d64..5c3917294 100644 --- a/ILSpy/Languages/ILAstLanguage.cs +++ b/ILSpy/Languages/ILAstLanguage.cs @@ -121,7 +121,6 @@ namespace ICSharpCode.ILSpy reader.UseDebugSymbols = options.DecompilerSettings.UseDebugSymbols; var methodBody = module.Reader.GetMethodBody(methodDef.RelativeVirtualAddress); ILFunction il = reader.ReadIL((SRM.MethodDefinitionHandle)method.MetadataToken, methodBody, kind: ILFunctionKind.TopLevelFunction, cancellationToken: options.CancellationToken); - var namespaces = new HashSet(); var decompiler = new CSharpDecompiler(typeSystem, options.DecompilerSettings) { CancellationToken = options.CancellationToken }; ILTransformContext context = decompiler.CreateILTransformContext(il); context.Stepper.StepLimit = options.StepLimit; diff --git a/ILSpy/Languages/Language.cs b/ILSpy/Languages/Language.cs index 40420f1cd..98d6b40db 100644 --- a/ILSpy/Languages/Language.cs +++ b/ILSpy/Languages/Language.cs @@ -489,9 +489,6 @@ namespace ICSharpCode.ILSpy public virtual CodeMappingInfo GetCodeMappingInfo(PEFile module, SRM.EntityHandle member) { - var parts = new Dictionary(); - var locations = new Dictionary(); - var declaringType = member.GetDeclaringType(module.Metadata); if (declaringType.IsNil && member.Kind == SRM.HandleKind.TypeDefinition) { diff --git a/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs b/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs index 01ed40bf9..4d387d989 100644 --- a/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs @@ -45,7 +45,6 @@ namespace ICSharpCode.ILSpy.Metadata tabPage.SupportsLanguageSwitching = false; var view = Helpers.PrepareDataGrid(tabPage, this); - var metadata = module.Metadata; var list = new List(); GenericParamEntry scrollTargetEntry = default; diff --git a/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs index 6ac44c930..e07ad1a7d 100644 --- a/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs @@ -44,7 +44,6 @@ namespace ICSharpCode.ILSpy.Metadata tabPage.SupportsLanguageSwitching = false; var view = Helpers.PrepareDataGrid(tabPage, this); - var metadata = module.Metadata; var list = new List(); MethodImplEntry scrollTargetEntry = default; diff --git a/ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs b/ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs index a8aa84f9b..bec566739 100644 --- a/ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ModuleTableTreeNode.cs @@ -42,7 +42,6 @@ namespace ICSharpCode.ILSpy.Metadata tabPage.SupportsLanguageSwitching = false; var view = Helpers.PrepareDataGrid(tabPage, this); - var metadata = module.Metadata; var list = new List(); ModuleEntry scrollTargetEntry = default; diff --git a/ILSpy/Metadata/CorTables/ParamTableTreeNode.cs b/ILSpy/Metadata/CorTables/ParamTableTreeNode.cs index 1f48bb126..37d82a930 100644 --- a/ILSpy/Metadata/CorTables/ParamTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ParamTableTreeNode.cs @@ -43,7 +43,6 @@ namespace ICSharpCode.ILSpy.Metadata tabPage.SupportsLanguageSwitching = false; var view = Helpers.PrepareDataGrid(tabPage, this); - var metadata = module.Metadata; var list = new List(); ParamEntry scrollTargetEntry = default; diff --git a/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs b/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs index 424bff4e5..1bb0cfa2e 100644 --- a/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/StandAloneSigTableTreeNode.cs @@ -44,7 +44,6 @@ namespace ICSharpCode.ILSpy.Metadata tabPage.SupportsLanguageSwitching = false; var view = Helpers.PrepareDataGrid(tabPage, this); - var metadata = module.Metadata; var list = new List(); StandAloneSigEntry scrollTargetEntry = default; diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index 5f25530f9..883822f11 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -141,7 +141,6 @@ namespace ICSharpCode.ILSpy.TreeNodes } typeSystem = LoadedAssembly.GetTypeSystemOrNull(DecompilerTypeSystem.GetOptions(new DecompilationOptions().DecompilerSettings)); var assembly = (MetadataModule)typeSystem.MainModule; - var metadata = module.Metadata; this.Children.Add(new Metadata.MetadataTreeNode(module, this)); Decompiler.DebugInfo.IDebugInfoProvider debugInfo = LoadedAssembly.GetDebugInfoOrNull(); if (debugInfo is Decompiler.PdbProvider.PortableDebugInfoProvider ppdb) { From 3c2eecb0080b97919def4c58fe6aa586e8223ae9 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 17 Mar 2020 13:53:40 +0100 Subject: [PATCH 07/30] Allow opening ILSpy solution when Core SDK 3.1.200 is installed --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 289df0eba..5b1f41961 100644 --- a/global.json +++ b/global.json @@ -3,6 +3,6 @@ "MSBuild.Sdk.Extras": "2.0.54" }, "sdk": { - "version": "3.1.100" + "version": "3.1" } } From ed7af2addb236e1268d741c2f95d0304ea414f13 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Sun, 22 Mar 2020 12:54:30 +0100 Subject: [PATCH 08/30] Windows: keep using Windows PowerShell, other OSs fall back on pwsh (to reduce deps to get up and running on Windows) --- ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj | 5 ++++- README.md | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index d7cd56cf4..2e73ea060 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -624,7 +624,10 @@ - + + powershell -NoProfile -ExecutionPolicy Bypass -File BuildTools/update-assemblyinfo.ps1 $(Configuration) + + pwsh -NoProfile -ExecutionPolicy Bypass -File BuildTools/update-assemblyinfo.ps1 $(Configuration) diff --git a/README.md b/README.md index 0a2486442..f9aa68fc0 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,6 @@ Windows: - Workload "Visual Studio extension development" (ILSpy.sln contains a VS extension project) - Individual Component "MSVC v142 - VS 2019 C++ x64/x86 build tools (v14.23)" (or similar) - The VC++ toolset is optional; if present it is used for `editbin.exe` to modify the stack size used by ILSpy.exe from 1MB to 16MB, because the decompiler makes heavy use of recursion, where small stack sizes lead to problems in very complex methods. -- Make sure [PowerShell](https://github.com/PowerShell/PowerShell) is installed (formerly known as PowerShell Core) - recommended way of installation via [choco install powershell-core](https://chocolatey.org/packages/powershell-core) - Check out the ILSpy repository using git. - Execute `git submodule update --init --recursive` to download the ILSpy-Tests submodule (used by some test cases). - Open ILSpy.sln in Visual Studio. From c2a2cf43f2cb499c7a26920d0fc86981028bc045 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 24 Mar 2020 18:43:28 +0100 Subject: [PATCH 09/30] Fix #1482: SequencePointBuilder fails with an assertion when trying to create sequence points for LINQ expressions --- ICSharpCode.Decompiler/CSharp/Annotations.cs | 30 ++++++++++++ .../CSharp/SequencePointBuilder.cs | 42 +++++++++++++++++ .../Transforms/CombineQueryExpressions.cs | 21 +++++---- .../Transforms/IntroduceQueryExpressions.cs | 38 ++++++++++----- .../DebugInfo/DebugInfoGenerator.cs | 47 ++++++++++++++++++- 5 files changed, 155 insertions(+), 23 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/Annotations.cs b/ICSharpCode.Decompiler/CSharp/Annotations.cs index ec5006cd8..645414250 100644 --- a/ICSharpCode.Decompiler/CSharp/Annotations.cs +++ b/ICSharpCode.Decompiler/CSharp/Annotations.cs @@ -289,4 +289,34 @@ namespace ICSharpCode.Decompiler.CSharp this.ConversionResolveResult = conversionResolveResult; } } + + /// + /// Annotates a QueryGroupClause with the ILFunctions of each (implicit lambda) expression. + /// + public class QueryGroupClauseAnnotation + { + public readonly ILFunction KeyLambda; + public readonly ILFunction ProjectionLambda; + + public QueryGroupClauseAnnotation(ILFunction key, ILFunction projection) + { + this.KeyLambda = key; + this.ProjectionLambda = projection; + } + } + + /// + /// Annotates a QueryJoinClause with the ILFunctions of each (implicit lambda) expression. + /// + public class QueryJoinClauseAnnotation + { + public readonly ILFunction OnLambda; + public readonly ILFunction EqualsLambda; + + public QueryJoinClauseAnnotation(ILFunction on, ILFunction equals) + { + this.OnLambda = on; + this.EqualsLambda = equals; + } + } } diff --git a/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs b/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs index 8fbcf57e5..7c7be5344 100644 --- a/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs @@ -160,6 +160,48 @@ namespace ICSharpCode.Decompiler.CSharp VisitAsSequencePoint(lambdaExpression.Body); } + public override void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) + { + AddToSequencePoint(queryContinuationClause); + VisitAsSequencePoint(queryContinuationClause.PrecedingQuery); + } + + public override void VisitQueryFromClause(QueryFromClause queryFromClause) + { + if (queryFromClause.Parent.FirstChild != queryFromClause) { + AddToSequencePoint(queryFromClause); + VisitAsSequencePoint(queryFromClause.Expression); + } else { + base.VisitQueryFromClause(queryFromClause); + } + } + + public override void VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + AddToSequencePoint(queryGroupClause); + VisitAsSequencePoint(queryGroupClause.Projection); + VisitAsSequencePoint(queryGroupClause.Key); + } + + public override void VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + AddToSequencePoint(queryJoinClause); + VisitAsSequencePoint(queryJoinClause.OnExpression); + VisitAsSequencePoint(queryJoinClause.EqualsExpression); + } + + public override void VisitQueryLetClause(QueryLetClause queryLetClause) + { + AddToSequencePoint(queryLetClause); + VisitAsSequencePoint(queryLetClause.Expression); + } + + public override void VisitQueryOrdering(QueryOrdering queryOrdering) + { + AddToSequencePoint(queryOrdering); + VisitAsSequencePoint(queryOrdering.Expression); + } + public override void VisitQuerySelectClause(QuerySelectClause querySelectClause) { AddToSequencePoint(querySelectClause); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs b/ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs index 9dbfd458a..a8a178da2 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs @@ -43,25 +43,26 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms TypeArguments = { new AnyNode("targetType") } }}; - void CombineQueries(AstNode node, Dictionary letIdentifiers) + void CombineQueries(AstNode node, Dictionary fromOrLetIdentifiers) { AstNode next; for (AstNode child = node.FirstChild; child != null; child = next) { - // store referece to next child before transformation + // store reference to next child before transformation next = child.NextSibling; - CombineQueries(child, letIdentifiers); + CombineQueries(child, fromOrLetIdentifiers); } QueryExpression query = node as QueryExpression; if (query != null) { QueryFromClause fromClause = (QueryFromClause)query.Clauses.First(); QueryExpression innerQuery = fromClause.Expression as QueryExpression; if (innerQuery != null) { - if (TryRemoveTransparentIdentifier(query, fromClause, innerQuery, letIdentifiers)) { - RemoveTransparentIdentifierReferences(query, letIdentifiers); + if (TryRemoveTransparentIdentifier(query, fromClause, innerQuery, fromOrLetIdentifiers)) { + RemoveTransparentIdentifierReferences(query, fromOrLetIdentifiers); } else { QueryContinuationClause continuation = new QueryContinuationClause(); continuation.PrecedingQuery = innerQuery.Detach(); continuation.Identifier = fromClause.Identifier; + continuation.CopyAnnotationsFrom(fromClause); fromClause.ReplaceWith(continuation); } } else { @@ -119,8 +120,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms foreach (var expr in match.Get("expr")) { switch (expr) { case IdentifierExpression identifier: - // nothing to add - continue; + letClauses[identifier.Identifier] = identifier.Annotation(); + break; case MemberReferenceExpression member: AddQueryLetClause(member.MemberName, member); break; @@ -148,10 +149,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms /// /// Removes all occurrences of transparent identifiers /// - void RemoveTransparentIdentifierReferences(AstNode node, Dictionary letClauses) + void RemoveTransparentIdentifierReferences(AstNode node, Dictionary fromOrLetIdentifiers) { foreach (AstNode child in node.Children) { - RemoveTransparentIdentifierReferences(child, letClauses); + RemoveTransparentIdentifierReferences(child, fromOrLetIdentifiers); } MemberReferenceExpression mre = node as MemberReferenceExpression; if (mre != null) { @@ -161,7 +162,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms mre.TypeArguments.MoveTo(newIdent.TypeArguments); newIdent.CopyAnnotationsFrom(mre); newIdent.RemoveAnnotations(); // remove the reference to the property of the anonymous type - if (letClauses.TryGetValue(mre.MemberName, out var annotation)) + if (fromOrLetIdentifiers.TryGetValue(mre.MemberName, out var annotation)) newIdent.AddAnnotation(annotation); mre.ReplaceWith(newIdent); return; diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs index 742ee42d7..d4a4b7555 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs @@ -107,16 +107,24 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms case "GroupBy": { if (invocation.Arguments.Count == 2) { - if (MatchSimpleLambda(invocation.Arguments.ElementAt(0), out ParameterDeclaration parameter1, out Expression keySelector) - && MatchSimpleLambda(invocation.Arguments.ElementAt(1), out ParameterDeclaration parameter2, out Expression elementSelector) + Expression keyLambda = invocation.Arguments.ElementAt(0); + Expression projectionLambda = invocation.Arguments.ElementAt(1); + if (MatchSimpleLambda(keyLambda, out ParameterDeclaration parameter1, out Expression keySelector) + && MatchSimpleLambda(projectionLambda, out ParameterDeclaration parameter2, out Expression elementSelector) && parameter1.Name == parameter2.Name) { QueryExpression query = new QueryExpression(); query.Clauses.Add(MakeFromClause(parameter1, mre.Target.Detach())); - query.Clauses.Add(new QueryGroupClause { Projection = elementSelector.Detach(), Key = keySelector.Detach() }); + var queryGroupClause = new QueryGroupClause { + Projection = elementSelector.Detach(), + Key = keySelector.Detach() + }; + queryGroupClause.AddAnnotation(new QueryGroupClauseAnnotation(keyLambda.Annotation(), projectionLambda.Annotation())); + query.Clauses.Add(queryGroupClause); return query; } } else if (invocation.Arguments.Count == 1) { - if (MatchSimpleLambda(invocation.Arguments.Single(), out ParameterDeclaration parameter, out Expression keySelector)) { + Expression lambda = invocation.Arguments.Single(); + if (MatchSimpleLambda(lambda, out ParameterDeclaration parameter, out Expression keySelector)) { QueryExpression query = new QueryExpression(); query.Clauses.Add(MakeFromClause(parameter, mre.Target.Detach())); query.Clauses.Add(new QueryGroupClause { Projection = new IdentifierExpression(parameter.Name).CopyAnnotationsFrom(parameter), Key = keySelector.Detach() }); @@ -129,7 +137,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { if (invocation.Arguments.Count != 2) return null; - if (!MatchSimpleLambda(invocation.Arguments.ElementAt(0), out ParameterDeclaration parameter, out Expression collectionSelector)) + var fromExpressionLambda = invocation.Arguments.ElementAt(0); + if (!MatchSimpleLambda(fromExpressionLambda, out ParameterDeclaration parameter, out Expression collectionSelector)) return null; if (IsNullConditional(collectionSelector)) return null; @@ -140,7 +149,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (p1.Name == parameter.Name) { QueryExpression query = new QueryExpression(); query.Clauses.Add(MakeFromClause(p1, mre.Target.Detach())); - query.Clauses.Add(MakeFromClause(p2, collectionSelector.Detach())); + query.Clauses.Add(MakeFromClause(p2, collectionSelector.Detach()).CopyAnnotationsFrom(fromExpressionLambda)); query.Clauses.Add(new QuerySelectClause { Expression = WrapExpressionInParenthesesIfNecessary(((Expression)lambda.Body).Detach(), parameter.Name) }); return query; } @@ -171,7 +180,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return null; if (!IsComplexQuery(mre)) return null; - if (MatchSimpleLambda(invocation.Arguments.Single(), out ParameterDeclaration parameter, out Expression orderExpression)) { + var lambda = invocation.Arguments.Single(); + if (MatchSimpleLambda(lambda, out ParameterDeclaration parameter, out Expression orderExpression)) { if (ValidateThenByChain(invocation, parameter.Name)) { QueryOrderClause orderClause = new QueryOrderClause(); while (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending") { @@ -180,18 +190,19 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms null, new QueryOrdering { Expression = orderExpression.Detach(), Direction = (mre.MemberName == "ThenBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending) - }); + }.CopyAnnotationsFrom(lambda)); InvocationExpression tmp = (InvocationExpression)mre.Target; mre = (MemberReferenceExpression)tmp.Target; - MatchSimpleLambda(tmp.Arguments.Single(), out parameter, out orderExpression); + lambda = tmp.Arguments.Single(); + MatchSimpleLambda(lambda, out parameter, out orderExpression); } // insert new ordering at beginning orderClause.Orderings.InsertAfter( null, new QueryOrdering { Expression = orderExpression.Detach(), Direction = (mre.MemberName == "OrderBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending) - }); + }.CopyAnnotationsFrom(lambda)); QueryExpression query = new QueryExpression(); query.Clauses.Add(MakeFromClause(parameter, mre.Target.Detach())); @@ -210,9 +221,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms Expression source2 = invocation.Arguments.ElementAt(0); if (IsNullConditional(source2)) return null; - if (!MatchSimpleLambda(invocation.Arguments.ElementAt(1), out ParameterDeclaration element1, out Expression key1)) + Expression outerLambda = invocation.Arguments.ElementAt(1); + if (!MatchSimpleLambda(outerLambda, out ParameterDeclaration element1, out Expression key1)) return null; - if (!MatchSimpleLambda(invocation.Arguments.ElementAt(2), out ParameterDeclaration element2, out Expression key2)) + Expression innerLambda = invocation.Arguments.ElementAt(2); + if (!MatchSimpleLambda(innerLambda, out ParameterDeclaration element2, out Expression key2)) return null; LambdaExpression lambda = invocation.Arguments.ElementAt(3) as LambdaExpression; if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression) { @@ -229,6 +242,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (mre.MemberName == "GroupJoin") { joinClause.IntoIdentifier = p2.Name; // into p2.Name } + joinClause.AddAnnotation(new QueryJoinClauseAnnotation(outerLambda.Annotation(), innerLambda.Annotation())); query.Clauses.Add(joinClause); query.Clauses.Add(new QuerySelectClause { Expression = ((Expression)lambda.Body).Detach() }.CopyAnnotationsFrom(lambda)); return query; diff --git a/ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs b/ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs index 6ba293229..cb04bc4da 100644 --- a/ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs +++ b/ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs @@ -133,6 +133,47 @@ namespace ICSharpCode.Decompiler.DebugInfo HandleMethod(anonymousMethodExpression); } + public override void VisitQueryFromClause(QueryFromClause queryFromClause) + { + if (queryFromClause.Parent.FirstChild != queryFromClause) { + HandleMethod(queryFromClause); + } else { + base.VisitQueryFromClause(queryFromClause); + } + } + + public override void VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + var annotation = queryGroupClause.Annotation(); + if (annotation == null) { + base.VisitQueryGroupClause(queryGroupClause); + return; + } + HandleMethod(queryGroupClause.Projection, annotation.ProjectionLambda); + HandleMethod(queryGroupClause.Key, annotation.KeyLambda); + } + + public override void VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + var annotation = queryJoinClause.Annotation(); + if (annotation == null) { + base.VisitQueryJoinClause(queryJoinClause); + return; + } + HandleMethod(queryJoinClause.OnExpression, annotation.OnLambda); + HandleMethod(queryJoinClause.EqualsExpression, annotation.EqualsLambda); + } + + public override void VisitQueryLetClause(QueryLetClause queryLetClause) + { + HandleMethod(queryLetClause); + } + + public override void VisitQueryOrdering(QueryOrdering queryOrdering) + { + HandleMethod(queryOrdering); + } + public override void VisitQuerySelectClause(QuerySelectClause querySelectClause) { HandleMethod(querySelectClause); @@ -144,11 +185,15 @@ namespace ICSharpCode.Decompiler.DebugInfo } void HandleMethod(AstNode node) + { + HandleMethod(node, node.Annotation()); + } + + void HandleMethod(AstNode node, ILFunction function) { // Look into method body, e.g. in order to find lambdas VisitChildren(node); - var function = node.Annotation(); if (function == null || function.Method == null || function.Method.MetadataToken.IsNil) return; this.functions.Add(function); From 7d1d7b2563b217022d4f0dcab6b054e610b44b91 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 24 Mar 2020 18:44:20 +0100 Subject: [PATCH 10/30] Fix EndLocation of double-typed PrimitiveExpression --- .../CSharp/OutputVisitor/TextWriterTokenWriter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs index a41042ca0..a4c0a5e5e 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs @@ -336,6 +336,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor number += ".0"; } textWriter.Write(number); + column += number.Length; Length += number.Length; } else if (value is IFormattable) { StringBuilder b = new StringBuilder(); From e10d255f1797d8c988766eda3786cccc816804a5 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 29 Mar 2020 10:00:28 +0200 Subject: [PATCH 11/30] Fix #1960: OptionalHeaderTreeNode: Tiny copy+paste text error --- ILSpy/Metadata/OptionalHeaderTreeNode.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ILSpy/Metadata/OptionalHeaderTreeNode.cs b/ILSpy/Metadata/OptionalHeaderTreeNode.cs index cce72b4cc..28c939771 100644 --- a/ILSpy/Metadata/OptionalHeaderTreeNode.cs +++ b/ILSpy/Metadata/OptionalHeaderTreeNode.cs @@ -65,8 +65,8 @@ namespace ICSharpCode.ILSpy.Metadata entries.Add(new Entry(reader.Offset, reader.ReadByte(), 1, "Major Linker Version", "")); entries.Add(new Entry(reader.Offset, reader.ReadByte(), 1, "Minor Linker Version", "")); entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "Code Size", "Size of the code (text) section, or the sum of all code sections if there are multiple sections.")); - entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "Initialized Data Size", "Size of the initialized data section, or the sum of all code sections if there are multiple data sections.")); - entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "Uninitialized Data Size", "Size of the uninitialized data section, or the sum of all code sections if there are multiple uninitialized data sections.")); + entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "Initialized Data Size", "Size of the initialized data section, or the sum of all initialized data sections if there are multiple data sections.")); + entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "Uninitialized Data Size", "Size of the uninitialized data section, or the sum of all uninitialized data sections if there are multiple uninitialized data sections.")); entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "Entry Point RVA", "RVA of entry point, needs to point to bytes 0xFF 0x25 followed by the RVA in a section marked execute / read for EXEs or 0 for DLLs")); entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "Base Of Code", "RVA of the code section.")); entries.Add(new Entry(reader.Offset, header.Magic == PEMagic.PE32Plus ? reader.ReadUInt64() : reader.ReadUInt32(), header.Magic == PEMagic.PE32Plus ? 8 : 4, "Base Of Data", "RVA of the data section.")); From 7c7328df3291378118be29e1855ced305f3e5086 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 29 Mar 2020 12:21:23 +0200 Subject: [PATCH 12/30] Fix #1955: struct 'base' access to ValueType mis-decompiles --- .../TestCases/Pretty/QualifierTests.cs | 11 ++++++++++ .../CSharp/ExpressionBuilder.cs | 20 ++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs index b09df51e7..35c042533 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs @@ -74,6 +74,17 @@ namespace ICSharpCode.Decompiler.Tests.Pretty { } + + public string ThisQualifierWithCast() + { + return ((object)this).ToString(); + } + + public override string ToString() + { + // decompiled as return ((ValueType)this).ToString(); + return base.ToString(); + } } internal class Parent diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 9856960d4..cb0047375 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1999,7 +1999,7 @@ namespace ICSharpCode.Decompiler.CSharp // If references are missing member.IsStatic might not be set correctly. // Additionally check target for null, in order to avoid a crash. if (!memberStatic && target != null) { - if (nonVirtualInvocation && target.MatchLdThis() && memberDeclaringType.GetDefinition() != resolver.CurrentTypeDefinition) { + if (nonVirtualInvocation && MatchLdThis(target) && memberDeclaringType.GetDefinition() != resolver.CurrentTypeDefinition) { return new BaseReferenceExpression() .WithILInstruction(target) .WithRR(new ThisResolveResult(memberDeclaringType, nonVirtualInvocation)); @@ -2034,6 +2034,24 @@ namespace ICSharpCode.Decompiler.CSharp .WithoutILInstruction() .WithRR(new TypeResolveResult(memberDeclaringType)); } + + bool MatchLdThis(ILInstruction inst) + { + // ldloc this + if (inst.MatchLdThis()) + return true; + if (resolver.CurrentTypeDefinition.Kind == TypeKind.Struct) { + // box T(ldobj T(ldloc this)) + if (!inst.MatchBox(out var arg, out var type)) + return false; + if (!arg.MatchLdObj(out var arg2, out var type2)) + return false; + if (!type.Equals(type2) || !type.Equals(resolver.CurrentTypeDefinition)) + return false; + return arg2.MatchLdThis(); + } + return false; + } } private TranslatedExpression EnsureTargetNotNullable(TranslatedExpression expr) From e748e71b568e5f6bb4589388fd2aef25601c317a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 29 Mar 2020 12:54:18 +0200 Subject: [PATCH 13/30] Implement workaround for #1961 --- .../CSharp/StatementBuilder.cs | 6 +++ .../IL/Transforms/DelegateConstruction.cs | 51 ++++++++++++------- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index b32efc029..28ce01f4e 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -992,6 +992,12 @@ namespace ICSharpCode.Decompiler.CSharp stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function)); stmt.ReturnType = exprBuilder.ConvertType(function.Method.ReturnType); stmt.Body = nestedBuilder.ConvertAsBlock(function.Body); + + Comment prev = null; + foreach (string warning in function.Warnings) { + stmt.Body.InsertChildAfter(prev, prev = new Comment(warning), Roles.Comment); + } + if (function.Method.TypeParameters.Count > 0) { var astBuilder = exprBuilder.astBuilder; if (astBuilder.ShowTypeParameters) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs index 07da7dd7b..5d68b4131 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -31,32 +31,42 @@ namespace ICSharpCode.Decompiler.IL.Transforms { ILTransformContext context; ITypeResolveContext decompilationContext; + readonly Stack activeMethods = new Stack(); void IILTransform.Run(ILFunction function, ILTransformContext context) { if (!context.Settings.AnonymousMethods) return; - this.context = context; - this.decompilationContext = new SimpleTypeResolveContext(function.Method); - var cancellationToken = context.CancellationToken; - foreach (var inst in function.Descendants) { - cancellationToken.ThrowIfCancellationRequested(); - if (inst is NewObj call) { - context.StepStartGroup($"TransformDelegateConstruction {call.StartILOffset}", call); - ILFunction f = TransformDelegateConstruction(call, out ILInstruction target); - if (f != null && target is IInstructionWithVariableOperand instWithVar) { - if (instWithVar.Variable.Kind == VariableKind.Local) { - instWithVar.Variable.Kind = VariableKind.DisplayClassLocal; - } - var displayClassTypeDef = instWithVar.Variable.Type.GetDefinition(); - if (instWithVar.Variable.IsSingleDefinition && instWithVar.Variable.StoreInstructions.SingleOrDefault() is StLoc store) { - if (store.Value is NewObj newObj) { - instWithVar.Variable.CaptureScope = BlockContainer.FindClosestContainer(store); + var prevContext = this.context; + var prevDecompilationContext = this.decompilationContext; + try { + activeMethods.Push((MethodDefinitionHandle)function.Method.MetadataToken); + this.context = context; + this.decompilationContext = new SimpleTypeResolveContext(function.Method); + var cancellationToken = context.CancellationToken; + foreach (var inst in function.Descendants) { + cancellationToken.ThrowIfCancellationRequested(); + if (inst is NewObj call) { + context.StepStartGroup($"TransformDelegateConstruction {call.StartILOffset}", call); + ILFunction f = TransformDelegateConstruction(call, out ILInstruction target); + if (f != null && target is IInstructionWithVariableOperand instWithVar) { + if (instWithVar.Variable.Kind == VariableKind.Local) { + instWithVar.Variable.Kind = VariableKind.DisplayClassLocal; + } + var displayClassTypeDef = instWithVar.Variable.Type.GetDefinition(); + if (instWithVar.Variable.IsSingleDefinition && instWithVar.Variable.StoreInstructions.SingleOrDefault() is StLoc store) { + if (store.Value is NewObj newObj) { + instWithVar.Variable.CaptureScope = BlockContainer.FindClosestContainer(store); + } } } + context.StepEndGroup(); } - context.StepEndGroup(); } + } finally { + this.context = prevContext; + this.decompilationContext = prevDecompilationContext; + activeMethods.Pop(); } } @@ -131,6 +141,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod, context)) return null; target = value.Arguments[0]; + var handle = (MethodDefinitionHandle)targetMethod.MetadataToken; + if (activeMethods.Contains(handle)) { + this.context.Function.Warnings.Add(" Found self-referencing delegate construction. Abort transformation to avoid stack overflow."); + return null; + } var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken); if (!methodDefinition.HasBody()) return null; @@ -157,7 +172,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms function.AcceptVisitor(new ReplaceDelegateTargetVisitor(target, function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter))); // handle nested lambdas nestedContext.StepStartGroup("DelegateConstruction (nested lambdas)", function); - ((IILTransform)new DelegateConstruction()).Run(function, nestedContext); + ((IILTransform)this).Run(function, nestedContext); nestedContext.StepEndGroup(); function.AddILRange(target); function.AddILRange(value); From 371d732c0a41a07acaaa46317939f56f2ccdd937 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 29 Mar 2020 21:18:23 +0200 Subject: [PATCH 14/30] Fix #1881: Not properly reusing names from PDB#2 --- .../Helpers/Tester.cs | 21 ++++++++--- .../ICSharpCode.Decompiler.Tests.csproj | 4 +++ .../PdbGenerationTestRunner.cs | 2 +- .../PrettyTestRunner.cs | 2 +- .../TestCases/Pretty/VariableNaming.cs | 35 ++++++++++++++++++- .../IL/Transforms/AssignVariableNames.cs | 35 +++++++++++++------ 6 files changed, 80 insertions(+), 19 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs index 85e2cc4a1..0dc01b9b4 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs @@ -56,6 +56,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers UseMcs = 0x20, ReferenceVisualBasic = 0x40, ReferenceCore = 0x80, + GeneratePdb = 0x100, } [Flags] @@ -272,7 +273,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers preprocessorSymbols: preprocessorSymbols.ToArray(), languageVersion: Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8 ); - var syntaxTrees = sourceFileNames.Select(f => SyntaxFactory.ParseSyntaxTree(File.ReadAllText(f), parseOptions, path: f)); + var syntaxTrees = sourceFileNames.Select(f => SyntaxFactory.ParseSyntaxTree(File.ReadAllText(f), parseOptions, path: f, encoding: Encoding.UTF8)); if (flags.HasFlag(CompilerOptions.ReferenceCore)) { syntaxTrees = syntaxTrees.Concat(new[] { SyntaxFactory.ParseSyntaxTree(targetFrameworkAttributeSnippet) }); } @@ -296,7 +297,10 @@ namespace ICSharpCode.Decompiler.Tests.Helpers )); CompilerResults results = new CompilerResults(new TempFileCollection()); results.PathToAssembly = outputFileName ?? Path.GetTempFileName(); - var emitResult = compilation.Emit(results.PathToAssembly); + string pdbName = null; + if (flags.HasFlag(CompilerOptions.GeneratePdb)) + pdbName = Path.ChangeExtension(outputFileName, ".pdb"); + var emitResult = compilation.Emit(results.PathToAssembly, pdbName); if (!emitResult.Success) { StringBuilder b = new StringBuilder("Compiler error:"); foreach (var diag in emitResult.Diagnostics) { @@ -360,7 +364,12 @@ namespace ICSharpCode.Decompiler.Tests.Helpers CompilerParameters options = new CompilerParameters(); options.GenerateExecutable = !flags.HasFlag(CompilerOptions.Library); options.CompilerOptions = "/unsafe /o" + (flags.HasFlag(CompilerOptions.Optimize) ? "+" : "-"); - options.CompilerOptions += (flags.HasFlag(CompilerOptions.UseDebug) ? " /debug" : ""); + string debugOption = " /debug"; + if (flags.HasFlag(CompilerOptions.GeneratePdb)) { + debugOption += ":full"; + options.IncludeDebugInformation = true; + } + options.CompilerOptions += (flags.HasFlag(CompilerOptions.UseDebug) ? debugOption : ""); options.CompilerOptions += (flags.HasFlag(CompilerOptions.Force32Bit) ? " /platform:anycpu32bitpreferred" : ""); if (preprocessorSymbols.Count > 0) { options.CompilerOptions += " /d:" + string.Join(";", preprocessorSymbols); @@ -368,7 +377,6 @@ namespace ICSharpCode.Decompiler.Tests.Helpers if (outputFileName != null) { options.OutputAssembly = outputFileName; } - options.ReferencedAssemblies.Add("System.dll"); options.ReferencedAssemblies.Add("System.Core.dll"); options.ReferencedAssemblies.Add("System.Xml.dll"); @@ -397,7 +405,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers } } - public static void CompileCSharpWithPdb(string assemblyName, Dictionary sourceFiles, PdbToXmlOptions options) + public static void CompileCSharpWithPdb(string assemblyName, Dictionary sourceFiles) { var parseOptions = new CSharpParseOptions(languageVersion: Microsoft.CodeAnalysis.CSharp.LanguageVersion.Latest); @@ -484,6 +492,9 @@ namespace ICSharpCode.Decompiler.Tests.Helpers decompiler.AstTransforms.Insert(0, new RemoveCompilerAttribute()); decompiler.AstTransforms.Insert(0, new RemoveNamespaceMy()); decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers()); + var pdbFileName = Path.ChangeExtension(assemblyFileName, ".pdb"); + if (File.Exists(pdbFileName)) + decompiler.DebugInfoProvider = PdbProvider.DebugInfoUtils.FromFile(module, pdbFileName); var syntaxTree = decompiler.DecompileWholeModuleAsSingleFile(sortTypes: true); StringWriter output = new StringWriter(); diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 1f9c4232c..2dea3c340 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -49,6 +49,7 @@ + @@ -80,6 +81,9 @@ + + + diff --git a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs index 78de11172..6077d6c15 100644 --- a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs @@ -51,7 +51,7 @@ namespace ICSharpCode.Decompiler.Tests string xmlContent = File.ReadAllText(xmlFile); XDocument document = XDocument.Parse(xmlContent); var files = document.Descendants("file").ToDictionary(f => f.Attribute("name").Value, f => f.Value); - Tester.CompileCSharpWithPdb(Path.Combine(TestCasePath, testName + ".expected"), files, options); + Tester.CompileCSharpWithPdb(Path.Combine(TestCasePath, testName + ".expected"), files); string peFileName = Path.Combine(TestCasePath, testName + ".expected.dll"); string pdbFileName = Path.Combine(TestCasePath, testName + ".expected.pdb"); diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 8af4d4a13..66456641c 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -284,7 +284,7 @@ namespace ICSharpCode.Decompiler.Tests [Test] public void VariableNaming([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { - RunForLibrary(cscOptions: cscOptions); + RunForLibrary(cscOptions: cscOptions | CompilerOptions.GeneratePdb); } [Test] diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/VariableNaming.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/VariableNaming.cs index 169299791..80b453a73 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/VariableNaming.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/VariableNaming.cs @@ -1,7 +1,17 @@ -namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +#if !OPT +using System; +#endif + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class VariableNaming { + private enum MyEnum + { + VALUE1 = 1, + VALUE2 + } + private class C { public string Name; @@ -25,5 +35,28 @@ string text2 = c.Text; #endif } + +#if !OPT + private void Issue1841() + { + C gen1 = new C(); + C gen2 = new C(); + C gen3 = new C(); + C gen4 = new C(); + } + + private void Issue1881() + { + MyEnum enumLocal1 = MyEnum.VALUE1; + MyEnum enumLocal2 = (MyEnum)0; + enumLocal2 = MyEnum.VALUE1; + object enumLocal3 = MyEnum.VALUE2; + object enumLocal4 = new object(); + enumLocal4 = MyEnum.VALUE2; + ValueType enumLocal5 = MyEnum.VALUE1; + ValueType enumLocal6 = (MyEnum)0; + enumLocal6 = MyEnum.VALUE2; + } +#endif } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs index e5f28ff72..02b25933c 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs @@ -20,12 +20,8 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Text; -using System.Threading.Tasks; using Humanizer; using ICSharpCode.Decompiler.CSharp.OutputVisitor; -using ICSharpCode.Decompiler.IL; -using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; @@ -141,6 +137,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // remove unused variables before assigning names function.Variables.RemoveDead(); int numDisplayClassLocals = 0; + Dictionary assignedLocalSignatureIndices = new Dictionary(); foreach (var v in function.Variables.OrderBy(v => v.Name)) { switch (v.Kind) { case VariableKind.Parameter: // ignore @@ -151,16 +148,32 @@ namespace ICSharpCode.Decompiler.IL.Transforms case VariableKind.DisplayClassLocal: v.Name = "CS$<>8__locals" + (numDisplayClassLocals++); break; - default: - if (v.HasGeneratedName || !IsValidName(v.Name) || ConflictWithLocal(v)) { - // don't use the name from the debug symbols if it looks like a generated name - v.Name = null; + case VariableKind.Local when v.Index != null: + if (assignedLocalSignatureIndices.TryGetValue(v.Index.Value, out string name)) { + // make sure all local ILVariables that refer to the same slot in the locals signature + // are assigned the same name. + v.Name = name; } else { - // use the name from the debug symbols - // (but ensure we don't use the same name for two variables) - v.Name = GetAlternativeName(v.Name); + AssignName(); + // Remember the newly assigned name: + assignedLocalSignatureIndices.Add(v.Index.Value, v.Name); } break; + default: + AssignName(); + break; + } + + void AssignName() + { + if (v.HasGeneratedName || !IsValidName(v.Name) || ConflictWithLocal(v)) { + // don't use the name from the debug symbols if it looks like a generated name + v.Name = null; + } else { + // use the name from the debug symbols + // (but ensure we don't use the same name for two variables) + v.Name = GetAlternativeName(v.Name); + } } } foreach (var localFunction in function.LocalFunctions) { From 5ee172d4c4715fa7911589a47d503a3c143cc49d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 29 Mar 2020 21:30:45 +0200 Subject: [PATCH 15/30] Fix #1900: RemoveDeadVariableInit.ResetHasInitialValueFlag should handle local functions --- .../IL/Transforms/TransformDisplayClassUsage.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 795800433..f843b6b7f 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -84,7 +84,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms containingBlock.Instructions.Remove(store); } } - RemoveDeadVariableInit.ResetHasInitialValueFlag(function, context); + foreach (var f in TreeTraversal.PostOrder(function, f => f.LocalFunctions)) + RemoveDeadVariableInit.ResetHasInitialValueFlag(f, context); } finally { instructionsToRemove.Clear(); displayClasses.Clear(); From 05568da1a4de6b22cd4b58738174c4cb63b63cb5 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 30 Mar 2020 15:10:41 +0200 Subject: [PATCH 16/30] use vstest.console.exe on AppVeyor --- appveyor.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 2500014d2..59ec497fc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -24,11 +24,8 @@ build_script: after_build: - 7z a ILSpy_binaries.zip %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net472\*.dll %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net472\*.exe %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net472\*.config %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net472\*\ILSpy.resources.dll -test: - assemblies: - - 'ICSharpCode.Decompiler.Tests\bin\%configuration%\net472\ICSharpCode.Decompiler.Tests.exe' - - 'ILSpy.Tests\bin\%configuration%\net472\ILSpy.Tests.exe' - - 'ILSpy.BamlDecompiler.Tests\bin\%configuration%\net472\ILSpy.BamlDecompiler.Tests.dll' +test_script: +- vstest.console.exe /logger:Appveyor "ICSharpCode.Decompiler.Tests\bin\%configuration%\net472\ICSharpCode.Decompiler.Tests.exe" "ILSpy.Tests\bin\%configuration%\net472\ILSpy.Tests.exe" "ILSpy.BamlDecompiler.Tests\bin\%configuration%\net472\ILSpy.BamlDecompiler.Tests.dll" after_test: - python BuildTools\tidy.py From f3b5642314ff8eedbb28ed8aa5e989a881f02293 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 30 Mar 2020 15:16:30 +0200 Subject: [PATCH 17/30] remove all tests except ICS.D.Tests.exe --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 59ec497fc..11f0c0a7e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -25,7 +25,7 @@ after_build: - 7z a ILSpy_binaries.zip %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net472\*.dll %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net472\*.exe %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net472\*.config %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net472\*\ILSpy.resources.dll test_script: -- vstest.console.exe /logger:Appveyor "ICSharpCode.Decompiler.Tests\bin\%configuration%\net472\ICSharpCode.Decompiler.Tests.exe" "ILSpy.Tests\bin\%configuration%\net472\ILSpy.Tests.exe" "ILSpy.BamlDecompiler.Tests\bin\%configuration%\net472\ILSpy.BamlDecompiler.Tests.dll" +- vstest.console.exe /logger:Appveyor "ICSharpCode.Decompiler.Tests\bin\%configuration%\net472\ICSharpCode.Decompiler.Tests.exe" after_test: - python BuildTools\tidy.py From c36a3c9c2082b00433bff8dda80e9a54b6a37b9c Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 30 Mar 2020 15:29:07 +0200 Subject: [PATCH 18/30] Add remaining test projects + parallelize --- appveyor.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 11f0c0a7e..70d3d79b0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -25,7 +25,9 @@ after_build: - 7z a ILSpy_binaries.zip %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net472\*.dll %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net472\*.exe %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net472\*.config %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net472\*\ILSpy.resources.dll test_script: -- vstest.console.exe /logger:Appveyor "ICSharpCode.Decompiler.Tests\bin\%configuration%\net472\ICSharpCode.Decompiler.Tests.exe" +- vstest.console.exe /logger:Appveyor /Parallel "ICSharpCode.Decompiler.Tests\bin\%configuration%\net472\ICSharpCode.Decompiler.Tests.exe" +- vstest.console.exe /logger:Appveyor /Parallel "ILSpy.Tests\bin\%configuration%\net472\ILSpy.Tests.exe" +- vstest.console.exe /logger:Appveyor /Parallel "ILSpy.BamlDecompiler.Tests\bin\%configuration%\net472\ILSpy.BamlDecompiler.Tests.dll" after_test: - python BuildTools\tidy.py From 4d5cdacc68b9492f09d3bdf8d51c14688f849639 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 30 Mar 2020 16:22:08 +0200 Subject: [PATCH 19/30] once again --- appveyor.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 70d3d79b0..151bac6c2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -25,9 +25,7 @@ after_build: - 7z a ILSpy_binaries.zip %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net472\*.dll %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net472\*.exe %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net472\*.config %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net472\*\ILSpy.resources.dll test_script: -- vstest.console.exe /logger:Appveyor /Parallel "ICSharpCode.Decompiler.Tests\bin\%configuration%\net472\ICSharpCode.Decompiler.Tests.exe" -- vstest.console.exe /logger:Appveyor /Parallel "ILSpy.Tests\bin\%configuration%\net472\ILSpy.Tests.exe" -- vstest.console.exe /logger:Appveyor /Parallel "ILSpy.BamlDecompiler.Tests\bin\%configuration%\net472\ILSpy.BamlDecompiler.Tests.dll" +- vstest.console.exe /logger:Appveyor /Parallel "ICSharpCode.Decompiler.Tests\bin\%configuration%\net472\ICSharpCode.Decompiler.Tests.exe" "ILSpy.Tests\bin\%configuration%\net472\ILSpy.Tests.exe" "ILSpy.BamlDecompiler.Tests\bin\%configuration%\net472\ILSpy.BamlDecompiler.Tests.exe" after_test: - python BuildTools\tidy.py From 79559f5316e73c336ecbcc0b56e84cd4a13b1f9b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 30 Mar 2020 17:16:32 +0200 Subject: [PATCH 20/30] Use correct Baml Tests file name in Azure pipeline. --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d3f6bb173..2322f87e3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -71,7 +71,7 @@ jobs: testAssemblyVer2: | ICSharpCode.Decompiler.Tests\bin\$(BuildConfiguration)\net472\ICSharpCode.Decompiler.Tests.exe ILSpy.Tests\bin\$(BuildConfiguration)\net472\ILSpy.Tests.exe - ILSpy.BamlDecompiler.Tests\bin\$(BuildConfiguration)\net472\ILSpy.BamlDecompiler.Tests.dll + ILSpy.BamlDecompiler.Tests\bin\$(BuildConfiguration)\net472\ILSpy.BamlDecompiler.Tests.exe - task: ArchiveFiles@1 displayName: Create zip From 0cf50aa8277c911cf53d2864c98f6751bfb0b4db Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 1 Apr 2020 21:10:35 +0200 Subject: [PATCH 21/30] Fix #1959: Resolve the "F(G(7));" grammar ambiguity by inserting parentheses when necessary. --- .../TestCases/Pretty/Generics.cs | 9 ++ .../CSharp/CSharpDecompiler.cs | 3 + .../OutputVisitor/CSharpOutputVisitor.cs | 23 ---- .../GenericGrammarAmbiguityVisitor.cs | 120 ++++++++++++++++++ .../DebugInfo/PortablePdbWriter.cs | 1 - .../ICSharpCode.Decompiler.csproj | 1 + 6 files changed, 133 insertions(+), 24 deletions(-) create mode 100644 ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs index e8d209e30..6dfb19368 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs @@ -276,5 +276,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return sizeof(T); } #endif + + public static void Issue1959(int a, int b, int? c) + { + // This line requires parentheses around `a < b` to avoid a grammar ambiguity. + Console.WriteLine("{}, {}", (a < b), a > (c ?? b)); + // But here there's no ambiguity: + Console.WriteLine("{}, {}", a < b, a > b); + Console.WriteLine("{}, {}", a < Environment.GetLogicalDrives().Length, a > (c ?? b)); + } } } diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 174673e13..53b10fa6f 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -428,7 +428,10 @@ namespace ICSharpCode.Decompiler.CSharp CancellationToken.ThrowIfCancellationRequested(); transform.Run(rootNode, context); } + CancellationToken.ThrowIfCancellationRequested(); rootNode.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); + CancellationToken.ThrowIfCancellationRequested(); + GenericGrammarAmbiguityVisitor.ResolveAmbiguities(rootNode); } string SyntaxTreeToString(SyntaxTree syntaxTree) diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index 1f399b9d9..faa67d21c 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -148,29 +148,6 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor RPar(); } -#if DOTNET35 - void WriteCommaSeparatedList(IEnumerable list) - { - WriteCommaSeparatedList(list.SafeCast()); - } - - void WriteCommaSeparatedList(IEnumerable list) - { - WriteCommaSeparatedList(list.SafeCast()); - } - - void WriteCommaSeparatedListInParenthesis(IEnumerable list, bool spaceWithin) - { - WriteCommaSeparatedListInParenthesis(list.SafeCast(), spaceWithin); - } - - void WriteCommaSeparatedListInParenthesis(IEnumerable list, bool spaceWithin) - { - WriteCommaSeparatedListInParenthesis(list.SafeCast(), spaceWithin); - } - -#endif - protected virtual void WriteCommaSeparatedListInBrackets(IEnumerable list, bool spaceWithin) { WriteToken(Roles.LBracket); diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs new file mode 100644 index 000000000..f621e99b3 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs @@ -0,0 +1,120 @@ +// Copyright (c) 2020 Daniel Grunwald +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Diagnostics; +using System.Linq; +using ICSharpCode.Decompiler.CSharp.Syntax; + +namespace ICSharpCode.Decompiler.CSharp.OutputVisitor +{ + /// + /// Used to test for the "F(G<A,B>(7));" grammar ambiguity. + /// + class GenericGrammarAmbiguityVisitor : DepthFirstAstVisitor + { + /// + /// Resolves ambiguities in the specified syntax tree. + /// This method must be called after the InsertParenthesesVisitor, because the ambiguity depends on whether the + /// final `>` in the possible-type-argument is followed by an opening parenthesis. + /// + public static void ResolveAmbiguities(AstNode rootNode) + { + foreach (var node in rootNode.Descendants.OfType()) { + if (CausesAmbiguityWithGenerics(node)) { + node.ReplaceWith(n => new ParenthesizedExpression(n)); + } + } + } + + public static bool CausesAmbiguityWithGenerics(BinaryOperatorExpression binaryOperatorExpression) + { + if (binaryOperatorExpression.Operator != BinaryOperatorType.LessThan) + return false; + + var v = new GenericGrammarAmbiguityVisitor(); + v.genericNestingLevel = 1; + + for (AstNode node = binaryOperatorExpression.Right; node != null; node = node.GetNextNode()) { + if (node.AcceptVisitor(v)) + return v.ambiguityFound; + } + return false; + } + + int genericNestingLevel; + bool ambiguityFound; + + protected override bool VisitChildren(AstNode node) + { + // unhandled node: probably not syntactically valid in a typename + + // These are preconditions for all recursive Visit() calls. + Debug.Assert(genericNestingLevel > 0); + Debug.Assert(!ambiguityFound); + + // The return value merely indicates whether to stop visiting. + return true; // stop visiting, no ambiguity found + } + + public override bool VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) + { + if (binaryOperatorExpression.Left.AcceptVisitor(this)) + return true; + Debug.Assert(genericNestingLevel > 0); + switch (binaryOperatorExpression.Operator) { + case BinaryOperatorType.LessThan: + genericNestingLevel += 1; + break; + case BinaryOperatorType.GreaterThan: + genericNestingLevel--; + break; + case BinaryOperatorType.ShiftRight when genericNestingLevel >= 2: + genericNestingLevel -= 2; + break; + default: + return true; // stop visiting, no ambiguity found + } + if (genericNestingLevel == 0) { + // Of the all tokens that might follow `>` and trigger the ambiguity to be resolved in favor of generics, + // `(` is the only one that might start an expression. + ambiguityFound = binaryOperatorExpression.Right is ParenthesizedExpression; + return true; // stop visiting + } + return binaryOperatorExpression.Right.AcceptVisitor(this); + } + + public override bool VisitIdentifierExpression(IdentifierExpression identifierExpression) + { + // identifier could also be valid in a type argument + return false; // keep visiting + } + + public override bool VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) + { + return false; // keep visiting + } + + public override bool VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) + { + // MRE could also be valid in a type argument + return memberReferenceExpression.Target.AcceptVisitor(this); + } + + } +} diff --git a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs index 1ed23bdda..64eda349e 100644 --- a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs +++ b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs @@ -297,7 +297,6 @@ namespace ICSharpCode.Decompiler.DebugInfo { StringWriter w = new StringWriter(); TokenWriter tokenWriter = new TextWriterTokenWriter(w); - syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); tokenWriter = TokenWriter.WrapInWriterThatSetsLocationsInAST(tokenWriter); syntaxTree.AcceptVisitor(new CSharpOutputVisitor(tokenWriter, settings.CSharpFormattingOptions)); return w.ToString(); diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 651a2ec30..a28fea867 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -61,6 +61,7 @@ + From fb5ab19bfd85393845940be1e6f06bfeb97622b5 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 1 Apr 2020 21:28:42 +0200 Subject: [PATCH 22/30] SolutionCreator: Store project type GUID in .sln --- .../CSharp/WholeProjectDecompiler.cs | 2 +- ICSharpCode.Decompiler/Solution/ProjectId.cs | 21 ++++++++++++++++++- .../Solution/ProjectItem.cs | 4 ++-- .../Solution/SolutionCreator.cs | 5 ++--- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs index 769b5d33c..bf272a3d2 100644 --- a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs @@ -302,7 +302,7 @@ namespace ICSharpCode.Decompiler.CSharp w.WriteEndDocument(); } - return new ProjectId(platformName, guid); + return new ProjectId(platformName, guid, ProjectTypeGuids.CSharpWindows); } protected virtual bool IsGacAssembly(Metadata.IAssemblyReference r, Metadata.PEFile asm) diff --git a/ICSharpCode.Decompiler/Solution/ProjectId.cs b/ICSharpCode.Decompiler/Solution/ProjectId.cs index cfa3feb7c..e067babed 100644 --- a/ICSharpCode.Decompiler/Solution/ProjectId.cs +++ b/ICSharpCode.Decompiler/Solution/ProjectId.cs @@ -32,24 +32,43 @@ namespace ICSharpCode.Decompiler.Solution /// The project GUID. /// /// Thrown when is null or empty. - public ProjectId(string projectPlatform, Guid projectGuid) + public ProjectId(string projectPlatform, Guid projectGuid, Guid typeGuid) { if (string.IsNullOrWhiteSpace(projectPlatform)) { throw new ArgumentException("The platform cannot be null or empty.", nameof(projectPlatform)); } Guid = projectGuid; + TypeGuid = typeGuid; PlatformName = projectPlatform; } /// /// Gets the GUID of this project. + /// This is usually a newly generated GUID for each decompiled project. /// public Guid Guid { get; } + /// + /// Gets the primary type GUID of this project. + /// This is one of the GUIDs from . + /// + public Guid TypeGuid { get; } + /// /// Gets the platform name of this project. Only single platform per project is supported. /// public string PlatformName { get; } } + + public static class ProjectTypeGuids + { + public static readonly Guid SolutionFolder = Guid.Parse("{2150E333-8FDC-42A3-9474-1A3956D46DE8}"); + + public static readonly Guid CSharpWindows = Guid.Parse("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"); + public static readonly Guid CSharpCore = Guid.Parse("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"); + + public static readonly Guid Silverlight = Guid.Parse("{A1591282-1198-4647-A2B1-27E5FF5F6F3B}"); + public static readonly Guid PortableLibrary = Guid.Parse("{786C830F-07A1-408B-BD7F-6EE04809D6DB}"); + } } diff --git a/ICSharpCode.Decompiler/Solution/ProjectItem.cs b/ICSharpCode.Decompiler/Solution/ProjectItem.cs index bf1222368..5f669b018 100644 --- a/ICSharpCode.Decompiler/Solution/ProjectItem.cs +++ b/ICSharpCode.Decompiler/Solution/ProjectItem.cs @@ -35,8 +35,8 @@ namespace ICSharpCode.Decompiler.Solution /// /// Thrown when /// or is null or empty. - public ProjectItem(string projectFile, string projectPlatform, Guid projectGuid) - : base(projectPlatform, projectGuid) + public ProjectItem(string projectFile, string projectPlatform, Guid projectGuid, Guid typeGuid) + : base(projectPlatform, projectGuid, typeGuid) { ProjectName = Path.GetFileNameWithoutExtension(projectFile); FilePath = projectFile; diff --git a/ICSharpCode.Decompiler/Solution/SolutionCreator.cs b/ICSharpCode.Decompiler/Solution/SolutionCreator.cs index 824f03a23..1e4d3619b 100644 --- a/ICSharpCode.Decompiler/Solution/SolutionCreator.cs +++ b/ICSharpCode.Decompiler/Solution/SolutionCreator.cs @@ -88,13 +88,12 @@ namespace ICSharpCode.Decompiler.Solution private static void WriteProjects(TextWriter writer, IEnumerable projects, string solutionFilePath) { - var solutionGuid = Guid.NewGuid().ToString("B").ToUpperInvariant(); - foreach (var project in projects) { var projectRelativePath = GetRelativePath(solutionFilePath, project.FilePath); + var typeGuid = project.TypeGuid.ToString("B").ToUpperInvariant(); var projectGuid = project.Guid.ToString("B").ToUpperInvariant(); - writer.WriteLine($"Project(\"{solutionGuid}\") = \"{project.ProjectName}\", \"{projectRelativePath}\", \"{projectGuid}\""); + writer.WriteLine($"Project(\"{typeGuid}\") = \"{project.ProjectName}\", \"{projectRelativePath}\", \"{projectGuid}\""); writer.WriteLine("EndProject"); } } From ff40fbd984bd24f218e16410e3aa95eb07b7fa82 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 1 Apr 2020 22:01:45 +0200 Subject: [PATCH 23/30] Fix #1958: Emit ProjectTypeGuids when generating .csproj For portable class libraries, the type GUID is required so that Visual Studio for Mac can open the project. --- .../CSharp/WholeProjectDecompiler.cs | 138 +++++++++--------- 1 file changed, 73 insertions(+), 65 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs index bf272a3d2..22e847230 100644 --- a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs @@ -127,18 +127,19 @@ namespace ICSharpCode.Decompiler.CSharp return WriteProjectFile(projectFileWriter, files, moduleDefinition); } - enum LanguageTargets - { - None, - Portable - } - #region WriteProjectFile ProjectId WriteProjectFile(TextWriter writer, IEnumerable> files, Metadata.PEFile module) { const string ns = "http://schemas.microsoft.com/developer/msbuild/2003"; string platformName = GetPlatformName(module); Guid guid = this.ProjectGuid ?? Guid.NewGuid(); + var targetFramework = DetectTargetFramework(module); + + List typeGuids = new List(); + if (targetFramework.IsPortableClassLibrary) + typeGuids.Add(ProjectTypeGuids.PortableLibrary); + typeGuids.Add(ProjectTypeGuids.CSharpWindows); + // TODO: .NET core support using (XmlTextWriter w = new XmlTextWriter(writer)) { w.Formatting = Formatting.Indented; @@ -149,6 +150,7 @@ namespace ICSharpCode.Decompiler.CSharp w.WriteStartElement("PropertyGroup"); w.WriteElementString("ProjectGuid", guid.ToString("B").ToUpperInvariant()); + w.WriteElementString("ProjectTypeGuids", string.Join(";", typeGuids.Select(g => g.ToString("B").ToUpperInvariant()))); w.WriteStartElement("Configuration"); w.WriteAttributeString("Condition", " '$(Configuration)' == '' "); @@ -179,53 +181,12 @@ namespace ICSharpCode.Decompiler.CSharp w.WriteElementString("LangVersion", LanguageVersion.ToString().Replace("CSharp", "").Replace('_', '.')); w.WriteElementString("AssemblyName", module.Name); - bool useTargetFrameworkAttribute = false; - LanguageTargets languageTargets = LanguageTargets.None; - string targetFramework = module.Reader.DetectTargetFrameworkId(); - int frameworkVersionNumber = 0; - if (!string.IsNullOrEmpty(targetFramework)) { - string[] frameworkParts = targetFramework.Split(','); - string frameworkIdentifier = frameworkParts.FirstOrDefault(a => !a.StartsWith("Version=", StringComparison.OrdinalIgnoreCase) && !a.StartsWith("Profile=", StringComparison.OrdinalIgnoreCase)); - if (frameworkIdentifier != null) { - w.WriteElementString("TargetFrameworkIdentifier", frameworkIdentifier); - switch (frameworkIdentifier) { - case ".NETPortable": - languageTargets = LanguageTargets.Portable; - break; - } - } - string frameworkVersion = frameworkParts.FirstOrDefault(a => a.StartsWith("Version=", StringComparison.OrdinalIgnoreCase)); - if (frameworkVersion != null) { - w.WriteElementString("TargetFrameworkVersion", frameworkVersion.Substring("Version=".Length)); - useTargetFrameworkAttribute = true; - frameworkVersionNumber = int.Parse(frameworkVersion.Substring("Version=v".Length).Replace(".", "")); - if (frameworkVersionNumber < 100) frameworkVersionNumber *= 10; - } - string frameworkProfile = frameworkParts.FirstOrDefault(a => a.StartsWith("Profile=", StringComparison.OrdinalIgnoreCase)); - if (frameworkProfile != null) - w.WriteElementString("TargetFrameworkProfile", frameworkProfile.Substring("Profile=".Length)); - } - if (!useTargetFrameworkAttribute) { - switch (module.GetRuntime()) { - case Metadata.TargetRuntime.Net_1_0: - frameworkVersionNumber = 100; - w.WriteElementString("TargetFrameworkVersion", "v1.0"); - break; - case Metadata.TargetRuntime.Net_1_1: - frameworkVersionNumber = 110; - w.WriteElementString("TargetFrameworkVersion", "v1.1"); - break; - case Metadata.TargetRuntime.Net_2_0: - frameworkVersionNumber = 200; - w.WriteElementString("TargetFrameworkVersion", "v2.0"); - // TODO: Detect when .NET 3.0/3.5 is required - break; - default: - frameworkVersionNumber = 400; - w.WriteElementString("TargetFrameworkVersion", "v4.0"); - break; - } - } + if (targetFramework.TargetFrameworkIdentifier != null) + w.WriteElementString("TargetFrameworkIdentifier", targetFramework.TargetFrameworkIdentifier); + if (targetFramework.TargetFrameworkVersion != null) + w.WriteElementString("TargetFrameworkVersion", targetFramework.TargetFrameworkVersion); + if (targetFramework.TargetFrameworkProfile != null) + w.WriteElementString("TargetFrameworkProfile", targetFramework.TargetFrameworkProfile); w.WriteElementString("WarningLevel", "4"); w.WriteElementString("AllowUnsafeBlocks", "True"); @@ -239,7 +200,7 @@ namespace ICSharpCode.Decompiler.CSharp w.WriteStartElement("PropertyGroup"); // platform-specific w.WriteAttributeString("Condition", " '$(Platform)' == '" + platformName + "' "); w.WriteElementString("PlatformTarget", platformName); - if (frameworkVersionNumber > 400 && platformName == "AnyCPU" && (module.Reader.PEHeaders.CorHeader.Flags & CorFlags.Prefers32Bit) == 0) { + if (targetFramework.VersionNumber > 400 && platformName == "AnyCPU" && (module.Reader.PEHeaders.CorHeader.Flags & CorFlags.Prefers32Bit) == 0) { w.WriteElementString("Prefer32Bit", "false"); } w.WriteEndElement(); // (platform-specific) @@ -286,17 +247,14 @@ namespace ICSharpCode.Decompiler.CSharp } w.WriteEndElement(); } - switch (languageTargets) { - case LanguageTargets.Portable: - w.WriteStartElement("Import"); - w.WriteAttributeString("Project", "$(MSBuildExtensionsPath32)\\Microsoft\\Portable\\$(TargetFrameworkVersion)\\Microsoft.Portable.CSharp.targets"); - w.WriteEndElement(); - break; - default: - w.WriteStartElement("Import"); - w.WriteAttributeString("Project", "$(MSBuildToolsPath)\\Microsoft.CSharp.targets"); - w.WriteEndElement(); - break; + if (targetFramework.IsPortableClassLibrary) { + w.WriteStartElement("Import"); + w.WriteAttributeString("Project", "$(MSBuildExtensionsPath32)\\Microsoft\\Portable\\$(TargetFrameworkVersion)\\Microsoft.Portable.CSharp.targets"); + w.WriteEndElement(); + } else { + w.WriteStartElement("Import"); + w.WriteAttributeString("Project", "$(MSBuildToolsPath)\\Microsoft.CSharp.targets"); + w.WriteEndElement(); } w.WriteEndDocument(); @@ -305,6 +263,56 @@ namespace ICSharpCode.Decompiler.CSharp return new ProjectId(platformName, guid, ProjectTypeGuids.CSharpWindows); } + struct TargetFramework + { + public string TargetFrameworkIdentifier; + public string TargetFrameworkVersion; + public string TargetFrameworkProfile; + public int VersionNumber; + public bool IsPortableClassLibrary => TargetFrameworkIdentifier == ".NETPortable"; + } + + private TargetFramework DetectTargetFramework(PEFile module) + { + TargetFramework result = default; + + switch (module.GetRuntime()) { + case Metadata.TargetRuntime.Net_1_0: + result.VersionNumber = 100; + result.TargetFrameworkVersion = "v1.0"; + break; + case Metadata.TargetRuntime.Net_1_1: + result.VersionNumber = 110; + result.TargetFrameworkVersion = "v1.1"; + break; + case Metadata.TargetRuntime.Net_2_0: + result.VersionNumber = 200; + result.TargetFrameworkVersion = "v2.0"; + // TODO: Detect when .NET 3.0/3.5 is required + break; + default: + result.VersionNumber = 400; + result.TargetFrameworkVersion = "v4.0"; + break; + } + + string targetFramework = module.Reader.DetectTargetFrameworkId(); + if (!string.IsNullOrEmpty(targetFramework)) { + string[] frameworkParts = targetFramework.Split(','); + result.TargetFrameworkIdentifier = frameworkParts.FirstOrDefault(a => !a.StartsWith("Version=", StringComparison.OrdinalIgnoreCase) && !a.StartsWith("Profile=", StringComparison.OrdinalIgnoreCase)); + string frameworkVersion = frameworkParts.FirstOrDefault(a => a.StartsWith("Version=", StringComparison.OrdinalIgnoreCase)); + if (frameworkVersion != null) { + result.TargetFrameworkVersion = frameworkVersion.Substring("Version=".Length); + result.VersionNumber = int.Parse(frameworkVersion.Substring("Version=v".Length).Replace(".", "")); + if (result.VersionNumber < 100) result.VersionNumber *= 10; + } + string frameworkProfile = frameworkParts.FirstOrDefault(a => a.StartsWith("Profile=", StringComparison.OrdinalIgnoreCase)); + if (frameworkProfile != null) + result.TargetFrameworkProfile = frameworkProfile.Substring("Profile=".Length); + } + return result; + } + protected virtual bool IsGacAssembly(Metadata.IAssemblyReference r, Metadata.PEFile asm) { return false; From 56c2f200d04aa7a6c6c538f2b5c7d8a60be050c0 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 1 Apr 2020 23:18:55 +0200 Subject: [PATCH 24/30] Fix build. --- ILSpy/SolutionWriter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ILSpy/SolutionWriter.cs b/ILSpy/SolutionWriter.cs index d892e22ec..03ee32ab3 100644 --- a/ILSpy/SolutionWriter.cs +++ b/ILSpy/SolutionWriter.cs @@ -171,7 +171,7 @@ namespace ICSharpCode.ILSpy var projectInfo = language.DecompileAssembly(loadedAssembly, projectFileOutput, options); if (projectInfo != null) { - projects.Add(new ProjectItem(projectFileName, projectInfo.PlatformName, projectInfo.Guid)); + projects.Add(new ProjectItem(projectFileName, projectInfo.PlatformName, projectInfo.Guid, projectInfo.TypeGuid)); } } } catch (Exception e) when (!(e is OperationCanceledException)) { From c55c1f947667439a82f027bfda5e5803fb813357 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 2 Apr 2020 21:38:01 +0200 Subject: [PATCH 25/30] Add "Hide empty metadata tables in tree view" option --- ILSpy/Metadata/DebugMetadataTreeNode.cs | 28 ++++-- ILSpy/Metadata/MetadataTreeNode.cs | 106 ++++++++++++++------- ILSpy/Options/DisplaySettings.cs | 13 +++ ILSpy/Options/DisplaySettingsPanel.xaml | 1 + ILSpy/Options/DisplaySettingsPanel.xaml.cs | 2 + ILSpy/Properties/Resources.Designer.cs | 9 ++ ILSpy/Properties/Resources.resx | 3 + 7 files changed, 120 insertions(+), 42 deletions(-) diff --git a/ILSpy/Metadata/DebugMetadataTreeNode.cs b/ILSpy/Metadata/DebugMetadataTreeNode.cs index 03220d533..bb52d8567 100644 --- a/ILSpy/Metadata/DebugMetadataTreeNode.cs +++ b/ILSpy/Metadata/DebugMetadataTreeNode.cs @@ -18,8 +18,10 @@ using System.Linq; using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.ViewModels; @@ -61,14 +63,24 @@ namespace ICSharpCode.ILSpy.Metadata protected override void LoadChildren() { - this.Children.Add(new DocumentTableTreeNode(this.module, this.provider, isEmbedded)); - this.Children.Add(new MethodDebugInformationTableTreeNode(this.module, this.provider, isEmbedded)); - this.Children.Add(new LocalScopeTableTreeNode(this.module, this.provider, isEmbedded)); - this.Children.Add(new LocalVariableTableTreeNode(this.module, this.provider, isEmbedded)); - this.Children.Add(new LocalConstantTableTreeNode(this.module, this.provider, isEmbedded)); - this.Children.Add(new ImportScopeTableTreeNode(this.module, this.provider, isEmbedded)); - this.Children.Add(new StateMachineMethodTableTreeNode(this.module, this.provider, isEmbedded)); - this.Children.Add(new CustomDebugInformationTableTreeNode(this.module, this.provider, isEmbedded)); + if (ShowTable(TableIndex.Document)) + this.Children.Add(new DocumentTableTreeNode(this.module, this.provider, isEmbedded)); + if (ShowTable(TableIndex.MethodDebugInformation)) + this.Children.Add(new MethodDebugInformationTableTreeNode(this.module, this.provider, isEmbedded)); + if (ShowTable(TableIndex.LocalScope)) + this.Children.Add(new LocalScopeTableTreeNode(this.module, this.provider, isEmbedded)); + if (ShowTable(TableIndex.LocalVariable)) + this.Children.Add(new LocalVariableTableTreeNode(this.module, this.provider, isEmbedded)); + if (ShowTable(TableIndex.LocalConstant)) + this.Children.Add(new LocalConstantTableTreeNode(this.module, this.provider, isEmbedded)); + if (ShowTable(TableIndex.ImportScope)) + this.Children.Add(new ImportScopeTableTreeNode(this.module, this.provider, isEmbedded)); + if (ShowTable(TableIndex.StateMachineMethod)) + this.Children.Add(new StateMachineMethodTableTreeNode(this.module, this.provider, isEmbedded)); + if (ShowTable(TableIndex.CustomDebugInformation)) + this.Children.Add(new CustomDebugInformationTableTreeNode(this.module, this.provider, isEmbedded)); + + bool ShowTable(TableIndex table) => !DisplaySettingsPanel.CurrentDisplaySettings.HideEmptyMetadataTables || module.Metadata.GetTableRowCount(table) > 0; } public MetadataTableTreeNode FindNodeByHandleKind(HandleKind kind) diff --git a/ILSpy/Metadata/MetadataTreeNode.cs b/ILSpy/Metadata/MetadataTreeNode.cs index 572d74c49..6a6f9d4f4 100644 --- a/ILSpy/Metadata/MetadataTreeNode.cs +++ b/ILSpy/Metadata/MetadataTreeNode.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; using System.Text; using System.Threading.Tasks; @@ -28,6 +29,7 @@ using System.Windows; using System.Windows.Data; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.ViewModels; @@ -68,40 +70,76 @@ namespace ICSharpCode.ILSpy.Metadata this.Children.Add(new CoffHeaderTreeNode(module)); this.Children.Add(new OptionalHeaderTreeNode(module)); this.Children.Add(new DataDirectoriesTreeNode(module)); - this.Children.Add(new ModuleTableTreeNode(module)); - this.Children.Add(new TypeRefTableTreeNode(module)); - this.Children.Add(new TypeDefTableTreeNode(module)); - this.Children.Add(new FieldTableTreeNode(module)); - this.Children.Add(new MethodTableTreeNode(module)); - this.Children.Add(new ParamTableTreeNode(module)); - this.Children.Add(new InterfaceImplTableTreeNode(module)); - this.Children.Add(new MemberRefTableTreeNode(module)); - this.Children.Add(new ConstantTableTreeNode(module)); - this.Children.Add(new CustomAttributeTableTreeNode(module)); - this.Children.Add(new FieldMarshalTableTreeNode(module)); - this.Children.Add(new DeclSecurityTableTreeNode(module)); - this.Children.Add(new ClassLayoutTableTreeNode(module)); - this.Children.Add(new FieldLayoutTableTreeNode(module)); - this.Children.Add(new StandAloneSigTableTreeNode(module)); - this.Children.Add(new EventMapTableTreeNode(module)); - this.Children.Add(new EventTableTreeNode(module)); - this.Children.Add(new PropertyMapTableTreeNode(module)); - this.Children.Add(new PropertyTableTreeNode(module)); - this.Children.Add(new MethodSemanticsTableTreeNode(module)); - this.Children.Add(new MethodImplTableTreeNode(module)); - this.Children.Add(new ModuleRefTableTreeNode(module)); - this.Children.Add(new TypeSpecTableTreeNode(module)); - this.Children.Add(new ImplMapTableTreeNode(module)); ; - this.Children.Add(new FieldRVATableTreeNode(module)); - this.Children.Add(new AssemblyTableTreeNode(module)); - this.Children.Add(new AssemblyRefTableTreeNode(module)); - this.Children.Add(new FileTableTreeNode(module)); - this.Children.Add(new ExportedTypeTableTreeNode(module)); - this.Children.Add(new ManifestResourceTableTreeNode(module)); - this.Children.Add(new NestedClassTableTreeNode(module)); - this.Children.Add(new GenericParamTableTreeNode(module)); - this.Children.Add(new MethodSpecTableTreeNode(module)); - this.Children.Add(new GenericParamConstraintTableTreeNode(module)); + if (ShowTable(TableIndex.Module)) + this.Children.Add(new ModuleTableTreeNode(module)); + if (ShowTable(TableIndex.TypeRef)) + this.Children.Add(new TypeRefTableTreeNode(module)); + if (ShowTable(TableIndex.TypeDef)) + this.Children.Add(new TypeDefTableTreeNode(module)); + if (ShowTable(TableIndex.Field)) + this.Children.Add(new FieldTableTreeNode(module)); + if (ShowTable(TableIndex.MethodDef)) + this.Children.Add(new MethodTableTreeNode(module)); + if (ShowTable(TableIndex.Param)) + this.Children.Add(new ParamTableTreeNode(module)); + if (ShowTable(TableIndex.InterfaceImpl)) + this.Children.Add(new InterfaceImplTableTreeNode(module)); + if (ShowTable(TableIndex.MemberRef)) + this.Children.Add(new MemberRefTableTreeNode(module)); + if (ShowTable(TableIndex.Constant)) + this.Children.Add(new ConstantTableTreeNode(module)); + if (ShowTable(TableIndex.CustomAttribute)) + this.Children.Add(new CustomAttributeTableTreeNode(module)); + if (ShowTable(TableIndex.FieldMarshal)) + this.Children.Add(new FieldMarshalTableTreeNode(module)); + if (ShowTable(TableIndex.DeclSecurity)) + this.Children.Add(new DeclSecurityTableTreeNode(module)); + if (ShowTable(TableIndex.ClassLayout)) + this.Children.Add(new ClassLayoutTableTreeNode(module)); + if (ShowTable(TableIndex.FieldLayout)) + this.Children.Add(new FieldLayoutTableTreeNode(module)); + if (ShowTable(TableIndex.StandAloneSig)) + this.Children.Add(new StandAloneSigTableTreeNode(module)); + if (ShowTable(TableIndex.EventMap)) + this.Children.Add(new EventMapTableTreeNode(module)); + if (ShowTable(TableIndex.Event)) + this.Children.Add(new EventTableTreeNode(module)); + if (ShowTable(TableIndex.PropertyMap)) + this.Children.Add(new PropertyMapTableTreeNode(module)); + if (ShowTable(TableIndex.Property)) + this.Children.Add(new PropertyTableTreeNode(module)); + if (ShowTable(TableIndex.MethodSemantics)) + this.Children.Add(new MethodSemanticsTableTreeNode(module)); + if (ShowTable(TableIndex.MethodImpl)) + this.Children.Add(new MethodImplTableTreeNode(module)); + if (ShowTable(TableIndex.ModuleRef)) + this.Children.Add(new ModuleRefTableTreeNode(module)); + if (ShowTable(TableIndex.TypeSpec)) + this.Children.Add(new TypeSpecTableTreeNode(module)); + if (ShowTable(TableIndex.ImplMap)) + this.Children.Add(new ImplMapTableTreeNode(module)); + if (ShowTable(TableIndex.FieldRva)) + this.Children.Add(new FieldRVATableTreeNode(module)); + if (ShowTable(TableIndex.Assembly)) + this.Children.Add(new AssemblyTableTreeNode(module)); + if (ShowTable(TableIndex.AssemblyRef)) + this.Children.Add(new AssemblyRefTableTreeNode(module)); + if (ShowTable(TableIndex.File)) + this.Children.Add(new FileTableTreeNode(module)); + if (ShowTable(TableIndex.ExportedType)) + this.Children.Add(new ExportedTypeTableTreeNode(module)); + if (ShowTable(TableIndex.ManifestResource)) + this.Children.Add(new ManifestResourceTableTreeNode(module)); + if (ShowTable(TableIndex.NestedClass)) + this.Children.Add(new NestedClassTableTreeNode(module)); + if (ShowTable(TableIndex.GenericParam)) + this.Children.Add(new GenericParamTableTreeNode(module)); + if (ShowTable(TableIndex.MethodSpec)) + this.Children.Add(new MethodSpecTableTreeNode(module)); + if (ShowTable(TableIndex.GenericParamConstraint)) + this.Children.Add(new GenericParamConstraintTableTreeNode(module)); + + bool ShowTable(TableIndex table) => !DisplaySettingsPanel.CurrentDisplaySettings.HideEmptyMetadataTables || module.Metadata.GetTableRowCount(table) > 0; } public MetadataTableTreeNode FindNodeByHandleKind(HandleKind kind) diff --git a/ILSpy/Options/DisplaySettings.cs b/ILSpy/Options/DisplaySettings.cs index 950a9b6c4..301bcd694 100644 --- a/ILSpy/Options/DisplaySettings.cs +++ b/ILSpy/Options/DisplaySettings.cs @@ -234,6 +234,18 @@ namespace ICSharpCode.ILSpy.Options } } + bool hideEmptyMetadataTables = true; + + public bool HideEmptyMetadataTables { + get { return hideEmptyMetadataTables; } + set { + if (hideEmptyMetadataTables != value) { + hideEmptyMetadataTables = value; + OnPropertyChanged(); + } + } + } + public void CopyValues(DisplaySettings s) { this.SelectedFont = s.selectedFont; @@ -251,6 +263,7 @@ namespace ICSharpCode.ILSpy.Options this.IndentationTabSize = s.indentationTabSize; this.IndentationSize = s.indentationSize; this.HighlightMatchingBraces = s.highlightMatchingBraces; + this.HideEmptyMetadataTables = s.HideEmptyMetadataTables; } } } diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml b/ILSpy/Options/DisplaySettingsPanel.xaml index 13d149e6d..57ac6ecf8 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml +++ b/ILSpy/Options/DisplaySettingsPanel.xaml @@ -77,6 +77,7 @@ + diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml.cs b/ILSpy/Options/DisplaySettingsPanel.xaml.cs index a8196a560..f64b45718 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml.cs +++ b/ILSpy/Options/DisplaySettingsPanel.xaml.cs @@ -115,6 +115,7 @@ namespace ICSharpCode.ILSpy.Options s.IndentationSize = (int?)e.Attribute("IndentationSize") ?? 4; s.IndentationTabSize = (int?)e.Attribute("IndentationTabSize") ?? 4; s.HighlightMatchingBraces = (bool?)e.Attribute("HighlightMatchingBraces") ?? true; + s.HideEmptyMetadataTables = (bool?)e.Attribute("HideEmptyMetadataTables") ?? true; return s; } @@ -139,6 +140,7 @@ namespace ICSharpCode.ILSpy.Options section.SetAttributeValue("IndentationSize", s.IndentationSize); section.SetAttributeValue("IndentationTabSize", s.IndentationTabSize); section.SetAttributeValue("HighlightMatchingBraces", s.HighlightMatchingBraces); + section.SetAttributeValue("HideEmptyMetadataTables", s.HideEmptyMetadataTables); XElement existingElement = root.Element("DisplaySettings"); if (existingElement != null) diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 4bab4df31..b10cd0e2f 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1305,6 +1305,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Hide empty metadata tables from tree view. + /// + public static string HideEmptyMetadataTables { + get { + return ResourceManager.GetString("HideEmptyMetadataTables", resourceCulture); + } + } + /// /// Looks up a localized string similar to Highlight matching braces. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index a653a52a9..942df8738 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -829,4 +829,7 @@ Are you sure you want to continue? Introduce static local functions + + Hide empty metadata tables from tree view + \ No newline at end of file From 1ff1ade8e76d95a18cafa913ba41a57ca0ac7fb7 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 2 Apr 2020 22:13:39 +0200 Subject: [PATCH 26/30] Add "(.netmodule)" to treenode, if the LoadedAssembly instance represents a .netmodule --- ILSpy/LoadedAssembly.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ILSpy/LoadedAssembly.cs b/ILSpy/LoadedAssembly.cs index 479e67e86..5015cbcf5 100644 --- a/ILSpy/LoadedAssembly.cs +++ b/ILSpy/LoadedAssembly.cs @@ -141,12 +141,17 @@ namespace ICSharpCode.ILSpy get { if (IsLoaded && !HasLoadError) { var metadata = GetPEFileOrNull()?.Metadata; - string version = null; - if (metadata != null && metadata.IsAssembly) - version = metadata.GetAssemblyDefinition().Version?.ToString(); - if (version == null) + string versionOrInfo = null; + if (metadata != null) { + if (metadata.IsAssembly) { + versionOrInfo = metadata.GetAssemblyDefinition().Version?.ToString(); + } else { + versionOrInfo = ".netmodule"; + } + } + if (versionOrInfo == null) return ShortName; - return String.Format("{0} ({1})", ShortName, version); + return string.Format("{0} ({1})", ShortName, versionOrInfo); } else { return ShortName; } From 32dad4f4765f58ab4ea29ea760559bea0280cbb6 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 2 Apr 2020 22:14:52 +0200 Subject: [PATCH 27/30] Fix bug occurring when trying to load .netmodule reference after invoking LoadedAssembly.DisableAssemblyLoad() --- ILSpy/AssemblyList.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ILSpy/AssemblyList.cs b/ILSpy/AssemblyList.cs index 612dc77d9..c243bef9c 100644 --- a/ILSpy/AssemblyList.cs +++ b/ILSpy/AssemblyList.cs @@ -146,6 +146,7 @@ namespace ICSharpCode.ILSpy internal void ClearCache() { assemblyLookupCache.Clear(); + moduleLookupCache.Clear(); } public LoadedAssembly Open(string assemblyUri, bool isAutoLoaded = false) From 69ac54fbb6a7afac7973f26fb3c142295b5faa65 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 3 Apr 2020 21:37:27 +0200 Subject: [PATCH 28/30] Fix #1879: Do not remove variables that look like display class variables, but are used in other patterns as well. --- .../IL/Transforms/TransformDisplayClassUsage.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index f843b6b7f..5592a588d 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -97,6 +97,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms private bool CanRemoveAllReferencesTo(ILTransformContext context, ILVariable v) { foreach (var use in v.LoadInstructions) { + // we only accept stloc, stobj/ldobj and ld(s)flda instructions, + // as these are required by all patterns this transform understands. + if (!(use.Parent is StLoc || use.Parent is LdFlda || use.Parent is LdsFlda || use.Parent is StObj || use.Parent is LdObj)) { + return false; + } if (use.Parent.MatchStLoc(out var targetVar) && !IsClosure(context, targetVar, out _, out _)) { return false; } From 65fe59e393d9819498e18bfe5ce22ba714e094fd Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 3 Apr 2020 22:56:56 +0200 Subject: [PATCH 29/30] #1918: Fix a bunch of issues with pinned region detection. Not every pinned region has a clean `P = null` assignment to mark its end. If a second pinned region starts with the same variable `P`, consider that to mark the end of the previous pinned region for that variable. Also, fix a bunch of special cases with empty pinned regions. --- .../ICSharpCode.Decompiler.Tests.csproj | 5 + .../ILPrettyTestRunner.cs | 6 + .../TestCases/ILPretty/Issue1918.cs | 25 ++++ .../TestCases/ILPretty/Issue1918.il | 96 ++++++++++++++ .../TestCases/Pretty/UnsafeCode.cs | 10 ++ .../IL/ControlFlow/DetectPinnedRegions.cs | 125 +++++++++++------- 6 files changed, 221 insertions(+), 46 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1918.cs create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1918.il diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 2dea3c340..053e73191 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -61,7 +61,10 @@ + + + @@ -86,6 +89,8 @@ + + diff --git a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs index 65cbac376..49c3bca2c 100644 --- a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs @@ -94,6 +94,12 @@ namespace ICSharpCode.Decompiler.Tests Run(); } + [Test] + public void Issue1918() + { + Run(); + } + [Test] public void Issue1922() { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1918.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1918.cs new file mode 100644 index 000000000..42787d642 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1918.cs @@ -0,0 +1,25 @@ +using System; + +namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty +{ + internal class Issue1918 + { + public static Guid[] NullVal; + + public unsafe void ProblemFunction(Guid[] A_0, int A_1) + { + fixed (Guid* ptr = A_0) { + void* ptr2 = ptr; + UIntPtr* ptr3 = (UIntPtr*)((byte*)ptr2 - sizeof(UIntPtr)); + UIntPtr uIntPtr = *ptr3; + try { + *ptr3 = (UIntPtr)(ulong)A_1; + } finally { + *ptr3 = uIntPtr; + } + } + fixed (Guid[] ptr = NullVal) { + } + } + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1918.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1918.il new file mode 100644 index 000000000..0bd27e98d --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1918.il @@ -0,0 +1,96 @@ +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly Issue1918 +{ + .ver 1:0:0:0 +} +.module Issue1918.exe +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00020003 // ILONLY 32BITPREFERRED + +.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1918 + extends [mscorlib]System.Object +{ + +.method public hidebysig + instance void ProblemFunction (valuetype [mscorlib]System.Guid[] '', int32 '' + ) cil managed +{ + .maxstack 2 + .locals init ( + [0] valuetype [mscorlib]System.Guid[], + [1] int32, + [2] void*, + [3] valuetype [mscorlib]System.Guid[] pinned, + [4] native uint*, + [5] native uint + ) + + IL_0000: ldarg.1 + stloc.0 + + IL_0010: + ldarg.2 + stloc.1 + ldloc.0 + dup + stloc.3 + brfalse.s IL_0026 + + ldloc.3 + ldlen + conv.i4 + brtrue.s IL_002b + + IL_0026: ldc.i4.0 + conv.u + stloc.2 + br.s IL_0034 + + IL_002b: ldloc.3 + ldc.i4.0 + ldelema [mscorlib]System.Guid + conv.u + stloc.2 + + IL_0034: ldloc.2 + sizeof [mscorlib]System.UIntPtr + sub + stloc.s 4 + ldloc.s 4 + ldind.i + stloc.s 5 + .try + { + ldloc.s 4 + ldloc.1 + conv.i8 + call native uint [mscorlib]System.UIntPtr::op_Explicit(uint64) + stind.i + ldarg.1 + + leave.s IL_005c + } // end .try + finally + { + ldloc.s 4 + ldloc.s 5 + stind.i + endfinally + } // end handler + + IL_005c: ldsfld valuetype [mscorlib]System.Guid[] ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue1918::NullVal + stloc.3 + ret +} + +.field public static valuetype [mscorlib]System.Guid[] NullVal + +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs index 1e804a64d..ee703d20b 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs @@ -208,6 +208,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } +#if !(LEGACY_CSC && OPT) + // legacy csc manages to optimize out the pinned variable altogether in this case; + // leaving no pinned region we could detect. + public unsafe void FixedArrayNoPointerUse(int[] arr) + { + fixed (int* ptr = arr) { + } + } +#endif + public unsafe void PutDoubleIntoLongArray1(long[] array, int index, double val) { fixed (long* ptr = array) { diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs index d2f9206ea..b8eb5d110 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs @@ -95,17 +95,30 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow for (int j = 0; j < block.Instructions.Count - 1; j++) { var inst = block.Instructions[j]; ILVariable v; - if (inst.MatchStLoc(out v) && v.Kind == VariableKind.PinnedLocal && block.Instructions[j + 1].OpCode != OpCode.Branch) { - // split block after j: - context.Step("Split block after pinned local write", inst); - var newBlock = new Block(); - for (int k = j + 1; k < block.Instructions.Count; k++) { - newBlock.Instructions.Add(block.Instructions[k]); + if (inst.MatchStLoc(out v) && v.Kind == VariableKind.PinnedLocal) { + if (block.Instructions[j + 1].OpCode != OpCode.Branch) { + // split block after j: + context.Step("Split block after pinned local write", inst); + var newBlock = new Block(); + for (int k = j + 1; k < block.Instructions.Count; k++) { + newBlock.Instructions.Add(block.Instructions[k]); + } + newBlock.AddILRange(newBlock.Instructions[0]); + block.Instructions.RemoveRange(j + 1, newBlock.Instructions.Count); + block.Instructions.Add(new Branch(newBlock)); + container.Blocks.Insert(i + 1, newBlock); + } + if (j > 0) { + // split block before j: + context.Step("Split block before pinned local write", inst); + var newBlock = new Block(); + newBlock.Instructions.Add(block.Instructions[j]); + newBlock.Instructions.Add(block.Instructions[j + 1]); + Debug.Assert(block.Instructions.Count == j + 2); + block.Instructions.RemoveRange(j, 2); + block.Instructions.Insert(j, new Branch(newBlock)); + container.Blocks.Insert(i + 1, newBlock); } - newBlock.AddILRange(newBlock.Instructions[0]); - block.Instructions.RemoveRange(j + 1, newBlock.Instructions.Count); - block.Instructions.Add(new Branch(newBlock)); - container.Blocks.Insert(i + 1, newBlock); } } } @@ -347,8 +360,17 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow ILInstruction value; if (block.Instructions.Count != 2) return false; - if (!block.Instructions[0].MatchStLoc(p, out value)) + if (!block.Instructions[0].MatchStLoc(out var p2, out value)) return false; + if (p != p2) { + // If the pointer is unused, the variable P might have been split. + if (p.LoadCount == 0 && p.AddressCount == 0 && p2.LoadCount == 0 && p2.AddressCount == 0) { + if (!ILVariableEqualityComparer.Instance.Equals(p, p2)) + return false; + } else { + return false; + } + } if (v.Kind == VariableKind.PinnedLocal) { value = value.UnwrapConv(ConversionKind.StopGCTracking); } @@ -411,30 +433,24 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // we didn't find a single block to be added to the pinned region return false; } - reachedEdgesPerBlock[entryBlock.ChildIndex]++; - workList.Enqueue(entryBlock); + if (entryBlock.Instructions[0].MatchStLoc(stLoc.Variable, out _)) { + // pinned region has empty body + } else { + reachedEdgesPerBlock[entryBlock.ChildIndex]++; + workList.Enqueue(entryBlock); + } while (workList.Count > 0) { Block workItem = workList.Dequeue(); - StLoc workStLoc = workItem.Instructions.SecondToLastOrDefault() as StLoc; - int instructionCount; - if (workStLoc != null && workStLoc.Variable == stLoc.Variable && IsNullOrZero(workStLoc.Value)) { - // found unpin instruction: only consider branches prior to that instruction - instructionCount = workStLoc.ChildIndex; - } else { - instructionCount = workItem.Instructions.Count; - } - for (int i = 0; i < instructionCount; i++) { - foreach (var branch in workItem.Instructions[i].Descendants.OfType()) { - if (branch.TargetBlock.Parent == sourceContainer) { - if (branch.TargetBlock == block) { - // pin instruction is within a loop, and can loop around without an unpin instruction - // This should never happen for C#-compiled code, but may happen with C++/CLI code. - return false; - } - if (reachedEdgesPerBlock[branch.TargetBlock.ChildIndex]++ == 0) { - // detected first edge to that block: add block as work item - workList.Enqueue(branch.TargetBlock); - } + foreach (var branch in workItem.Descendants.OfType()) { + if (branch.TargetBlock.Parent == sourceContainer) { + if (branch.TargetBlock.Instructions[0].MatchStLoc(stLoc.Variable, out _)) { + // Found unpin instruction + continue; + } + Debug.Assert(branch.TargetBlock != block); + if (reachedEdgesPerBlock[branch.TargetBlock.ChildIndex]++ == 0) { + // detected first edge to that block: add block as work item + workList.Enqueue(branch.TargetBlock); } } } @@ -454,12 +470,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow if (reachedEdgesPerBlock[i] > 0) { var innerBlock = sourceContainer.Blocks[i]; Branch br = innerBlock.Instructions.LastOrDefault() as Branch; - if (br != null && br.TargetContainer == sourceContainer && reachedEdgesPerBlock[br.TargetBlock.ChildIndex] == 0) { + if (br != null && br.TargetBlock.IncomingEdgeCount == 1 + && br.TargetContainer == sourceContainer && reachedEdgesPerBlock[br.TargetBlock.ChildIndex] == 0) + { // branch that leaves body. - // Should have an instruction that resets the pin; delete that instruction: - StLoc innerStLoc = innerBlock.Instructions.SecondToLastOrDefault() as StLoc; - if (innerStLoc != null && innerStLoc.Variable == stLoc.Variable && IsNullOrZero(innerStLoc.Value)) { - innerBlock.Instructions.RemoveAt(innerBlock.Instructions.Count - 2); + // The target block should have an instruction that resets the pin; delete that instruction: + StLoc unpin = br.TargetBlock.Instructions.First() as StLoc; + if (unpin != null && unpin.Variable == stLoc.Variable && IsNullOrZero(unpin.Value)) { + br.TargetBlock.Instructions.RemoveAt(0); } } @@ -468,6 +486,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // we'll delete the dummy block later } } + if (body.Blocks.Count == 0) { + // empty body, the entryBlock itself doesn't belong into the pinned region + Debug.Assert(reachedEdgesPerBlock[entryBlock.ChildIndex] == 0); + var bodyBlock = new Block(); + bodyBlock.SetILRange(stLoc); + bodyBlock.Instructions.Add(new Branch(entryBlock)); + body.Blocks.Add(bodyBlock); + } var pinnedRegion = new PinnedRegion(stLoc.Variable, stLoc.Value, body).WithILRange(stLoc); stLoc.ReplaceWith(pinnedRegion); @@ -546,6 +572,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow return; // variable access that is not LdLoc } } + if (ldloc == null) + return; if (!(ldloc.Parent is GetPinnableReference arrayToPointer)) return; if (!(arrayToPointer.Parent is Conv conv && conv.Kind == ConversionKind.StopGCTracking)) @@ -642,20 +670,25 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow return; if (!IsBranchOnNull(body.EntryPoint.Instructions[1], nativeVar, out Block targetBlock)) return; - if (targetBlock.Parent != body) - return; if (!body.EntryPoint.Instructions[2].MatchBranch(out Block adjustOffsetToStringData)) return; if (!(adjustOffsetToStringData.Parent == body && adjustOffsetToStringData.IncomingEdgeCount == 1 && IsOffsetToStringDataBlock(adjustOffsetToStringData, nativeVar, targetBlock))) return; context.Step("Handle pinned string (with adjustOffsetToStringData)", pinnedRegion); - // remove old entry point - body.Blocks.RemoveAt(0); - body.Blocks.RemoveAt(adjustOffsetToStringData.ChildIndex); - // make targetBlock the new entry point - body.Blocks.RemoveAt(targetBlock.ChildIndex); - body.Blocks.Insert(0, targetBlock); + if (targetBlock.Parent == body) { + // remove old entry point + body.Blocks.RemoveAt(0); + body.Blocks.RemoveAt(adjustOffsetToStringData.ChildIndex); + // make targetBlock the new entry point + body.Blocks.RemoveAt(targetBlock.ChildIndex); + body.Blocks.Insert(0, targetBlock); + } else { + // pinned region has empty body, immediately jumps to targetBlock which is outside + body.Blocks[0].Instructions.Clear(); + body.Blocks.RemoveRange(1, body.Blocks.Count - 1); + body.Blocks[0].Instructions.Add(new Branch(targetBlock)); + } pinnedRegion.Init = new GetPinnableReference(pinnedRegion.Init, null); ILVariable otherVar; From 5ef1a973d3827ac00a7c7c7893e44b503e7db5e2 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 4 Apr 2020 16:37:49 +0200 Subject: [PATCH 30/30] #1904: Cherry-picked SharpTreeView changes from SharpDevelop repository. 7edf1c4f1 fix focusing bug in SharpTreeView 60b89bb14 Changed behavior for 'Key.Space' in SharpTreeView.cs, instead of 'double click', now it sets 'IsChecked'. 103aff080 Set e.Handled = true in SharpTreeView.OnKeyDown() c928f88b5 Fix F2/Escape for editable SharpTreeNodes. 6b01d6f55 Fix #354: "+" icon in SharpTreeView is missing right border in 120DPI mode --- SharpTreeView/SharpTreeNode.cs | 13 ++++--------- SharpTreeView/SharpTreeNodeView.cs | 2 +- SharpTreeView/SharpTreeView.cs | 17 +++++++++++++++-- SharpTreeView/SharpTreeViewItem.cs | 13 ++++++++----- SharpTreeView/Themes/Generic.xaml | 2 +- 5 files changed, 29 insertions(+), 18 deletions(-) diff --git a/SharpTreeView/SharpTreeNode.cs b/SharpTreeView/SharpTreeNode.cs index c8a586c66..e34cd3ee4 100644 --- a/SharpTreeView/SharpTreeNode.cs +++ b/SharpTreeView/SharpTreeNode.cs @@ -335,19 +335,14 @@ namespace ICSharpCode.TreeView public IEnumerable Ancestors() { - var node = this; - while (node.Parent != null) { - yield return node.Parent; - node = node.Parent; - } + for (SharpTreeNode n = this.Parent; n != null; n = n.Parent) + yield return n; } public IEnumerable AncestorsAndSelf() { - yield return this; - foreach (var node in Ancestors()) { - yield return node; - } + for (SharpTreeNode n = this; n != null; n = n.Parent) + yield return n; } #endregion diff --git a/SharpTreeView/SharpTreeNodeView.cs b/SharpTreeView/SharpTreeNodeView.cs index 618a90ded..60e50a32d 100644 --- a/SharpTreeView/SharpTreeNodeView.cs +++ b/SharpTreeView/SharpTreeNodeView.cs @@ -147,7 +147,7 @@ namespace ICSharpCode.TreeView result -= 19; } if (result < 0) { - Debug.WriteLine("SharpTreeNodeView.CalculateIndent() on node without correctly-set level"); + Debug.WriteLine("Negative indent level detected for node " + Node); return 0; } return result; diff --git a/SharpTreeView/SharpTreeView.cs b/SharpTreeView/SharpTreeView.cs index 747928bc6..ddfb21443 100644 --- a/SharpTreeView/SharpTreeView.cs +++ b/SharpTreeView/SharpTreeView.cs @@ -178,7 +178,7 @@ namespace ICSharpCode.TreeView { if (updatesLocked) return; SetSelectedItems(newSelection ?? Enumerable.Empty()); - if (SelectedItem == null) { + if (SelectedItem == null && this.IsKeyboardFocusWithin) { // if we removed all selected nodes, then move the focus to the node // preceding the first of the old selected nodes SelectedIndex = topSelectedIndex; @@ -265,11 +265,24 @@ namespace ICSharpCode.TreeView } break; case Key.Return: - case Key.Space: if (container != null && Keyboard.Modifiers == ModifierKeys.None && this.SelectedItems.Count == 1 && this.SelectedItem == container.Node) { + e.Handled = true; container.Node.ActivateItem(e); } break; + case Key.Space: + if (container != null && Keyboard.Modifiers == ModifierKeys.None && this.SelectedItems.Count == 1 && this.SelectedItem == container.Node) { + e.Handled = true; + if (container.Node.IsCheckable) { + if (container.Node.IsChecked == null) // If partially selected, we want to select everything + container.Node.IsChecked = true; + else + container.Node.IsChecked = !container.Node.IsChecked; + } else { + container.Node.ActivateItem(e); + } + } + break; case Key.Add: if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) { container.Node.IsExpanded = true; diff --git a/SharpTreeView/SharpTreeViewItem.cs b/SharpTreeView/SharpTreeViewItem.cs index ce4951700..a438624d6 100644 --- a/SharpTreeView/SharpTreeViewItem.cs +++ b/SharpTreeView/SharpTreeViewItem.cs @@ -29,13 +29,16 @@ namespace ICSharpCode.TreeView { switch (e.Key) { case Key.F2: -// if (SharpTreeNode.ActiveNodes.Count == 1 && Node.IsEditable) { -// Node.IsEditing = true; -// e.Handled = true; -// } + if (Node.IsEditable && ParentTreeView != null && ParentTreeView.SelectedItems.Count == 1 && ParentTreeView.SelectedItems[0] == Node) { + Node.IsEditing = true; + e.Handled = true; + } break; case Key.Escape: - Node.IsEditing = false; + if (Node.IsEditing) { + Node.IsEditing = false; + e.Handled = true; + } break; } } diff --git a/SharpTreeView/Themes/Generic.xaml b/SharpTreeView/Themes/Generic.xaml index 46c49793d..6b7b2dbfb 100644 --- a/SharpTreeView/Themes/Generic.xaml +++ b/SharpTreeView/Themes/Generic.xaml @@ -237,7 +237,7 @@ Style="{StaticResource ExpandCollapseToggleStyle}" IsChecked="{Binding IsExpanded}" Visibility="Hidden" - Margin="0 0 6 0" + Margin="0 0 7 0" VerticalAlignment="Center" />