diff --git a/DecompilerNuGetDemos.workbook b/DecompilerNuGetDemos.workbook
index 0d4e3d504..6e0d3c3b7 100644
--- a/DecompilerNuGetDemos.workbook
+++ b/DecompilerNuGetDemos.workbook
@@ -6,7 +6,7 @@ platforms:
- DotNetCore
packages:
- id: ICSharpCode.Decompiler
- version: 5.0.0.5124
+ version: 6.0.0.5420-preview1
---
Setup: load the references required to work with the decompiler
diff --git a/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj b/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj
index 6d337805c..8097b323e 100644
--- a/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj
+++ b/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj
@@ -2,21 +2,22 @@
Exe
- netcoreapp2.1
+ netcoreapp2.1;netcoreapp3.1
+ true
true
true
ilspycmd
ilspycmd
- 5.0.0.5124
+ 6.0.0.5420-preview1
Command-line decompiler using the ILSpy decompilation engine
Copyright 2011-2019 AlphaSierraPapa
https://github.com/icsharpcode/ILSpy/
MIT
- ILSpyCmdNuGetPackageIcon.png
+ ILSpyCmdNuGetPackageIcon.png
https://github.com/icsharpcode/ILSpy/
- 5.0.0.0
- 5.0.0.0
+ 6.0.0.0
+ 6.0.0.0
true
ILSpy Team
@@ -26,6 +27,12 @@
NU1605
+
+
+
+
+
+
@@ -35,7 +42,7 @@
-
+
@@ -45,6 +52,7 @@
+
diff --git a/ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs b/ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs
index 02fdcdf23..3821f042f 100644
--- a/ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs
+++ b/ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs
@@ -12,6 +12,7 @@ using System.Threading;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using ICSharpCode.Decompiler.DebugInfo;
+using ICSharpCode.Decompiler.PdbProvider;
// ReSharper disable All
namespace ICSharpCode.Decompiler.Console
@@ -45,9 +46,13 @@ Remarks:
[Option("-il|--ilcode", "Show IL code.", CommandOptionType.NoValue)]
public bool ShowILCodeFlag { get; }
- [Option("-d|--debuginfo", "Generate PDB.", CommandOptionType.NoValue)]
+ [Option("-genpdb", "Generate PDB.", CommandOptionType.NoValue)]
public bool CreateDebugInfoFlag { get; }
+ [FileExistsOrNull]
+ [Option("-usepdb", "Use PDB.", CommandOptionType.SingleOrNoValue)]
+ public (bool IsSet, string Value) InputPDBFile { get; }
+
[Option("-l|--list ", "Lists all entities of the specified type(s). Valid types: c(lass), i(interface), s(truct), d(elegate), e(num)", CommandOptionType.MultipleValue)]
public string[] EntityTypes { get; } = new string[0];
@@ -142,7 +147,9 @@ Remarks:
foreach (var path in ReferencePaths) {
resolver.AddSearchDirectory(path);
}
- return new CSharpDecompiler(assemblyFileName, resolver, GetSettings());
+ return new CSharpDecompiler(assemblyFileName, resolver, GetSettings()) {
+ DebugInfoProvider = TryLoadPDB(module)
+ };
}
int ListContent(string assemblyFileName, TextWriter output, ISet kinds)
@@ -175,6 +182,7 @@ Remarks:
resolver.AddSearchDirectory(path);
}
decompiler.AssemblyResolver = resolver;
+ decompiler.DebugInfoProvider = TryLoadPDB(module);
decompiler.DecompileProject(module, outputDirectory);
return 0;
}
@@ -211,5 +219,16 @@ Remarks:
return 0;
}
+
+ IDebugInfoProvider TryLoadPDB(PEFile module)
+ {
+ if (InputPDBFile.IsSet) {
+ if (InputPDBFile.Value == null)
+ return DebugInfoUtils.LoadSymbols(module);
+ return DebugInfoUtils.FromFile(module, InputPDBFile.Value);
+ }
+
+ return null;
+ }
}
}
diff --git a/ICSharpCode.Decompiler.Console/ValidationAttributes.cs b/ICSharpCode.Decompiler.Console/ValidationAttributes.cs
index 802382bbf..a593b1674 100644
--- a/ICSharpCode.Decompiler.Console/ValidationAttributes.cs
+++ b/ICSharpCode.Decompiler.Console/ValidationAttributes.cs
@@ -1,19 +1,38 @@
using System;
using System.ComponentModel.DataAnnotations;
+using System.IO;
namespace ICSharpCode.Decompiler.Console
{
[AttributeUsage(AttributeTargets.Class)]
- public class ProjectOptionRequiresOutputDirectoryValidationAttribute : ValidationAttribute
+ public sealed class ProjectOptionRequiresOutputDirectoryValidationAttribute : ValidationAttribute
{
+ public ProjectOptionRequiresOutputDirectoryValidationAttribute()
+ {
+ }
+
protected override ValidationResult IsValid(object value, ValidationContext context)
{
if (value is ILSpyCmdProgram obj) {
- if (obj.CreateCompilableProjectFlag && String.IsNullOrEmpty(obj.OutputDirectory)) {
+ if (obj.CreateCompilableProjectFlag && string.IsNullOrEmpty(obj.OutputDirectory)) {
return new ValidationResult("--project cannot be used unless --outputdir is also specified");
}
}
return ValidationResult.Success;
}
}
+
+ [AttributeUsage(AttributeTargets.Property)]
+ public sealed class FileExistsOrNullAttribute : ValidationAttribute
+ {
+ protected override ValidationResult IsValid(object value, ValidationContext context)
+ {
+ var s = value as string;
+ if (string.IsNullOrEmpty(s))
+ return ValidationResult.Success;
+ if (File.Exists(s))
+ return ValidationResult.Success;
+ return new ValidationResult($"File '{s}' does not exist!");
+ }
+ }
}
diff --git a/ICSharpCode.Decompiler.PdbProvider.Cecil/MonoCecilDebugInfoProvider.cs b/ICSharpCode.Decompiler.PdbProvider.Cecil/MonoCecilDebugInfoProvider.cs
index 0b8f5a383..9f79d0c73 100644
--- a/ICSharpCode.Decompiler.PdbProvider.Cecil/MonoCecilDebugInfoProvider.cs
+++ b/ICSharpCode.Decompiler.PdbProvider.Cecil/MonoCecilDebugInfoProvider.cs
@@ -30,7 +30,7 @@ using Mono.Cecil;
using Mono.Cecil.Pdb;
using SRM = System.Reflection.Metadata;
-namespace ICSharpCode.Decompiler.PdbProvider.Cecil
+namespace ICSharpCode.Decompiler.PdbProvider
{
public class MonoCecilDebugInfoProvider : IDebugInfoProvider
{
diff --git a/ICSharpCode.Decompiler.PowerShell/GetDecompilerCmdlet.cs b/ICSharpCode.Decompiler.PowerShell/GetDecompilerCmdlet.cs
index a268c3c42..30616b253 100644
--- a/ICSharpCode.Decompiler.PowerShell/GetDecompilerCmdlet.cs
+++ b/ICSharpCode.Decompiler.PowerShell/GetDecompilerCmdlet.cs
@@ -1,8 +1,12 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Management.Automation;
+using System.Reflection.PortableExecutable;
using System.Text;
using ICSharpCode.Decompiler.CSharp;
+using ICSharpCode.Decompiler.Metadata;
+using ICSharpCode.Decompiler.PdbProvider;
namespace ICSharpCode.Decompiler.PowerShell
{
@@ -24,18 +28,25 @@ namespace ICSharpCode.Decompiler.PowerShell
[Parameter(HelpMessage = "Remove dead code")]
public bool RemoveDeadCode { get; set; }
+ [Parameter(HelpMessage = "Use PDB")]
+ public string PDBFilePath { get; set; }
+
protected override void ProcessRecord()
{
string path = GetUnresolvedProviderPathFromPSPath(LiteralPath);
try {
+ var module = new PEFile(LiteralPath, new FileStream(LiteralPath, FileMode.Open, FileAccess.Read), PEStreamOptions.Default);
+ var debugInfo = DebugInfoUtils.FromFile(module, PDBFilePath);
var decompiler = new CSharpDecompiler(path, new DecompilerSettings(LanguageVersion) {
ThrowOnAssemblyResolveErrors = false,
RemoveDeadCode = RemoveDeadCode,
- RemoveDeadStores = RemoveDeadStores
+ RemoveDeadStores = RemoveDeadStores,
+ UseDebugSymbols = debugInfo != null,
+ ShowDebugInfo = debugInfo != null,
});
+ decompiler.DebugInfoProvider = debugInfo;
WriteObject(decompiler);
-
} catch (Exception e) {
WriteVerbose(e.ToString());
WriteError(new ErrorRecord(e, ErrorIds.AssemblyLoadFailed, ErrorCategory.OperationStopped, null));
diff --git a/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj b/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj
index 4b621af3b..d092cc8f9 100644
--- a/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj
+++ b/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj
@@ -2,13 +2,21 @@
netstandard2.0
+ true
true
ICSharpCode.Decompiler.PowerShell
-
+
+
+
+
+
+
+
+
diff --git a/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs b/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs
index f75ca7a41..d9d89af5e 100644
--- a/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs
+++ b/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs
@@ -13,14 +13,12 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
var section = (AttributeSection)attribute.Parent;
SimpleType type = attribute.Type as SimpleType;
if (section.AttributeTarget == "assembly" &&
- (type.Identifier == "CompilationRelaxations" || type.Identifier == "RuntimeCompatibility" || type.Identifier == "SecurityPermission" || type.Identifier == "PermissionSet" || type.Identifier == "AssemblyVersion" || type.Identifier == "Debuggable" || type.Identifier == "TargetFramework"))
- {
+ (type.Identifier == "CompilationRelaxations" || type.Identifier == "RuntimeCompatibility" || type.Identifier == "SecurityPermission" || type.Identifier == "PermissionSet" || type.Identifier == "AssemblyVersion" || type.Identifier == "Debuggable" || type.Identifier == "TargetFramework")) {
attribute.Remove();
if (section.Attributes.Count == 0)
section.Remove();
}
- if (section.AttributeTarget == "module" && type.Identifier == "UnverifiableCode")
- {
+ if (section.AttributeTarget == "module" && type.Identifier == "UnverifiableCode") {
attribute.Remove();
if (section.Attributes.Count == 0)
section.Remove();
@@ -60,4 +58,21 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
rootNode.AcceptVisitor(this);
}
}
+
+ public class RemoveNamespaceMy : DepthFirstAstVisitor, IAstTransform
+ {
+ public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration)
+ {
+ if (namespaceDeclaration.Name == "My") {
+ namespaceDeclaration.Remove();
+ } else {
+ base.VisitNamespaceDeclaration(namespaceDeclaration);
+ }
+ }
+
+ public void Run(AstNode rootNode, TransformContext context)
+ {
+ rootNode.AcceptVisitor(this);
+ }
+ }
}
diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
index 8776bac1f..85e2cc4a1 100644
--- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
+++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
@@ -183,7 +183,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
return Regex.Replace(il, @"'\{[0-9A-F-]+\}'", "''");
}
- static readonly string coreRefAsmPath = new DotNetCorePathFinder(new Version(3, 0)).GetReferenceAssemblyPath(".NETCoreApp, Version = v3.0");
+ static readonly string coreRefAsmPath = new DotNetCorePathFinder(new Version(3, 1)).GetReferenceAssemblyPath(".NETCoreApp, Version = v3.1");
static readonly string refAsmPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
@"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2");
@@ -209,7 +209,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
const string targetFrameworkAttributeSnippet = @"
-[assembly: System.Runtime.Versioning.TargetFramework("".NETCoreApp, Version = v3.0"", FrameworkDisplayName = """")]
+[assembly: System.Runtime.Versioning.TargetFramework("".NETCoreApp, Version = v3.1"", FrameworkDisplayName = """")]
";
@@ -482,6 +482,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
CSharpDecompiler decompiler = new CSharpDecompiler(typeSystem, settings);
decompiler.AstTransforms.Insert(0, new RemoveEmbeddedAttributes());
decompiler.AstTransforms.Insert(0, new RemoveCompilerAttribute());
+ decompiler.AstTransforms.Insert(0, new RemoveNamespaceMy());
decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers());
var syntaxTree = decompiler.DecompileWholeModuleAsSingleFile(sortTypes: true);
diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
index a3070ae32..816c0fb31 100644
--- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -81,6 +81,7 @@
+
@@ -91,6 +92,7 @@
+
@@ -261,6 +263,7 @@
+
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs
index 1acbd5449..5a571f6aa 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs
@@ -379,11 +379,33 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return UseSpan(span);
}
+ public string GetSpan3()
+ {
+ Span span = stackalloc decimal[GetSize()];
+ return UseSpan(span);
+ }
+
+ public string GetSpan4()
+ {
+ Span span = stackalloc decimal[4] {
+ 1m,
+ 2m,
+ 3m,
+ 4m
+ };
+ return UseSpan(span);
+ }
+
public string UseSpan(Span span)
{
throw new NotImplementedException();
}
+ public string UseSpan(Span span)
+ {
+ throw new NotImplementedException();
+ }
+
public int GetSize()
{
throw new NotImplementedException();
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs
index 0704d886b..eb8d29d05 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs
@@ -1038,7 +1038,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
await Task.Delay(100);
if (string.IsNullOrEmpty(str)) {
-#if ROSLYN
+#if CS70
if (int.TryParse(str, out int id)) {
#else
int id;
@@ -1050,6 +1050,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
}
+
+ public void NullCoalescing()
+ {
+ Test>((string a, string b) => a ?? b, (string a, string b) => a ?? b);
+ Test>((int? a) => a ?? 1, (int? a) => a ?? 1);
+ }
}
internal static class Extensions
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PropertiesAndEvents.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PropertiesAndEvents.cs
index 5741b77bb..9ad23b9c6 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PropertiesAndEvents.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PropertiesAndEvents.cs
@@ -191,6 +191,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public event EventHandler AutomaticEventWithInitializer = delegate {
};
+#if ROSLYN
+ // Legacy csc has a bug where EventHandler is only used for the backing field
+ public event EventHandler DynamicAutoEvent;
+ public event EventHandler<(int A, string B)> AutoEventWithTuple;
+#endif
+#if CS80
+ public event EventHandler<(int a, dynamic? b)> ComplexAutoEvent;
+#endif
+
public event EventHandler CustomEvent {
add {
AutomaticEvent += value;
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs
index 5125b3982..a6b6813ee 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs
@@ -65,6 +65,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
+#if CS73
+ public class CustomPinnable
+ {
+ public ref int GetPinnableReference()
+ {
+ throw new NotImplementedException();
+ }
+ }
+#endif
+
public unsafe delegate void UnsafeDelegate(byte* ptr);
private UnsafeDelegate unsafeDelegate;
@@ -354,6 +364,22 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return ptr->ToString();
}
+#if CS73
+ public unsafe void PinSpan(Span span)
+ {
+ fixed (int* ptr = span) {
+ UsePointer(ptr);
+ }
+ }
+
+ //public unsafe void CustomPinReferenceType(CustomPinnable mem)
+ //{
+ // fixed (int* ptr = mem) {
+ // UsePointer(ptr);
+ // }
+ //}
+#endif
+
public unsafe string StackAlloc(int count)
{
char* ptr = stackalloc char[count];
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Select.cs b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Select.cs
new file mode 100644
index 000000000..1b9e9ede6
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Select.cs
@@ -0,0 +1,37 @@
+using Microsoft.VisualBasic.CompilerServices;
+using System;
+
+[StandardModule]
+internal sealed class Program
+{
+ public static void SelectOnString()
+ {
+ switch (Environment.CommandLine) {
+ case "123":
+ Console.WriteLine("a");
+ break;
+ case "444":
+ Console.WriteLine("b");
+ break;
+ case "222":
+ Console.WriteLine("c");
+ break;
+ case "11":
+ Console.WriteLine("d");
+ break;
+ case "dd":
+ Console.WriteLine("e");
+ break;
+ case "sss":
+ Console.WriteLine("f");
+ break;
+ case "aa":
+ Console.WriteLine("g");
+ break;
+ case null:
+ case "":
+ Console.WriteLine("empty");
+ break;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Select.vb b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Select.vb
new file mode 100644
index 000000000..2fa10c8fd
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Select.vb
@@ -0,0 +1,24 @@
+Imports System
+
+Module Program
+ Sub SelectOnString()
+ Select Case Environment.CommandLine
+ Case "123"
+ Console.WriteLine("a")
+ Case "444"
+ Console.WriteLine("b")
+ Case "222"
+ Console.WriteLine("c")
+ Case "11"
+ Console.WriteLine("d")
+ Case "dd"
+ Console.WriteLine("e")
+ Case "sss"
+ Console.WriteLine("f")
+ Case "aa"
+ Console.WriteLine("g")
+ Case ""
+ Console.WriteLine("empty")
+ End Select
+ End Sub
+End Module
diff --git a/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs
index 8cf1c28c0..d41f6aa70 100644
--- a/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs
@@ -47,7 +47,7 @@ namespace ICSharpCode.Decompiler.Tests
}
static readonly CompilerOptions[] defaultOptions =
-{
+ {
CompilerOptions.None,
CompilerOptions.Optimize,
CompilerOptions.UseRoslyn,
@@ -55,7 +55,7 @@ namespace ICSharpCode.Decompiler.Tests
};
static readonly CompilerOptions[] roslynOnlyOptions =
-{
+ {
CompilerOptions.UseRoslyn,
CompilerOptions.Optimize | CompilerOptions.UseRoslyn,
};
@@ -72,6 +72,12 @@ namespace ICSharpCode.Decompiler.Tests
Run(options: options | CompilerOptions.Library);
}
+ [Test]
+ public void Select([ValueSource(nameof(defaultOptions))] CompilerOptions options)
+ {
+ Run(options: options | CompilerOptions.Library);
+ }
+
void Run([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug, DecompilerSettings settings = null)
{
var vbFile = Path.Combine(TestCasePath, testName + ".vb");
diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
index 432f706ad..659e5490a 100644
--- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
@@ -819,8 +819,13 @@ namespace ICSharpCode.Decompiler.CSharp
var fixedStmt = new FixedStatement();
fixedStmt.Type = exprBuilder.ConvertType(inst.Variable.Type);
Expression initExpr;
- if (inst.Init.OpCode == OpCode.ArrayToPointer) {
- initExpr = exprBuilder.Translate(((ArrayToPointer)inst.Init).Array);
+ if (inst.Init is GetPinnableReference gpr) {
+ if (gpr.Method != null) {
+ IType expectedType = gpr.Method.IsStatic ? gpr.Method.Parameters[0].Type : gpr.Method.DeclaringType;
+ initExpr = exprBuilder.Translate(gpr.Argument, typeHint: expectedType).ConvertTo(expectedType, exprBuilder);
+ } else {
+ initExpr = exprBuilder.Translate(gpr.Argument);
+ }
} else {
initExpr = exprBuilder.Translate(inst.Init, typeHint: inst.Variable.Type).ConvertTo(inst.Variable.Type, exprBuilder);
}
diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
index ecf39fc27..75b516614 100644
--- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
@@ -1362,6 +1362,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
ClassType classType;
switch (typeDefinition.Kind) {
case TypeKind.Struct:
+ case TypeKind.Void:
classType = ClassType.Struct;
modifiers &= ~Modifiers.Sealed;
if (ShowModifiers) {
@@ -1420,7 +1421,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
decl.BaseTypes.Add(ConvertType(typeDefinition.EnumUnderlyingType));
}
// if the declared type is a struct, ignore System.ValueType
- } else if (typeDefinition.Kind == TypeKind.Struct && baseType.IsKnownType(KnownTypeCode.ValueType)) {
+ } else if ((typeDefinition.Kind == TypeKind.Struct || typeDefinition.Kind == TypeKind.Void) && baseType.IsKnownType(KnownTypeCode.ValueType)) {
continue;
// always ignore System.Object
} else if (baseType.IsKnownType(KnownTypeCode.Object)) {
diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
index 41600de3d..4a29f5aac 100644
--- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
+++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
@@ -432,6 +432,17 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
&& identExpr.TypeArguments.Count == 0;
}
+ bool CombineDeclarationAndInitializer(VariableToDeclare v, TransformContext context)
+ {
+ if (v.Type.IsByRefLike)
+ return true; // by-ref-like variables always must be initialized at their declaration.
+
+ if (v.InsertionPoint.nextNode.Role == ForStatement.InitializerRole)
+ return true; // for-statement initializers always should combine declaration and initialization.
+
+ return !context.Settings.SeparateLocalVariableDeclarations;
+ }
+
void InsertVariableDeclarations(TransformContext context)
{
var replacements = new List<(AstNode, AstNode)>();
@@ -439,7 +450,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (v.RemovedDueToCollision)
continue;
- if (IsMatchingAssignment(v, out AssignmentExpression assignment)) {
+ if (CombineDeclarationAndInitializer(v, context) && IsMatchingAssignment(v, out AssignmentExpression assignment)) {
// 'int v; v = expr;' can be combined to 'int v = expr;'
AstType type;
if (context.Settings.AnonymousTypes && v.Type.ContainsAnonymousType()) {
diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
index b4758059d..81814b0b0 100644
--- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
+++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
@@ -165,6 +165,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public ForStatement TransformFor(ExpressionStatement node)
{
+ if (!context.Settings.ForStatement)
+ return null;
Match m1 = variableAssignPattern.Match(node);
if (!m1.Success) return null;
var variable = m1.Get("variable").Single().GetILVariable();
@@ -810,16 +812,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
default:
return false;
}
- if (!ev.ReturnType.IsMatch(m.Get("type").Single())) {
- // Variable types must match event type,
- // except that the event type may have an additional nullability annotation
- if (ev.ReturnType is ComposedType ct && ct.HasOnlyNullableSpecifier) {
- if (!ct.BaseType.IsMatch(m.Get("type").Single()))
- return false;
- } else {
- return false;
- }
- }
+ var returnType = ev.ReturnType.GetResolveResult().Type;
+ var eventType = m.Get("type").Single().GetResolveResult().Type;
+ // ignore tuple element names, dynamic and nullability
+ if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(returnType, eventType))
+ return false;
var combineMethod = m.Get("delegateCombine").Single().Parent.GetSymbol() as IMethod;
if (combineMethod == null || combineMethod.Name != (isAddAccessor ? "Combine" : "Remove"))
return false;
@@ -889,9 +886,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
ed.Modifiers = ev.Modifiers;
ed.Variables.Add(new VariableInitializer(ev.Name));
ed.CopyAnnotationsFrom(ev);
-
- IEvent eventDef = ev.GetSymbol() as IEvent;
- if (eventDef != null) {
+
+ if (ev.GetSymbol() is IEvent eventDef) {
IField field = eventDef.DeclaringType.GetFields(f => f.Name == ev.Name, GetMemberOptions.IgnoreInheritedMembers).SingleOrDefault();
if (field != null) {
ed.AddAnnotation(field);
@@ -907,7 +903,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
}
}
-
+
ev.ReplaceWith(ed);
return ed;
}
@@ -1040,5 +1036,28 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return base.VisitBinaryOperatorExpression(expr);
}
#endregion
+
+ #region C# 7.3 pattern based fixed
+ static readonly Expression addressOfPinnableReference = new UnaryOperatorExpression {
+ Operator = UnaryOperatorType.AddressOf,
+ Expression = new InvocationExpression {
+ Target = new MemberReferenceExpression(new AnyNode("target"), "GetPinnableReference"),
+ Arguments = { }
+ }
+ };
+
+ public override AstNode VisitFixedStatement(FixedStatement fixedStatement)
+ {
+ if (context.Settings.PatternBasedFixedStatement) {
+ foreach (var v in fixedStatement.Variables) {
+ var m = addressOfPinnableReference.Match(v.Initializer);
+ if (m.Success) {
+ v.Initializer = m.Get("target").Single().Detach();
+ }
+ }
+ }
+ return base.VisitFixedStatement(fixedStatement);
+ }
+ #endregion
}
}
diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs
index fed45dec5..2b177db3f 100644
--- a/ICSharpCode.Decompiler/DecompilerSettings.cs
+++ b/ICSharpCode.Decompiler/DecompilerSettings.cs
@@ -104,6 +104,7 @@ namespace ICSharpCode.Decompiler
introduceUnmanagedConstraint = false;
stackAllocInitializers = false;
tupleComparisons = false;
+ patternBasedFixedStatement = false;
}
if (languageVersion < CSharp.LanguageVersion.CSharp8_0) {
nullableReferenceTypes = false;
@@ -117,7 +118,7 @@ namespace ICSharpCode.Decompiler
{
if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement)
return CSharp.LanguageVersion.CSharp8_0;
- if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers)
+ if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers || patternBasedFixedStatement)
return CSharp.LanguageVersion.CSharp7_3;
if (introduceRefModifiersOnStructs || introduceReadonlyAndInModifiers || nonTrailingNamedArguments || refExtensionMethods)
return CSharp.LanguageVersion.CSharp7_2;
@@ -926,6 +927,23 @@ namespace ICSharpCode.Decompiler
}
}
+ bool patternBasedFixedStatement = true;
+
+ ///
+ /// Gets/Sets whether C# 7.3 pattern based fixed statement should be used.
+ ///
+ [Category("C# 7.3 / VS 2017.7")]
+ [Description("DecompilerSettings.UsePatternBasedFixedStatement")]
+ public bool PatternBasedFixedStatement {
+ get { return patternBasedFixedStatement; }
+ set {
+ if (patternBasedFixedStatement != value) {
+ patternBasedFixedStatement = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
bool tupleTypes = true;
///
@@ -1224,6 +1242,57 @@ namespace ICSharpCode.Decompiler
#endregion
+ bool forStatement = true;
+
+ ///
+ /// Gets/sets whether the decompiler should produce for loops.
+ ///
+ [Category("C# 1.0 / VS .NET")]
+ [Description("DecompilerSettings.ForStatement")]
+ public bool ForStatement {
+ get { return forStatement; }
+ set {
+ if (forStatement != value) {
+ forStatement = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ bool doWhileStatement = true;
+
+ ///
+ /// Gets/sets whether the decompiler should produce do-while loops.
+ ///
+ [Category("C# 1.0 / VS .NET")]
+ [Description("DecompilerSettings.DoWhileStatement")]
+ public bool DoWhileStatement {
+ get { return doWhileStatement; }
+ set {
+ if (doWhileStatement != value) {
+ doWhileStatement = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ bool separateLocalVariableDeclarations = false;
+
+ ///
+ /// Gets/sets whether the decompiler should separate local variable declarations from their initialization.
+ ///
+ [Category("DecompilerSettings.Other")]
+ [Description("DecompilerSettings.SeparateLocalVariableDeclarations")]
+ public bool SeparateLocalVariableDeclarations {
+ get { return separateLocalVariableDeclarations; }
+ set {
+ if (separateLocalVariableDeclarations != value) {
+ separateLocalVariableDeclarations = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
CSharpFormattingOptions csharpFormattingOptions;
[Browsable(false)]
diff --git a/ICSharpCode.Decompiler/Documentation/XmlDocumentationElement.cs b/ICSharpCode.Decompiler/Documentation/XmlDocumentationElement.cs
index 7f360b9ed..3b8b0f21e 100644
--- a/ICSharpCode.Decompiler/Documentation/XmlDocumentationElement.cs
+++ b/ICSharpCode.Decompiler/Documentation/XmlDocumentationElement.cs
@@ -91,8 +91,12 @@ namespace ICSharpCode.Decompiler.Documentation
get {
if (!referencedEntityInitialized) {
string cref = GetAttribute("cref");
- if (!string.IsNullOrEmpty(cref) && crefResolver != null)
- referencedEntity = crefResolver(cref);
+ try {
+ if (!string.IsNullOrEmpty(cref) && crefResolver != null)
+ referencedEntity = crefResolver(cref);
+ } catch {
+ referencedEntity = null;
+ }
referencedEntityInitialized = true;
}
return referencedEntity;
@@ -113,7 +117,7 @@ namespace ICSharpCode.Decompiler.Documentation
///
public string GetAttribute(string name)
{
- return element?.Attribute(name)?.Value ?? string.Empty;
+ return element?.Attribute(name)?.Value;
}
///
diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs
index 049ad5bdc..402ea7f3e 100644
--- a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs
+++ b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs
@@ -139,11 +139,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
bool modified = false;
for (int i = 0; i < container.Blocks.Count; i++) {
var block = container.Blocks[i];
- ILVariable v, p;
- Block targetBlock;
- if (IsNullSafeArrayToPointerPattern(block, out v, out p, out targetBlock)) {
+ if (IsNullSafeArrayToPointerPattern(block, out ILVariable v, out ILVariable p, out Block targetBlock)) {
context.Step("NullSafeArrayToPointerPattern", block);
- ILInstruction arrayToPointer = new ArrayToPointer(new LdLoc(v));
+ ILInstruction arrayToPointer = new GetPinnableReference(new LdLoc(v), null);
if (p.StackType != StackType.Ref) {
arrayToPointer = new Conv(arrayToPointer, p.StackType.ToPrimitiveType(), false, Sign.None);
}
@@ -432,7 +430,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return; // variable access that is not LdLoc
}
}
- if (!(ldloc.Parent is ArrayToPointer arrayToPointer))
+ if (!(ldloc.Parent is GetPinnableReference arrayToPointer))
return;
if (!(arrayToPointer.Parent is Conv conv && conv.Kind == ConversionKind.StopGCTracking))
return;
@@ -446,7 +444,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
newVar.HasGeneratedName = oldVar.HasGeneratedName;
oldVar.Function.Variables.Add(newVar);
pinnedRegion.Variable = newVar;
- pinnedRegion.Init = new ArrayToPointer(pinnedRegion.Init).WithILRange(arrayToPointer);
+ pinnedRegion.Init = new GetPinnableReference(pinnedRegion.Init, arrayToPointer.Method).WithILRange(arrayToPointer);
conv.ReplaceWith(new LdLoc(newVar).WithILRange(conv));
}
@@ -514,7 +512,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
newVar.HasGeneratedName = pinnedRegion.Variable.HasGeneratedName;
pinnedRegion.Variable.Function.Variables.Add(newVar);
pinnedRegion.Variable = newVar;
- pinnedRegion.Init = new ArrayToPointer(pinnedRegion.Init);
+ pinnedRegion.Init = new GetPinnableReference(pinnedRegion.Init, null);
}
return;
}
@@ -542,7 +540,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// make targetBlock the new entry point
body.Blocks.RemoveAt(targetBlock.ChildIndex);
body.Blocks.Insert(0, targetBlock);
- pinnedRegion.Init = new ArrayToPointer(pinnedRegion.Init);
+ pinnedRegion.Init = new GetPinnableReference(pinnedRegion.Init, null);
ILVariable otherVar;
ILInstruction otherVarInit;
diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
index 5156a9fef..0c377228e 100644
--- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
+++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
@@ -131,7 +131,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
AnalyzeCurrentProperty();
ResolveIEnumerableIEnumeratorFieldMapping();
ConstructExceptionTable();
- newBody = AnalyzeMoveNext();
+ newBody = AnalyzeMoveNext(function);
} catch (SymbolicAnalysisFailedException) {
return;
}
@@ -525,12 +525,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
#endregion
#region Analyze MoveNext() and generate new body
- BlockContainer AnalyzeMoveNext()
+ BlockContainer AnalyzeMoveNext(ILFunction function)
{
context.StepStartGroup("AnalyzeMoveNext");
MethodDefinitionHandle moveNextMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault(m => metadata.GetString(metadata.GetMethodDefinition(m).Name) == "MoveNext");
ILFunction moveNextFunction = CreateILAst(moveNextMethod, context);
+ function.MoveNextMethod = moveNextFunction.Method;
+
// Copy-propagate temporaries holding a copy of 'this'.
// This is necessary because the old (pre-Roslyn) C# compiler likes to store 'this' in temporary variables.
foreach (var stloc in moveNextFunction.Descendants.OfType().Where(s => s.Variable.IsSingleDefinition && s.Value.MatchLdThis()).ToList()) {
diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs
index 0d01840b5..6a3192338 100644
--- a/ICSharpCode.Decompiler/IL/Instructions.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions.cs
@@ -184,9 +184,12 @@ namespace ICSharpCode.Decompiler.IL
LdLen,
/// Load address of array element.
LdElema,
- /// Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty.
- /// Also used to convert a string to a reference to the first character.
- ArrayToPointer,
+ /// Retrieves a pinnable reference for the input object.
+ /// The input must be an object reference (O).
+ /// If the input is an array/string, evaluates to a reference to the first element/character, or to a null reference if the array is null or empty.
+ /// Otherwise, uses the GetPinnableReference method to get the reference, or evaluates to a null reference if the input is null.
+ ///
+ GetPinnableReference,
/// Maps a string value to an integer. This is used in switch(string).
StringToInt,
/// ILAst representation of Expression.Convert.
@@ -4756,21 +4759,25 @@ namespace ICSharpCode.Decompiler.IL
}
namespace ICSharpCode.Decompiler.IL
{
- /// Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty.
- /// Also used to convert a string to a reference to the first character.
- public sealed partial class ArrayToPointer : ILInstruction
+ /// Retrieves a pinnable reference for the input object.
+ /// The input must be an object reference (O).
+ /// If the input is an array/string, evaluates to a reference to the first element/character, or to a null reference if the array is null or empty.
+ /// Otherwise, uses the GetPinnableReference method to get the reference, or evaluates to a null reference if the input is null.
+ ///
+ public sealed partial class GetPinnableReference : ILInstruction, IInstructionWithMethodOperand
{
- public ArrayToPointer(ILInstruction array) : base(OpCode.ArrayToPointer)
+ public GetPinnableReference(ILInstruction argument, IMethod method) : base(OpCode.GetPinnableReference)
{
- this.Array = array;
+ this.Argument = argument;
+ this.method = method;
}
- public static readonly SlotInfo ArraySlot = new SlotInfo("Array", canInlineInto: true);
- ILInstruction array;
- public ILInstruction Array {
- get { return this.array; }
+ public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true);
+ ILInstruction argument;
+ public ILInstruction Argument {
+ get { return this.argument; }
set {
ValidateChild(value);
- SetChildInstruction(ref this.array, value, 0);
+ SetChildInstruction(ref this.argument, value, 0);
}
}
protected sealed override int GetChildCount()
@@ -4781,7 +4788,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
case 0:
- return this.array;
+ return this.argument;
default:
throw new IndexOutOfRangeException();
}
@@ -4790,7 +4797,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
case 0:
- this.Array = value;
+ this.Argument = value;
break;
default:
throw new IndexOutOfRangeException();
@@ -4800,21 +4807,24 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
case 0:
- return ArraySlot;
+ return ArgumentSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone()
{
- var clone = (ArrayToPointer)ShallowClone();
- clone.Array = this.array.Clone();
+ var clone = (GetPinnableReference)ShallowClone();
+ clone.Argument = this.argument.Clone();
return clone;
}
public override StackType ResultType { get { return StackType.Ref; } }
+ readonly IMethod method;
+ /// Returns the method operand.
+ public IMethod Method { get { return method; } }
protected override InstructionFlags ComputeFlags()
{
- return array.Flags;
+ return argument.Flags;
}
public override InstructionFlags DirectFlags {
get {
@@ -4825,31 +4835,33 @@ namespace ICSharpCode.Decompiler.IL
{
WriteILRange(output, options);
output.Write(OpCode);
+ output.Write(' ');
+ method.WriteTo(output);
output.Write('(');
- this.array.WriteTo(output, options);
+ this.argument.WriteTo(output, options);
output.Write(')');
}
public override void AcceptVisitor(ILVisitor visitor)
{
- visitor.VisitArrayToPointer(this);
+ visitor.VisitGetPinnableReference(this);
}
public override T AcceptVisitor(ILVisitor visitor)
{
- return visitor.VisitArrayToPointer(this);
+ return visitor.VisitGetPinnableReference(this);
}
public override T AcceptVisitor(ILVisitor visitor, C context)
{
- return visitor.VisitArrayToPointer(this, context);
+ return visitor.VisitGetPinnableReference(this, context);
}
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
- var o = other as ArrayToPointer;
- return o != null && this.array.PerformMatch(o.array, ref match);
+ var o = other as GetPinnableReference;
+ return o != null && this.argument.PerformMatch(o.argument, ref match) && method.Equals(o.method);
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
- Debug.Assert(array.ResultType == StackType.O);
+ Debug.Assert(argument.ResultType == StackType.O);
}
}
}
@@ -6717,7 +6729,7 @@ namespace ICSharpCode.Decompiler.IL
{
Default(inst);
}
- protected internal virtual void VisitArrayToPointer(ArrayToPointer inst)
+ protected internal virtual void VisitGetPinnableReference(GetPinnableReference inst)
{
Default(inst);
}
@@ -7103,7 +7115,7 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
- protected internal virtual T VisitArrayToPointer(ArrayToPointer inst)
+ protected internal virtual T VisitGetPinnableReference(GetPinnableReference inst)
{
return Default(inst);
}
@@ -7489,7 +7501,7 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst, context);
}
- protected internal virtual T VisitArrayToPointer(ArrayToPointer inst, C context)
+ protected internal virtual T VisitGetPinnableReference(GetPinnableReference inst, C context)
{
return Default(inst, context);
}
@@ -7651,7 +7663,7 @@ namespace ICSharpCode.Decompiler.IL
"sizeof",
"ldlen",
"ldelema",
- "array.to.pointer",
+ "get.pinnable.reference",
"string.to.int",
"expression.tree.cast",
"user.logic.operator",
@@ -8202,14 +8214,16 @@ namespace ICSharpCode.Decompiler.IL
array = default(ILInstruction);
return false;
}
- public bool MatchArrayToPointer(out ILInstruction array)
+ public bool MatchGetPinnableReference(out ILInstruction argument, out IMethod method)
{
- var inst = this as ArrayToPointer;
+ var inst = this as GetPinnableReference;
if (inst != null) {
- array = inst.Array;
+ argument = inst.Argument;
+ method = inst.Method;
return true;
}
- array = default(ILInstruction);
+ argument = default(ILInstruction);
+ method = default(IMethod);
return false;
}
public bool MatchUserDefinedLogicOperator(out IMethod method, out ILInstruction left, out ILInstruction right)
diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt
index db99589f3..c4889bb7f 100644
--- a/ICSharpCode.Decompiler/IL/Instructions.tt
+++ b/ICSharpCode.Decompiler/IL/Instructions.tt
@@ -279,9 +279,11 @@
new OpCode("ldelema", "Load address of array element.",
CustomClassName("LdElema"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("array"), new ArgumentInfo("indices") { IsCollection = true } }, true),
MayThrowIfNotDelayed, ResultType("Ref"), SupportsReadonlyPrefix),
- new OpCode("array.to.pointer", "Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty." + Environment.NewLine
- + "Also used to convert a string to a reference to the first character.",
- CustomArguments(("array", new[] { "O" })), ResultType("Ref")),
+ new OpCode("get.pinnable.reference", "Retrieves a pinnable reference for the input object." + Environment.NewLine
+ + "The input must be an object reference (O)." + Environment.NewLine
+ + "If the input is an array/string, evaluates to a reference to the first element/character, or to a null reference if the array is null or empty." + Environment.NewLine
+ + "Otherwise, uses the GetPinnableReference method to get the reference, or evaluates to a null reference if the input is null." + Environment.NewLine,
+ CustomArguments(("argument", new[] { "O" })), ResultType("Ref"), HasMethodOperand),
new OpCode("string.to.int", "Maps a string value to an integer. This is used in switch(string).",
CustomArguments(("argument", new[] { "O" })), CustomConstructor, CustomWriteTo, ResultType("I4")),
diff --git a/ICSharpCode.Decompiler/IL/PointerArithmeticOffset.cs b/ICSharpCode.Decompiler/IL/PointerArithmeticOffset.cs
index d8048b2ed..4a32b9358 100644
--- a/ICSharpCode.Decompiler/IL/PointerArithmeticOffset.cs
+++ b/ICSharpCode.Decompiler/IL/PointerArithmeticOffset.cs
@@ -77,6 +77,8 @@ namespace ICSharpCode.Decompiler.IL
case KnownTypeCode.UInt64:
case KnownTypeCode.Double:
return 8;
+ case KnownTypeCode.Decimal:
+ return 16;
}
return null;
}
diff --git a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
index 80007b1c7..61913889f 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
@@ -140,7 +140,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// remove unused variables before assigning names
function.Variables.RemoveDead();
int numDisplayClassLocals = 0;
- foreach (var v in function.Variables) {
+ foreach (var v in function.Variables.OrderBy(v => v.Name)) {
switch (v.Kind) {
case VariableKind.Parameter: // ignore
break;
@@ -243,8 +243,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
reservedVariableNames.Add(nameWithoutDigits, number - 1);
}
int count = ++reservedVariableNames[nameWithoutDigits];
+ string nameWithDigits = nameWithoutDigits + count.ToString();
+ if (oldVariableName == nameWithDigits) {
+ return oldVariableName;
+ }
if (count != 1) {
- return nameWithoutDigits + count.ToString();
+ return nameWithDigits;
} else {
return nameWithoutDigits;
}
diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
index 0d73b2586..061ee7721 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
@@ -405,7 +405,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
var pointerType = new PointerType(elementType);
var elementCountInstr = PointerArithmeticOffset.Detect(sizeInBytesInstr, pointerType.ElementType, checkForOverflow: true, unwrapZeroExtension: true);
- if (!elementCountInstr.Match(elementCountInstr2).Success)
+ if (elementCountInstr == null || !elementCountInstr.Match(elementCountInstr2).Success)
return false;
return true;
}
diff --git a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs
index 2e4df8080..b119f0c62 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs
@@ -42,10 +42,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (loop.Kind != ContainerKind.Loop)
continue;
if (MatchWhileLoop(loop, out var condition, out var loopBody)) {
- MatchForLoop(loop, condition, loopBody);
+ if (context.Settings.ForStatement)
+ MatchForLoop(loop, condition, loopBody);
continue;
}
- if (MatchDoWhileLoop(loop))
+ if (context.Settings.DoWhileStatement && MatchDoWhileLoop(loop))
continue;
}
}
diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
index 1983006f2..95fd133ba 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
@@ -459,7 +459,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
break;
case OpCode.DynamicCompoundAssign:
return true;
- case OpCode.ArrayToPointer:
+ case OpCode.GetPinnableReference:
case OpCode.LocAllocSpan:
return true; // inline size-expressions into localloc.span
case OpCode.Call:
diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs
index 46d6a304c..7b39f389c 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs
@@ -160,6 +160,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// if (comp(ldloc switchValueVar == ldnull)) br defaultBlock
if (!instructions[i].MatchIfInstruction(out var condition, out var firstBlockOrDefaultJump))
return false;
+ var nextCaseJump = instructions[i + 1];
+ while (condition.MatchLogicNot(out var arg)) {
+ condition = arg;
+ ExtensionMethods.Swap(ref firstBlockOrDefaultJump, ref nextCaseJump);
+ }
+ // match call to operator ==(string, string)
+ if (!MatchStringEqualityComparison(condition, out var switchValueVar, out string firstBlockValue, out bool isVBCompareString))
+ return false;
+ if (isVBCompareString) {
+ ExtensionMethods.Swap(ref firstBlockOrDefaultJump, ref nextCaseJump);
+ }
if (firstBlockOrDefaultJump.MatchBranch(out var firstBlock)) {
// success
@@ -172,11 +183,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
List<(string, ILInstruction)> values = new List<(string, ILInstruction)>();
ILInstruction switchValue = null;
-
- // match call to operator ==(string, string)
- if (!MatchStringEqualityComparison(condition, out var switchValueVar, out string firstBlockValue))
- return false;
- values.Add((firstBlockValue, firstBlock ?? firstBlockOrDefaultJump));
+ if (isVBCompareString && string.IsNullOrEmpty(firstBlockValue)) {
+ values.Add((null, firstBlock ?? firstBlockOrDefaultJump));
+ values.Add((string.Empty, firstBlock ?? firstBlockOrDefaultJump));
+ } else {
+ values.Add((firstBlockValue, firstBlock ?? firstBlockOrDefaultJump));
+ }
bool extraLoad = false;
bool keepAssignmentBefore = false;
@@ -215,13 +227,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
switchValue = new LdLoc(switchValueVar);
}
// if instruction must be followed by a branch to the next case
- if (!(instructions.ElementAtOrDefault(i + 1) is Branch nextCaseJump))
+ if (!nextCaseJump.MatchBranch(out Block currentCaseBlock))
return false;
// extract all cases and add them to the values list.
- Block currentCaseBlock = nextCaseJump.TargetBlock;
ILInstruction nextCaseBlock;
- while ((nextCaseBlock = MatchCaseBlock(currentCaseBlock, switchValueVar, out string value, out Block block)) != null) {
- values.Add((value, block));
+ while ((nextCaseBlock = MatchCaseBlock(currentCaseBlock, switchValueVar, out string value, out bool emptyStringEqualsNull, out Block block)) != null) {
+ if (emptyStringEqualsNull && string.IsNullOrEmpty(value)) {
+ values.Add((null, block));
+ values.Add((string.Empty, block));
+ } else {
+ values.Add((value, block));
+ }
currentCaseBlock = nextCaseBlock as Block;
if (currentCaseBlock == null)
break;
@@ -367,37 +383,34 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// The is updated if the value gets copied to a different variable.
/// See comments below for more info.
///
- ILInstruction MatchCaseBlock(Block currentBlock, ILVariable switchVariable, out string value, out Block caseBlock)
+ ILInstruction MatchCaseBlock(Block currentBlock, ILVariable switchVariable, out string value, out bool emptyStringEqualsNull, out Block caseBlock)
{
value = null;
caseBlock = null;
+ emptyStringEqualsNull = false;
if (currentBlock.IncomingEdgeCount != 1 || currentBlock.Instructions.Count != 2)
return null;
- if (!currentBlock.Instructions[0].MatchIfInstruction(out var condition, out var caseBlockBranch))
+ if (!currentBlock.MatchIfAtEndOfBlock(out var condition, out var caseBlockBranch, out var nextBlockBranch))
+ return null;
+ if (!MatchStringEqualityComparison(condition, switchVariable, out value, out bool isVBCompareString)) {
return null;
+ }
+ if (isVBCompareString) {
+ ExtensionMethods.Swap(ref caseBlockBranch, ref nextBlockBranch);
+ emptyStringEqualsNull = true;
+ }
if (!caseBlockBranch.MatchBranch(out caseBlock))
return null;
- Block nextBlock;
- BlockContainer blockContainer = null;
- if (condition.MatchLogicNot(out var inner)) {
- condition = inner;
- nextBlock = caseBlock;
- if (!currentBlock.Instructions[1].MatchBranch(out caseBlock))
- return null;
+ if (nextBlockBranch.MatchBranch(out Block nextBlock)) {
+ // success
+ return nextBlock;
+ } else if (nextBlockBranch.MatchLeave(out BlockContainer blockContainer)) {
+ // success
+ return blockContainer;
} else {
- if (currentBlock.Instructions[1].MatchBranch(out nextBlock)) {
- // success
- } else if (currentBlock.Instructions[1].MatchLeave(out blockContainer)) {
- // success
- } else {
- return null;
- }
- }
- if (!MatchStringEqualityComparison(condition, switchVariable, out value)) {
return null;
}
- return nextBlock ?? (ILInstruction)blockContainer;
}
///
@@ -836,8 +849,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
MatchComputeStringHashCall(switchBlockInstructions[switchBlockInstructionsOffset], switchValueVar, out LdLoc switchValueLoad)))
return false;
- var stringValues = new List<(int Index, string Value, ILInstruction TargetBlockOrLeave)>();
- int index = 0;
+ var stringValues = new List<(string Value, ILInstruction TargetBlockOrLeave)>();
SwitchSection defaultSection = switchInst.Sections.MaxBy(s => s.Labels.Count());
Block exitOrDefaultBlock = null;
foreach (var section in switchInst.Sections) {
@@ -846,20 +858,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!section.Body.MatchBranch(out Block target))
return false;
string stringValue;
+ bool emptyStringEqualsNull;
if (MatchRoslynEmptyStringCaseBlockHead(target, switchValueLoad.Variable, out ILInstruction targetOrLeave, out Block currentExitBlock)) {
stringValue = "";
- } else if (!MatchRoslynCaseBlockHead(target, switchValueLoad.Variable, out targetOrLeave, out currentExitBlock, out stringValue)) {
+ emptyStringEqualsNull = false;
+ } else if (!MatchRoslynCaseBlockHead(target, switchValueLoad.Variable, out targetOrLeave, out currentExitBlock, out stringValue, out emptyStringEqualsNull)) {
return false;
}
if (exitOrDefaultBlock != null && exitOrDefaultBlock != currentExitBlock)
return false;
exitOrDefaultBlock = currentExitBlock;
- stringValues.Add((index++, stringValue, targetOrLeave));
+ if (emptyStringEqualsNull && string.IsNullOrEmpty(stringValue)) {
+ stringValues.Add((null, targetOrLeave));
+ stringValues.Add((string.Empty, targetOrLeave));
+ } else {
+ stringValues.Add((stringValue, targetOrLeave));
+ }
}
if (nullValueCaseBlock != null && exitOrDefaultBlock != nullValueCaseBlock) {
- stringValues.Add((index++, null, nullValueCaseBlock));
+ stringValues.Add((null, nullValueCaseBlock));
}
ILInstruction switchValueInst = switchValueLoad;
@@ -914,13 +933,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
SwitchInstruction ReplaceWithSwitchInstruction(int offset)
{
- var defaultLabel = new LongSet(new LongInterval(0, index)).Invert();
+ var defaultLabel = new LongSet(new LongInterval(0, stringValues.Count)).Invert();
var values = new string[stringValues.Count];
var sections = new SwitchSection[stringValues.Count];
- foreach (var (idx, (label, value, bodyInstruction)) in stringValues.WithIndex()) {
+ foreach (var (idx, (value, bodyInstruction)) in stringValues.WithIndex()) {
values[idx] = value;
var body = bodyInstruction is Block b ? new Branch(b) : bodyInstruction;
- sections[idx] = new SwitchSection { Labels = new LongSet(label), Body = body };
+ sections[idx] = new SwitchSection { Labels = new LongSet(idx), Body = body };
}
var newSwitch = new SwitchInstruction(new StringToInt(switchValueInst, values));
newSwitch.Sections.AddRange(sections);
@@ -935,26 +954,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// if (call op_Equality(ldloc switchValueVar, stringValue)) br body
/// br exit
///
- bool MatchRoslynCaseBlockHead(Block target, ILVariable switchValueVar, out ILInstruction bodyOrLeave, out Block defaultOrExitBlock, out string stringValue)
+ bool MatchRoslynCaseBlockHead(Block target, ILVariable switchValueVar, out ILInstruction bodyOrLeave, out Block defaultOrExitBlock, out string stringValue, out bool emptyStringEqualsNull)
{
bodyOrLeave = null;
defaultOrExitBlock = null;
stringValue = null;
+ emptyStringEqualsNull = false;
if (target.Instructions.Count != 2)
return false;
if (!target.Instructions[0].MatchIfInstruction(out var condition, out var bodyBranch))
return false;
- ILInstruction exitBranch;
+ ILInstruction exitBranch = target.Instructions[1];
// Handle negated conditions first:
- if (condition.MatchLogicNot(out var expr)) {
- exitBranch = bodyBranch;
- bodyBranch = target.Instructions[1];
+ while (condition.MatchLogicNot(out var expr)) {
+ ExtensionMethods.Swap(ref exitBranch, ref bodyBranch);
condition = expr;
- } else {
- exitBranch = target.Instructions[1];
}
- if (!MatchStringEqualityComparison(condition, switchValueVar, out stringValue))
+ if (!MatchStringEqualityComparison(condition, switchValueVar, out stringValue, out bool isVBCompareString))
return false;
+ if (isVBCompareString) {
+ ExtensionMethods.Swap(ref exitBranch, ref bodyBranch);
+ emptyStringEqualsNull = true;
+ }
if (!(exitBranch.MatchBranch(out defaultOrExitBlock) || exitBranch.MatchLeave(out _)))
return false;
if (bodyBranch.MatchLeave(out _)) {
@@ -1054,25 +1075,45 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Matches 'call string.op_Equality(ldloc(variable), ldstr(stringValue))'
/// or 'comp(ldloc(variable) == ldnull)'
///
- bool MatchStringEqualityComparison(ILInstruction condition, ILVariable variable, out string stringValue)
+ bool MatchStringEqualityComparison(ILInstruction condition, ILVariable variable, out string stringValue, out bool isVBCompareString)
{
- return MatchStringEqualityComparison(condition, out var v, out stringValue) && v == variable;
+ return MatchStringEqualityComparison(condition, out var v, out stringValue, out isVBCompareString) && v == variable;
}
///
/// Matches 'call string.op_Equality(ldloc(variable), ldstr(stringValue))'
/// or 'comp(ldloc(variable) == ldnull)'
///
- bool MatchStringEqualityComparison(ILInstruction condition, out ILVariable variable, out string stringValue)
+ bool MatchStringEqualityComparison(ILInstruction condition, out ILVariable variable, out string stringValue, out bool isVBCompareString)
{
stringValue = null;
variable = null;
- ILInstruction left, right;
- if (condition is Call c && c.Method.IsOperator && c.Method.Name == "op_Equality"
- && c.Method.DeclaringType.IsKnownType(KnownTypeCode.String) && c.Arguments.Count == 2)
- {
- left = c.Arguments[0];
- right = c.Arguments[1];
+ isVBCompareString = false;
+ while (condition is Comp comp && comp.Kind == ComparisonKind.Inequality && comp.Right.MatchLdcI4(0)) {
+ // if (x != 0) == if (x)
+ condition = comp.Left;
+ }
+ if (condition is Call c) {
+ ILInstruction left, right;
+ if (c.Method.IsOperator && c.Method.Name == "op_Equality"
+ && c.Method.DeclaringType.IsKnownType(KnownTypeCode.String) && c.Arguments.Count == 2) {
+ left = c.Arguments[0];
+ right = c.Arguments[1];
+ } else if (c.Method.IsStatic && c.Method.Name == "CompareString"
+ && c.Method.DeclaringType.FullName == "Microsoft.VisualBasic.CompilerServices.Operators"
+ && c.Arguments.Count == 3) {
+ left = c.Arguments[0];
+ right = c.Arguments[1];
+ // VB CompareString(): return 0 on equality -> condition is effectively negated.
+ // Also, the empty string is considered equal to null.
+ isVBCompareString = true;
+ if (!c.Arguments[2].MatchLdcI4(0)) {
+ // Option Compare Text: case insensitive comparison is not supported in C#
+ return false;
+ }
+ } else {
+ return false;
+ }
return left.MatchLdLoc(out variable) && right.MatchLdStr(out stringValue);
} else if (condition.MatchCompEqualsNull(out var arg)) {
stringValue = null;
diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs
index 537c1fb03..3c93013a2 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs
@@ -604,7 +604,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} else {
targetType = fallbackInstType;
}
- return (new NullCoalescingInstruction(kind, trueInst, fallbackInst), targetType);
+ return (new NullCoalescingInstruction(kind, trueInst, fallbackInst) {
+ UnderlyingResultType = trueInstTypeNonNullable.GetStackType()
+ }, targetType);
}
(ILInstruction, IType) ConvertComparison(CallInstruction invocation, ComparisonKind kind)
diff --git a/ICSharpCode.Decompiler/TypeSystem/Accessibility.cs b/ICSharpCode.Decompiler/TypeSystem/Accessibility.cs
index 5d7b5a13b..9cee2d55e 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Accessibility.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Accessibility.cs
@@ -16,6 +16,9 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
+using System.Diagnostics;
+using ICSharpCode.Decompiler.Util;
+
namespace ICSharpCode.Decompiler.TypeSystem
{
///
@@ -23,8 +26,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
///
public enum Accessibility : byte
{
- // note: some code depends on the fact that these values are within the range 0-7
-
+ // Note: some code depends on the fact that these values are within the range 0-7
+
///
/// The entity is completely inaccessible. This is used for C# explicit interface implementations.
///
@@ -34,26 +37,83 @@ namespace ICSharpCode.Decompiler.TypeSystem
///
Private,
///
- /// The entity is accessible everywhere.
+ /// The entity is accessible in derived classes within the same assembly.
+ /// This corresponds to C# private protected.
///
- Public,
+ ProtectedAndInternal,
///
/// The entity is only accessible within the same class and in derived classes.
///
Protected,
///
- /// The entity is accessible within the same project content.
+ /// The entity is accessible within the same assembly.
///
Internal,
///
- /// The entity is accessible both everywhere in the project content, and in all derived classes.
+ /// The entity is accessible both everywhere in the assembly, and in all derived classes.
+ /// This corresponds to C# protected internal.
///
- /// This corresponds to C# 'protected internal'.
ProtectedOrInternal,
///
- /// The entity is accessible in derived classes within the same project content.
+ /// The entity is accessible everywhere.
///
- /// This corresponds to C# 'private protected'.
- ProtectedAndInternal,
+ Public,
+ }
+
+ public static class AccessibilityExtensions
+ {
+ // This code depends on the fact that the enum values are sorted similar to the partial order
+ // where an accessibility is smaller than another if it makes an entity visibible to a subset of the code:
+ // digraph Accessibilities {
+ // none -> private -> protected_and_internal -> protected -> protected_or_internal -> public;
+ // none -> private -> protected_and_internal -> internal -> protected_or_internal -> public;
+ // }
+
+ ///
+ /// Gets whether a <= b in the partial order of accessibilities:
+ /// return true if b is accessible everywhere where a is accessible.
+ ///
+ public static bool LessThanOrEqual(this Accessibility a, Accessibility b)
+ {
+ // Exploit the enum order being similar to the partial order to dramatically simplify the logic here:
+ // protected vs. internal is the only pair for which the enum value order doesn't match the partial order
+ return a <= b && !(a == Accessibility.Protected && b == Accessibility.Internal);
+ }
+
+ ///
+ /// Computes the intersection of the two accessibilities:
+ /// The result is accessible from any given point in the code
+ /// iff both a and b are accessible from that point.
+ ///
+ public static Accessibility Intersect(this Accessibility a, Accessibility b)
+ {
+ if (a > b) {
+ ExtensionMethods.Swap(ref a, ref b);
+ }
+ if (a == Accessibility.Protected && b == Accessibility.Internal) {
+ return Accessibility.ProtectedAndInternal;
+ } else {
+ Debug.Assert(!(a == Accessibility.Internal && b == Accessibility.Protected));
+ return a;
+ }
+ }
+
+ ///
+ /// Computes the union of the two accessibilities:
+ /// The result is accessible from any given point in the code
+ /// iff at least one of a or b is accessible from that point.
+ ///
+ public static Accessibility Union(this Accessibility a, Accessibility b)
+ {
+ if (a > b) {
+ ExtensionMethods.Swap(ref a, ref b);
+ }
+ if (a == Accessibility.Protected && b == Accessibility.Internal) {
+ return Accessibility.ProtectedOrInternal;
+ } else {
+ Debug.Assert(!(a == Accessibility.Internal && b == Accessibility.Protected));
+ return b;
+ }
+ }
}
}
diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs
index 264bb5e02..cd98cf907 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs
@@ -196,37 +196,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return baseMember.Accessibility;
}
}
- return MergePropertyAccessibility(
+ return AccessibilityExtensions.Union(
this.Getter?.Accessibility ?? Accessibility.None,
this.Setter?.Accessibility ?? Accessibility.None);
}
-
- static internal Accessibility MergePropertyAccessibility(Accessibility left, Accessibility right)
- {
- if (left == Accessibility.Public || right == Accessibility.Public)
- return Accessibility.Public;
-
- if (left == Accessibility.ProtectedOrInternal || right == Accessibility.ProtectedOrInternal)
- return Accessibility.ProtectedOrInternal;
-
- if (left == Accessibility.Protected && right == Accessibility.Internal ||
- left == Accessibility.Internal && right == Accessibility.Protected)
- return Accessibility.ProtectedOrInternal;
-
- if (left == Accessibility.Protected || right == Accessibility.Protected)
- return Accessibility.Protected;
-
- if (left == Accessibility.Internal || right == Accessibility.Internal)
- return Accessibility.Internal;
-
- if (left == Accessibility.ProtectedAndInternal || right == Accessibility.ProtectedAndInternal)
- return Accessibility.ProtectedAndInternal;
-
- if (left == Accessibility.Private || right == Accessibility.Private)
- return Accessibility.Private;
-
- return left;
- }
#endregion
public bool IsStatic => AnyAccessor?.IsStatic ?? false;
diff --git a/ILSpy/Analyzers/AnalyzerScope.cs b/ILSpy/Analyzers/AnalyzerScope.cs
index 6a6e27344..68eac7f9d 100644
--- a/ILSpy/Analyzers/AnalyzerScope.cs
+++ b/ILSpy/Analyzers/AnalyzerScope.cs
@@ -45,7 +45,7 @@ namespace ICSharpCode.ILSpy.Analyzers
public ITypeDefinition TypeScope => typeScope;
- Accessibility memberAccessibility, typeAccessibility;
+ Accessibility effectiveAccessibility;
public AnalyzerScope(AssemblyList assemblyList, IEntity entity)
{
@@ -53,13 +53,12 @@ namespace ICSharpCode.ILSpy.Analyzers
AnalyzedSymbol = entity;
if (entity is ITypeDefinition type) {
typeScope = type;
- memberAccessibility = Accessibility.None;
+ effectiveAccessibility = DetermineEffectiveAccessibility(ref typeScope);
} else {
typeScope = entity.DeclaringTypeDefinition;
- memberAccessibility = entity.Accessibility;
+ effectiveAccessibility = DetermineEffectiveAccessibility(ref typeScope, entity.Accessibility);
}
- typeAccessibility = DetermineTypeAccessibility(ref typeScope);
- IsLocal = memberAccessibility == Accessibility.Private || typeAccessibility == Accessibility.Private;
+ IsLocal = effectiveAccessibility.LessThanOrEqual(Accessibility.Private);
}
public IEnumerable GetModulesInScope(CancellationToken ct)
@@ -67,10 +66,7 @@ namespace ICSharpCode.ILSpy.Analyzers
if (IsLocal)
return new[] { TypeScope.ParentModule.PEFile };
- if (memberAccessibility == Accessibility.Internal ||
- memberAccessibility == Accessibility.ProtectedOrInternal ||
- typeAccessibility == Accessibility.Internal ||
- typeAccessibility == Accessibility.ProtectedAndInternal)
+ if (effectiveAccessibility.LessThanOrEqual(Accessibility.Internal))
return GetModuleAndAnyFriends(TypeScope, ct);
return GetReferencingModules(TypeScope.ParentModule.PEFile, ct);
@@ -89,11 +85,7 @@ namespace ICSharpCode.ILSpy.Analyzers
{
if (IsLocal) {
var typeSystem = new DecompilerTypeSystem(TypeScope.ParentModule.PEFile, TypeScope.ParentModule.PEFile.GetAssemblyResolver());
- ITypeDefinition scope = typeScope;
- if (memberAccessibility != Accessibility.Private && typeScope.DeclaringTypeDefinition != null) {
- scope = typeScope.DeclaringTypeDefinition;
- }
- foreach (var type in TreeTraversal.PreOrder(scope, t => t.NestedTypes)) {
+ foreach (var type in TreeTraversal.PreOrder(typeScope, t => t.NestedTypes)) {
yield return type;
}
} else {
@@ -106,23 +98,17 @@ namespace ICSharpCode.ILSpy.Analyzers
}
}
- Accessibility DetermineTypeAccessibility(ref ITypeDefinition typeScope)
+ static Accessibility DetermineEffectiveAccessibility(ref ITypeDefinition typeScope, Accessibility memberAccessibility = Accessibility.Public)
{
- var typeAccessibility = typeScope.Accessibility;
- while (typeScope.DeclaringType != null) {
- Accessibility accessibility = typeScope.Accessibility;
- if ((int)typeAccessibility > (int)accessibility) {
- typeAccessibility = accessibility;
- if (typeAccessibility == Accessibility.Private)
- break;
- }
+ Accessibility accessibility = memberAccessibility;
+ while (typeScope.DeclaringTypeDefinition != null && !accessibility.LessThanOrEqual(Accessibility.Private)) {
+ accessibility = accessibility.Intersect(typeScope.Accessibility);
typeScope = typeScope.DeclaringTypeDefinition;
}
-
- if ((int)typeAccessibility > (int)Accessibility.Internal) {
- typeAccessibility = Accessibility.Internal;
- }
- return typeAccessibility;
+ // Once we reach a private entity, we leave the loop with typeScope set to the class that
+ // contains the private entity = the scope that needs to be searched.
+ // Otherwise (if we don't find a private entity) we return the top-level class.
+ return accessibility;
}
#region Find modules
@@ -134,24 +120,39 @@ namespace ICSharpCode.ILSpy.Analyzers
if (typeScope.TypeParameterCount > 0)
reflectionTypeScopeName += "`" + typeScope.TypeParameterCount;
- foreach (var assembly in AssemblyList.GetAssemblies()) {
- ct.ThrowIfCancellationRequested();
- bool found = false;
- var module = assembly.GetPEFileOrNull();
- if (module == null || !module.IsAssembly)
- continue;
- var resolver = assembly.GetAssemblyResolver();
- foreach (var reference in module.AssemblyReferences) {
- using (LoadedAssembly.DisableAssemblyLoad()) {
- if (resolver.Resolve(reference) == self) {
- found = true;
- break;
+ var toWalkFiles = new Stack();
+ var checkedFiles = new HashSet();
+
+ toWalkFiles.Push(self);
+ checkedFiles.Add(self);
+
+ do {
+ PEFile curFile = toWalkFiles.Pop();
+ foreach (var assembly in AssemblyList.GetAssemblies()) {
+ ct.ThrowIfCancellationRequested();
+ bool found = false;
+ var module = assembly.GetPEFileOrNull();
+ if (module == null || !module.IsAssembly)
+ continue;
+ if (checkedFiles.Contains(module))
+ continue;
+ var resolver = assembly.GetAssemblyResolver();
+ foreach (var reference in module.AssemblyReferences) {
+ using (LoadedAssembly.DisableAssemblyLoad()) {
+ if (resolver.Resolve(reference) == curFile) {
+ found = true;
+ break;
+ }
}
}
+ if (found && checkedFiles.Add(module)) {
+ if (ModuleReferencesScopeType(module.Metadata, reflectionTypeScopeName, typeScope.Namespace))
+ yield return module;
+ if (ModuleForwardsScopeType(module.Metadata, reflectionTypeScopeName, typeScope.Namespace))
+ toWalkFiles.Push(module);
+ }
}
- if (found && ModuleReferencesScopeType(module.Metadata, reflectionTypeScopeName, typeScope.Namespace))
- yield return module;
- }
+ } while (toWalkFiles.Count > 0);
}
IEnumerable GetModuleAndAnyFriends(ITypeDefinition typeScope, CancellationToken ct)
@@ -198,6 +199,19 @@ namespace ICSharpCode.ILSpy.Analyzers
}
return hasRef;
}
+
+ bool ModuleForwardsScopeType(MetadataReader metadata, string typeScopeName, string typeScopeNamespace)
+ {
+ bool hasForward = false;
+ foreach (var h in metadata.ExportedTypes) {
+ var exportedType = metadata.GetExportedType(h);
+ if (exportedType.IsForwarder && metadata.StringComparer.Equals(exportedType.Name, typeScopeName) && metadata.StringComparer.Equals(exportedType.Namespace, typeScopeNamespace)) {
+ hasForward = true;
+ break;
+ }
+ }
+ return hasForward;
+ }
#endregion
}
}
diff --git a/ILSpy/Analyzers/AnalyzerTreeView.cs b/ILSpy/Analyzers/AnalyzerTreeView.cs
index 4e6402b12..edbdf43cf 100644
--- a/ILSpy/Analyzers/AnalyzerTreeView.cs
+++ b/ILSpy/Analyzers/AnalyzerTreeView.cs
@@ -60,7 +60,7 @@ namespace ICSharpCode.ILSpy.Analyzers
public void Show()
{
- AnalyzerPaneModel.Instance.IsVisible = true;
+ AnalyzerPaneModel.Instance.Show();
}
public void Show(AnalyzerTreeNode node)
diff --git a/ILSpy/AssemblyList.cs b/ILSpy/AssemblyList.cs
index caa7f8ae3..6739bf1c5 100644
--- a/ILSpy/AssemblyList.cs
+++ b/ILSpy/AssemblyList.cs
@@ -70,6 +70,15 @@ namespace ICSharpCode.ILSpy
}
this.dirty = false; // OpenAssembly() sets dirty, so reset it afterwards
}
+
+ ///
+ /// Creates a copy of an assembly list.
+ ///
+ public AssemblyList(AssemblyList list, string newName)
+ : this(newName)
+ {
+ this.assemblies.AddRange(list.assemblies);
+ }
///
/// Gets the loaded assemblies. This method is thread-safe.
diff --git a/ILSpy/AssemblyListManager.cs b/ILSpy/AssemblyListManager.cs
index 7c989f4eb..0c3404fc6 100644
--- a/ILSpy/AssemblyListManager.cs
+++ b/ILSpy/AssemblyListManager.cs
@@ -30,22 +30,26 @@ namespace ICSharpCode.ILSpy
///
sealed class AssemblyListManager
{
+ ILSpySettings spySettings;
+
public AssemblyListManager(ILSpySettings spySettings)
{
+ this.spySettings = spySettings;
XElement doc = spySettings["AssemblyLists"];
foreach (var list in doc.Elements("List")) {
AssemblyLists.Add((string)list.Attribute("name"));
}
}
- public readonly ObservableCollection AssemblyLists = new ObservableCollection();
+ public ObservableCollection AssemblyLists { get; } = new ObservableCollection();
///
/// Loads an assembly list from the ILSpySettings.
/// If no list with the specified name is found, the default list is loaded instead.
///
- public AssemblyList LoadList(ILSpySettings spySettings, string listName)
+ public AssemblyList LoadList(ILSpySettings settings, string listName)
{
+ this.spySettings = settings;
AssemblyList list = DoLoadList(spySettings, listName);
if (!AssemblyLists.Contains(list.ListName))
AssemblyLists.Add(list.ListName);
@@ -62,13 +66,16 @@ namespace ICSharpCode.ILSpy
}
}
}
- XElement firstList = doc.Elements("List").FirstOrDefault();
- if (firstList != null)
- return new AssemblyList(firstList);
- else
- return new AssemblyList(listName ?? DefaultListName);
+ return new AssemblyList(listName ?? DefaultListName);
}
-
+
+ public bool CloneList(string selectedAssemblyList, string newListName)
+ {
+ var list = DoLoadList(spySettings, selectedAssemblyList);
+ var newList = new AssemblyList(list, newListName);
+ return CreateList(newList);
+ }
+
public const string DefaultListName = "(Default)";
///
diff --git a/ILSpy/Commands/DelegateCommand.cs b/ILSpy/Commands/DelegateCommand.cs
new file mode 100644
index 000000000..4c0c033d2
--- /dev/null
+++ b/ILSpy/Commands/DelegateCommand.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+
+namespace ICSharpCode.ILSpy.Commands
+{
+ public class DelegateCommand : ICommand
+ {
+ private readonly Action action;
+ private readonly Func canExecute;
+
+ public event EventHandler CanExecuteChanged {
+ add { CommandManager.RequerySuggested += value; }
+ remove { CommandManager.RequerySuggested -= value; }
+ }
+
+ public DelegateCommand(Action action)
+ : this(action, _ => true)
+ {
+ }
+
+ public DelegateCommand(Action action, Func canExecute)
+ {
+ this.action = action;
+ this.canExecute = canExecute;
+ }
+
+ public bool CanExecute(object parameter)
+ {
+ return canExecute((T)parameter);
+ }
+
+ public void Execute(object parameter)
+ {
+ action((T)parameter);
+ }
+ }
+}
diff --git a/ILSpy/Commands/ILSpyCommands.cs b/ILSpy/Commands/ILSpyCommands.cs
index 4a2d7e98f..379432b7f 100644
--- a/ILSpy/Commands/ILSpyCommands.cs
+++ b/ILSpy/Commands/ILSpyCommands.cs
@@ -29,5 +29,6 @@ namespace ICSharpCode.ILSpy
static class ILSpyCommands
{
public static readonly AnalyzeCommand Analyze = new AnalyzeCommand();
+ public static readonly ManageAssemblyListsCommand ManageAssemblyListsCommand = new ManageAssemblyListsCommand();
}
}
diff --git a/ILSpy/Commands/OpenListCommand.cs b/ILSpy/Commands/ManageAssemblyListsCommand.cs
similarity index 81%
rename from ILSpy/Commands/OpenListCommand.cs
rename to ILSpy/Commands/ManageAssemblyListsCommand.cs
index 807171875..49effa4a7 100644
--- a/ILSpy/Commands/OpenListCommand.cs
+++ b/ILSpy/Commands/ManageAssemblyListsCommand.cs
@@ -21,15 +21,14 @@ using ICSharpCode.ILSpy.Properties;
namespace ICSharpCode.ILSpy
{
- [ExportMainMenuCommand(Menu = nameof(Resources._File), Header = nameof(Resources.Open_List), MenuIcon = "Images/AssemblyList", MenuCategory = nameof(Resources.Open), MenuOrder = 1.7)]
- sealed class OpenListCommand : SimpleCommand
+ [ExportMainMenuCommand(Menu = nameof(Resources._File), Header = nameof(Resources.ManageAssemblyLists), MenuIcon = "Images/AssemblyList", MenuCategory = nameof(Resources.Open), MenuOrder = 1.7)]
+ sealed class ManageAssemblyListsCommand : SimpleCommand
{
public override void Execute(object parameter)
{
- OpenListDialog dlg = new OpenListDialog();
+ ManageAssemblyListsDialog dlg = new ManageAssemblyListsDialog();
dlg.Owner = MainWindow.Instance;
- if (dlg.ShowDialog() == true)
- MainWindow.Instance.ShowAssemblyList(dlg.SelectedListName);
+ dlg.ShowDialog();
}
}
}
diff --git a/ILSpy/Commands/ShowDebugSteps.cs b/ILSpy/Commands/ShowDebugSteps.cs
index 4f7c5fa8d..a5c3d98fd 100644
--- a/ILSpy/Commands/ShowDebugSteps.cs
+++ b/ILSpy/Commands/ShowDebugSteps.cs
@@ -11,7 +11,7 @@ namespace ICSharpCode.ILSpy.Commands
{
public override void Execute(object parameter)
{
- DebugStepsPaneModel.Instance.IsVisible = true;
+ DebugStepsPaneModel.Instance.Show();
}
}
}
diff --git a/ILSpy/DebugInfo/DebugInfoUtils.cs b/ILSpy/DebugInfo/DebugInfoUtils.cs
new file mode 100644
index 000000000..f52a790d7
--- /dev/null
+++ b/ILSpy/DebugInfo/DebugInfoUtils.cs
@@ -0,0 +1,113 @@
+// Copyright (c) 2018 Siegfried Pammer
+//
+// 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.IO;
+using System.Linq;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using System.Runtime.InteropServices;
+using ICSharpCode.Decompiler.DebugInfo;
+using ICSharpCode.Decompiler.Metadata;
+
+namespace ICSharpCode.Decompiler.PdbProvider
+{
+ static class DebugInfoUtils
+ {
+ 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);
+ } else {
+ // search for pdb in same directory as dll
+ pdbFileName = Path.Combine(Path.GetDirectoryName(module.FileName), Path.GetFileNameWithoutExtension(module.FileName) + ".pdb");
+ if (File.Exists(pdbFileName)) {
+ return new MonoCecilDebugInfoProvider(module, pdbFileName);
+ }
+ }
+ } catch (Exception ex) when (ex is BadImageFormatException || ex is COMException) {
+ // Ignore PDB load errors
+ }
+ return null;
+ }
+
+ public static IDebugInfoProvider FromFile(PEFile module, string pdbFileName)
+ {
+ if (string.IsNullOrEmpty(pdbFileName))
+ return null;
+
+ Stream stream = OpenStream(pdbFileName);
+ if (stream == null)
+ return null;
+
+ if (stream.Read(buffer, 0, buffer.Length) == LegacyPDBPrefix.Length
+ && System.Text.Encoding.ASCII.GetString(buffer) == LegacyPDBPrefix) {
+ stream.Position = 0;
+ return new MonoCecilDebugInfoProvider(module, pdbFileName);
+ } else {
+ stream.Position = 0;
+ var provider = MetadataReaderProvider.FromPortablePdbStream(stream);
+ return new PortableDebugInfoProvider(pdbFileName, provider);
+ }
+ }
+
+ const string LegacyPDBPrefix = "Microsoft C/C++ MSF 7.00";
+ static readonly byte[] buffer = new byte[LegacyPDBPrefix.Length];
+
+ static bool TryOpenPortablePdb(PEFile module, out MetadataReaderProvider provider, out string pdbFileName)
+ {
+ provider = null;
+ pdbFileName = null;
+ var reader = module.Reader;
+ foreach (var entry in reader.ReadDebugDirectory()) {
+ if (entry.IsPortableCodeView) {
+ return reader.TryOpenAssociatedPortablePdb(module.FileName, OpenStream, out provider, out pdbFileName);
+ }
+ if (entry.Type == DebugDirectoryEntryType.CodeView) {
+ string pdbDirectory = Path.GetDirectoryName(module.FileName);
+ pdbFileName = Path.Combine(pdbDirectory, Path.GetFileNameWithoutExtension(module.FileName) + ".pdb");
+ if (File.Exists(pdbFileName)) {
+ Stream stream = OpenStream(pdbFileName);
+ if (stream.Read(buffer, 0, buffer.Length) == LegacyPDBPrefix.Length
+ && System.Text.Encoding.ASCII.GetString(buffer) == LegacyPDBPrefix) {
+ return false;
+ }
+ stream.Position = 0;
+ provider = MetadataReaderProvider.FromPortablePdbStream(stream);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ static Stream OpenStream(string fileName)
+ {
+ if (!File.Exists(fileName))
+ return null;
+ var memory = new MemoryStream();
+ using (var stream = File.OpenRead(fileName))
+ stream.CopyTo(memory);
+ memory.Position = 0;
+ return memory;
+ }
+ }
+}
diff --git a/ILSpy/DebugInfo/PortableDebugInfoProvider.cs b/ILSpy/DebugInfo/PortableDebugInfoProvider.cs
index c76cc2977..b811ce681 100644
--- a/ILSpy/DebugInfo/PortableDebugInfoProvider.cs
+++ b/ILSpy/DebugInfo/PortableDebugInfoProvider.cs
@@ -20,9 +20,8 @@ using System;
using System.Collections.Generic;
using System.Reflection.Metadata;
using ICSharpCode.Decompiler.DebugInfo;
-using ICSharpCode.Decompiler.Metadata;
-namespace ICSharpCode.ILSpy.DebugInfo
+namespace ICSharpCode.Decompiler.PdbProvider
{
class PortableDebugInfoProvider : IDebugInfoProvider
{
diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj
index 01e573f79..26c105b08 100644
--- a/ILSpy/ILSpy.csproj
+++ b/ILSpy/ILSpy.csproj
@@ -108,12 +108,13 @@
+
-
+
@@ -135,11 +136,14 @@
-
+
+
+
CreateListDialog.xaml
+
-
+
DebugSteps.xaml
@@ -313,18 +317,16 @@
-
+
NugetPackageBrowserDialog.xaml
-
+
OpenFromGacDialog.xaml
ResourceStringTable.xaml
-
- OpenListDialog.xaml
-
+
DisplaySettingsPanel.xaml
@@ -438,8 +440,8 @@
-
-
+
+
@@ -504,9 +506,9 @@
-
-
-
+
+
+
diff --git a/ILSpy/Languages/ILAstLanguage.cs b/ILSpy/Languages/ILAstLanguage.cs
index 3d0fbdc8a..d7c100990 100644
--- a/ILSpy/Languages/ILAstLanguage.cs
+++ b/ILSpy/Languages/ILAstLanguage.cs
@@ -31,6 +31,7 @@ using ICSharpCode.Decompiler.TypeSystem;
using SRM = System.Reflection.Metadata;
using static System.Reflection.Metadata.PEReaderExtensions;
+using ICSharpCode.ILSpy.ViewModels;
namespace ICSharpCode.ILSpy
{
@@ -140,7 +141,7 @@ namespace ICSharpCode.ILSpy
}
}
(output as ISmartTextOutput)?.AddButton(Images.ViewCode, "Show Steps", delegate {
- DebugSteps.Show();
+ DebugStepsPaneModel.Instance.Show();
});
output.WriteLine();
il.WriteTo(output, DebugSteps.Options);
diff --git a/ILSpy/LoadedAssembly.cs b/ILSpy/LoadedAssembly.cs
index 78b3e1854..46fe60622 100644
--- a/ILSpy/LoadedAssembly.cs
+++ b/ILSpy/LoadedAssembly.cs
@@ -28,10 +28,9 @@ using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Metadata;
-using ICSharpCode.Decompiler.PdbProvider.Cecil;
+using ICSharpCode.Decompiler.PdbProvider;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
-using ICSharpCode.ILSpy.DebugInfo;
using ICSharpCode.ILSpy.Options;
namespace ICSharpCode.ILSpy
@@ -167,7 +166,7 @@ namespace ICSharpCode.ILSpy
if (DecompilerSettingsPanel.CurrentDecompilerSettings.UseDebugSymbols) {
try {
- LoadSymbols(module);
+ debugInfoProvider = DebugInfoUtils.LoadSymbols(module);
} catch (IOException) {
} catch (UnauthorizedAccessException) {
} catch (InvalidOperationException) {
@@ -180,70 +179,6 @@ namespace ICSharpCode.ILSpy
return module;
}
- void 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)) {
- debugInfoProvider = new PortableDebugInfoProvider(pdbFileName, provider);
- } else {
- // search for pdb in same directory as dll
- string pdbDirectory = Path.GetDirectoryName(fileName);
- pdbFileName = Path.Combine(pdbDirectory, Path.GetFileNameWithoutExtension(fileName) + ".pdb");
- if (File.Exists(pdbFileName)) {
- debugInfoProvider = new MonoCecilDebugInfoProvider(module, pdbFileName);
- return;
- }
-
- // TODO: use symbol cache, get symbols from microsoft
- }
- } catch (Exception ex) when (ex is BadImageFormatException || ex is COMException) {
- // Ignore PDB load errors
- }
- }
-
- const string LegacyPDBPrefix = "Microsoft C/C++ MSF 7.00";
- byte[] buffer = new byte[LegacyPDBPrefix.Length];
-
- bool TryOpenPortablePdb(PEFile module, out MetadataReaderProvider provider, out string pdbFileName)
- {
- provider = null;
- pdbFileName = null;
- var reader = module.Reader;
- foreach (var entry in reader.ReadDebugDirectory()) {
- if (entry.IsPortableCodeView) {
- return reader.TryOpenAssociatedPortablePdb(fileName, OpenStream, out provider, out pdbFileName);
- }
- if (entry.Type == DebugDirectoryEntryType.CodeView) {
- string pdbDirectory = Path.GetDirectoryName(fileName);
- pdbFileName = Path.Combine(pdbDirectory, Path.GetFileNameWithoutExtension(fileName) + ".pdb");
- if (File.Exists(pdbFileName)) {
- Stream stream = OpenStream(pdbFileName);
- if (stream.Read(buffer, 0, buffer.Length) == LegacyPDBPrefix.Length
- && System.Text.Encoding.ASCII.GetString(buffer) == LegacyPDBPrefix) {
- return false;
- }
- stream.Position = 0;
- provider = MetadataReaderProvider.FromPortablePdbStream(stream);
- return true;
- }
- }
- }
- return false;
- }
-
- Stream OpenStream(string fileName)
- {
- if (!File.Exists(fileName))
- return null;
- var memory = new MemoryStream();
- using (var stream = File.OpenRead(fileName))
- stream.CopyTo(memory);
- memory.Position = 0;
- return memory;
- }
-
[ThreadStatic]
static int assemblyLoadDisableCount;
diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml
index 77c6086b8..3eb590066 100644
--- a/ILSpy/MainWindow.xaml
+++ b/ILSpy/MainWindow.xaml
@@ -6,7 +6,6 @@
xmlns:local="clr-namespace:ICSharpCode.ILSpy"
xmlns:avalondock="http://schemas.xceed.com/wpf/xaml/avalondock"
xmlns:controls="clr-namespace:ICSharpCode.ILSpy.Controls"
- xmlns:avalondockproperties="clr-namespace:Xceed.Wpf.AvalonDock.Properties;assembly=Xceed.Wpf.AvalonDock"
xmlns:docking="clr-namespace:ICSharpCode.ILSpy.Docking"
xmlns:textview="clr-namespace:ICSharpCode.ILSpy.TextView"
xmlns:analyzers="clr-namespace:ICSharpCode.ILSpy.Analyzers"
@@ -121,6 +120,13 @@
+
+
+
diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs
index d294cf5d2..546c45ac7 100644
--- a/ILSpy/MainWindow.xaml.cs
+++ b/ILSpy/MainWindow.xaml.cs
@@ -24,6 +24,7 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
+using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
@@ -56,6 +57,7 @@ namespace ICSharpCode.ILSpy
{
public DockWorkspace Workspace { get; set; }
public SessionSettings SessionSettings { get; set; }
+ public AssemblyListManager AssemblyListManager { get; set; }
}
///
@@ -68,8 +70,6 @@ namespace ICSharpCode.ILSpy
readonly NavigationHistory history = new NavigationHistory();
ILSpySettings spySettingsForMainWindow_Loaded;
internal SessionSettings sessionSettings;
-
- internal AssemblyListManager assemblyListManager;
AssemblyList assemblyList;
AssemblyListTreeNode assemblyListTreeNode;
@@ -83,6 +83,8 @@ namespace ICSharpCode.ILSpy
get { return sessionSettings; }
}
+ internal AssemblyListManager AssemblyListManager { get; }
+
public SharpTreeView treeView {
get {
return FindResource("TreeView") as SharpTreeView;
@@ -107,19 +109,21 @@ namespace ICSharpCode.ILSpy
var spySettings = ILSpySettings.Load();
this.spySettingsForMainWindow_Loaded = spySettings;
this.sessionSettings = new SessionSettings(spySettings);
- this.assemblyListManager = new AssemblyListManager(spySettings);
+ this.AssemblyListManager = new AssemblyListManager(spySettings);
this.Icon = new BitmapImage(new Uri("pack://application:,,,/ILSpy;component/images/ILSpy.ico"));
this.DataContext = new MainWindowDataContext {
Workspace = DockWorkspace.Instance,
- SessionSettings = sessionSettings
+ SessionSettings = sessionSettings,
+ AssemblyListManager = AssemblyListManager
};
DockWorkspace.Instance.LoadSettings(sessionSettings);
InitializeComponent();
DockWorkspace.Instance.InitializeLayout(DockManager);
sessionSettings.FilterSettings.PropertyChanged += filterSettings_PropertyChanged;
+ sessionSettings.PropertyChanged += SessionSettings_PropertyChanged;
InitMainMenu();
InitToolbar();
@@ -128,6 +132,13 @@ namespace ICSharpCode.ILSpy
this.Loaded += MainWindow_Loaded;
}
+ private void SessionSettings_PropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "ActiveAssemblyList") {
+ ShowAssemblyList(sessionSettings.ActiveAssemblyList);
+ }
+ }
+
void SetWindowBounds(Rect bounds)
{
this.Left = bounds.Left;
@@ -376,7 +387,7 @@ namespace ICSharpCode.ILSpy
} else if (spySettings != null) {
SharpTreeNode node = null;
if (activeTreeViewPath?.Length > 0) {
- foreach (var asm in assemblyList.GetAssemblies()) {
+ foreach (var asm in CurrentAssemblyList.GetAssemblies()) {
if (asm.FileName == activeTreeViewPath[0]) {
// FindNodeByPath() blocks the UI if the assembly is not yet loaded,
// so use an async wait instead.
@@ -466,10 +477,10 @@ namespace ICSharpCode.ILSpy
if (loadPreviousAssemblies) {
// Load AssemblyList only in Loaded event so that WPF is initialized before we start the CPU-heavy stuff.
// This makes the UI come up a bit faster.
- this.assemblyList = assemblyListManager.LoadList(spySettings, sessionSettings.ActiveAssemblyList);
+ this.assemblyList = AssemblyListManager.LoadList(spySettings, sessionSettings.ActiveAssemblyList);
} else {
this.assemblyList = new AssemblyList(AssemblyListManager.DefaultListName);
- assemblyListManager.ClearAll();
+ AssemblyListManager.ClearAll();
}
HandleCommandLineArguments(App.CommandLineArguments);
@@ -584,8 +595,7 @@ namespace ICSharpCode.ILSpy
public void ShowAssemblyList(string name)
{
- ILSpySettings settings = ILSpySettings.Load();
- AssemblyList list = this.assemblyListManager.LoadList(settings, name);
+ AssemblyList list = this.AssemblyListManager.LoadList(ILSpySettings.Load(), name);
//Only load a new list when it is a different one
if (list.ListName != CurrentAssemblyList.ListName) {
ShowAssemblyList(list);
@@ -906,7 +916,7 @@ namespace ICSharpCode.ILSpy
try {
refreshInProgress = true;
var path = GetPathForNode(treeView.SelectedItem as SharpTreeNode);
- ShowAssemblyList(assemblyListManager.LoadList(ILSpySettings.Load(), assemblyList.ListName));
+ ShowAssemblyList(AssemblyListManager.LoadList(ILSpySettings.Load(), assemblyList.ListName));
SelectNode(FindNodeByPath(path, true));
} finally {
refreshInProgress = false;
@@ -915,7 +925,7 @@ namespace ICSharpCode.ILSpy
void SearchCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
- SearchPaneModel.Instance.IsVisible = true;
+ SearchPaneModel.Instance.Show();
}
#endregion
diff --git a/ILSpy/OpenListDialog.xaml b/ILSpy/OpenListDialog.xaml
deleted file mode 100644
index 99c5864e1..000000000
--- a/ILSpy/OpenListDialog.xaml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs
index 134f9faf0..1b1daa917 100644
--- a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs
+++ b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs
@@ -128,6 +128,12 @@ namespace ICSharpCode.ILSpy.Options
CheckBox checkBox = (CheckBox)sender;
checkBox.IsChecked = IsGroupChecked((CollectionViewGroup)checkBox.DataContext);
}
+
+ public void LoadDefaults()
+ {
+ currentDecompilerSettings = new Decompiler.DecompilerSettings();
+ this.DataContext = new DecompilerSettings(currentDecompilerSettings);
+ }
}
public class DecompilerSettings : INotifyPropertyChanged
diff --git a/ILSpy/Options/DisplaySettings.cs b/ILSpy/Options/DisplaySettings.cs
index a887e2913..950a9b6c4 100644
--- a/ILSpy/Options/DisplaySettings.cs
+++ b/ILSpy/Options/DisplaySettings.cs
@@ -29,6 +29,13 @@ namespace ICSharpCode.ILSpy.Options
{
public DisplaySettings()
{
+ this.selectedFont = new FontFamily("Consolas");
+ this.selectedFontSize = 10.0 * 4 / 3;
+ this.sortResults = true;
+ this.indentationUseTabs = true;
+ this.indentationSize = 4;
+ this.indentationTabSize = 4;
+ this.highlightMatchingBraces = true;
}
#region INotifyPropertyChanged implementation
diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml.cs b/ILSpy/Options/DisplaySettingsPanel.xaml.cs
index ef13c55d9..a8196a560 100644
--- a/ILSpy/Options/DisplaySettingsPanel.xaml.cs
+++ b/ILSpy/Options/DisplaySettingsPanel.xaml.cs
@@ -164,6 +164,12 @@ namespace ICSharpCode.ILSpy.Options
if (!text.All(char.IsDigit))
e.CancelCommand();
}
+
+ public void LoadDefaults()
+ {
+ currentDisplaySettings = new DisplaySettings();
+ this.DataContext = currentDisplaySettings;
+ }
}
public class FontSizeConverter : IValueConverter
diff --git a/ILSpy/Options/MiscSettings.cs b/ILSpy/Options/MiscSettings.cs
index a78cc8609..d5c7659a5 100644
--- a/ILSpy/Options/MiscSettings.cs
+++ b/ILSpy/Options/MiscSettings.cs
@@ -26,6 +26,11 @@ namespace ICSharpCode.ILSpy.Options
bool allowMultipleInstances;
bool loadPreviousAssemblies;
+ public MiscSettings()
+ {
+ this.loadPreviousAssemblies = true;
+ }
+
///
/// Allow multiple instances.
///
diff --git a/ILSpy/Options/MiscSettingsPanel.xaml.cs b/ILSpy/Options/MiscSettingsPanel.xaml.cs
index 75f0dd151..866efcf0a 100644
--- a/ILSpy/Options/MiscSettingsPanel.xaml.cs
+++ b/ILSpy/Options/MiscSettingsPanel.xaml.cs
@@ -71,5 +71,11 @@ namespace ICSharpCode.ILSpy.Options
currentMiscSettings = null; // invalidate cached settings
}
+
+ public void LoadDefaults()
+ {
+ currentMiscSettings = new MiscSettings();
+ this.DataContext = currentMiscSettings;
+ }
}
}
diff --git a/ILSpy/Options/OptionsDialog.xaml b/ILSpy/Options/OptionsDialog.xaml
index 5eb0389ee..f8b9232fb 100644
--- a/ILSpy/Options/OptionsDialog.xaml
+++ b/ILSpy/Options/OptionsDialog.xaml
@@ -15,8 +15,9 @@
-
-
+
+
+
\ No newline at end of file
diff --git a/ILSpy/Options/OptionsDialog.xaml.cs b/ILSpy/Options/OptionsDialog.xaml.cs
index d4f5a6191..b6f2161da 100644
--- a/ILSpy/Options/OptionsDialog.xaml.cs
+++ b/ILSpy/Options/OptionsDialog.xaml.cs
@@ -68,8 +68,17 @@ namespace ICSharpCode.ILSpy.Options
this.DialogResult = true;
Close();
}
+
+ private void DefaultsButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (MessageBox.Show(Properties.Resources.ResetToDefaultsConfirmationMessage, "ILSpy", MessageBoxButton.YesNo) == MessageBoxResult.Yes) {
+ var page = ((TabItem)tabControl.SelectedItem).Content as IOptionPage;
+ if (page != null)
+ page.LoadDefaults();
+ }
+ }
}
-
+
public interface IOptionsMetadata
{
string Title { get; }
@@ -80,6 +89,7 @@ namespace ICSharpCode.ILSpy.Options
{
void Load(ILSpySettings settings);
void Save(XElement root);
+ void LoadDefaults();
}
[MetadataAttribute]
diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs
index af49b718b..21b15a4c0 100644
--- a/ILSpy/Properties/Resources.Designer.cs
+++ b/ILSpy/Properties/Resources.Designer.cs
@@ -96,15 +96,6 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
- ///
- /// Looks up a localized string similar to _Create.
- ///
- public static string _Create {
- get {
- return ResourceManager.GetString("_Create", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to _File.
///
@@ -132,6 +123,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to _New.
+ ///
+ public static string _New {
+ get {
+ return ResourceManager.GetString("_New", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to _Open....
///
@@ -376,6 +376,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to C_lone.
+ ///
+ public static string C_lone {
+ get {
+ return ResourceManager.GetString("C_lone", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Cancel.
///
@@ -421,6 +430,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Close.
+ ///
+ public static string Close {
+ get {
+ return ResourceManager.GetString("Close", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Collapse all tree nodes.
///
@@ -763,6 +781,24 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Transform to do-while, if possible..
+ ///
+ public static string DecompilerSettings_DoWhileStatement {
+ get {
+ return ResourceManager.GetString("DecompilerSettings.DoWhileStatement", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Transform to for, if possible..
+ ///
+ public static string DecompilerSettings_ForStatement {
+ get {
+ return ResourceManager.GetString("DecompilerSettings.ForStatement", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to F#-specific options.
///
@@ -891,6 +927,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Separate local variable declarations and initializers (int x = 5; -> int x; x = 5;), if possible..
+ ///
+ public static string DecompilerSettings_SeparateLocalVariableDeclarations {
+ get {
+ return ResourceManager.GetString("DecompilerSettings.SeparateLocalVariableDeclarations", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Show info from debug symbols, if available.
///
@@ -999,6 +1044,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Use pattern-based fixed statement.
+ ///
+ public static string DecompilerSettings_UsePatternBasedFixedStatement {
+ get {
+ return ResourceManager.GetString("DecompilerSettings.UsePatternBasedFixedStatement", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Use stackalloc initializer syntax.
///
@@ -1305,6 +1359,33 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Are you sure that you want to delete the selected assembly list?.
+ ///
+ public static string ListDeleteConfirmation {
+ get {
+ return ResourceManager.GetString("ListDeleteConfirmation", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to A list with the same name was found..
+ ///
+ public static string ListExistsAlready {
+ get {
+ return ResourceManager.GetString("ListExistsAlready", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Are you sure that you want to remove all assembly lists and recreate the default assembly lists?.
+ ///
+ public static string ListsResetConfirmation {
+ get {
+ return ResourceManager.GetString("ListsResetConfirmation", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Load assemblies that were loaded in the last instance..
///
@@ -1332,6 +1413,24 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Manage assembly _lists....
+ ///
+ public static string ManageAssembly_Lists {
+ get {
+ return ResourceManager.GetString("ManageAssembly_Lists", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Manage Assembly Lists.
+ ///
+ public static string ManageAssemblyLists {
+ get {
+ return ResourceManager.GetString("ManageAssemblyLists", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Misc.
///
@@ -1386,15 +1485,6 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
- ///
- /// Looks up a localized string similar to Open _List....
- ///
- public static string Open_List {
- get {
- return ResourceManager.GetString("Open_List", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Open Explorer.
///
@@ -1422,15 +1512,6 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
- ///
- /// Looks up a localized string similar to Open List.
- ///
- public static string OpenList {
- get {
- return ResourceManager.GetString("OpenList", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to _Delete.
///
@@ -1557,6 +1638,24 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Reset to defaults.
+ ///
+ public static string ResetToDefaults {
+ get {
+ return ResourceManager.GetString("ResetToDefaults", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Do you really want to load the default settings for the active page?.
+ ///
+ public static string ResetToDefaultsConfirmationMessage {
+ get {
+ return ResourceManager.GetString("ResetToDefaultsConfirmationMessage", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Resources file (*.resources)|*.resources|Resource XML file|*.resx.
///
@@ -1656,6 +1755,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Select a list of assemblies.
+ ///
+ public static string SelectAssemblyListDropdownTooltip {
+ get {
+ return ResourceManager.GetString("SelectAssemblyListDropdownTooltip", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Select language to decompile to.
///
diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx
index c2d4ba976..4a7684684 100644
--- a/ILSpy/Properties/Resources.resx
+++ b/ILSpy/Properties/Resources.resx
@@ -156,8 +156,8 @@
Open from _GAC...
-
- Open _List...
+
+ Manage assembly _lists...
Reload all assemblies
@@ -450,14 +450,14 @@
Public Key Token
-
- Open List
+
+ Manage Assembly Lists
Select a list:
-
- _Create
+
+ _New
_Open
@@ -790,4 +790,40 @@ Are you sure you want to continue?
_Window
+
+ Use pattern-based fixed statement
+
+
+ Transform to do-while, if possible.
+
+
+ Transform to for, if possible.
+
+
+ Separate local variable declarations and initializers (int x = 5; -> int x; x = 5;), if possible.
+
+
+ Reset to defaults
+
+
+ Do you really want to load the default settings for the active page?
+
+
+ Select a list of assemblies
+
+
+ Close
+
+
+ C_lone
+
+
+ Are you sure that you want to delete the selected assembly list?
+
+
+ A list with the same name was found.
+
+
+ Are you sure that you want to remove all assembly lists and recreate the default assembly lists?
+
\ No newline at end of file
diff --git a/ILSpy/SessionSettings.cs b/ILSpy/SessionSettings.cs
index b05a45e23..bd7faa5cf 100644
--- a/ILSpy/SessionSettings.cs
+++ b/ILSpy/SessionSettings.cs
@@ -20,6 +20,7 @@ using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
+using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
@@ -63,10 +64,9 @@ namespace ICSharpCode.ILSpy
public event PropertyChangedEventHandler PropertyChanged;
- void OnPropertyChanged(string propertyName)
+ void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
- if (PropertyChanged != null)
- PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public FilterSettings FilterSettings { get; private set; }
@@ -75,7 +75,15 @@ namespace ICSharpCode.ILSpy
public string[] ActiveTreeViewPath;
public string ActiveAutoLoadedAssembly;
- public string ActiveAssemblyList;
+ public string ActiveAssemblyList {
+ get => activeAssemblyList;
+ set {
+ if (value != activeAssemblyList) {
+ activeAssemblyList = value;
+ OnPropertyChanged();
+ }
+ }
+ }
public WindowState WindowState = WindowState.Normal;
public Rect WindowBounds;
@@ -118,6 +126,7 @@ namespace ICSharpCode.ILSpy
}
static Regex regex = new Regex("\\\\x(?[0-9A-f]{4})");
+ private string activeAssemblyList;
static string Escape(string p)
{
diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs
index 5fa68e137..56f5633bf 100644
--- a/ILSpy/TreeNodes/AssemblyTreeNode.cs
+++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs
@@ -127,8 +127,6 @@ namespace ICSharpCode.ILSpy.TreeNodes
RaisePropertyChanged("Tooltip");
if (moduleTask.IsFaulted) {
RaisePropertyChanged("ShowExpander"); // cannot expand assemblies with load error
- // observe the exception so that the Task's finalizer doesn't re-throw it
- try { moduleTask.Wait(); } catch (AggregateException) { }
} else {
RaisePropertyChanged("Text"); // shortname might have changed
}
diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs
index 750c5bc61..be9bc9546 100644
--- a/ILSpy/TreeNodes/TypeTreeNode.cs
+++ b/ILSpy/TreeNodes/TypeTreeNode.cs
@@ -122,6 +122,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
case TypeKind.Interface:
return TypeIcon.Interface;
case TypeKind.Struct:
+ case TypeKind.Void:
return TypeIcon.Struct;
case TypeKind.Delegate:
return TypeIcon.Delegate;
diff --git a/ILSpy/OpenListDialog.xaml.cs b/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs
similarity index 72%
rename from ILSpy/OpenListDialog.xaml.cs
rename to ILSpy/ViewModels/ManageAssemblyListsViewModel.cs
index cf073e2de..baf1e6be0 100644
--- a/ILSpy/OpenListDialog.xaml.cs
+++ b/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+// Copyright (c) 2019 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
@@ -16,61 +16,117 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
+using System.Collections.ObjectModel;
using System.Windows;
-using System.Windows.Controls;
using System.Windows.Input;
-using System;
using ICSharpCode.Decompiler.Metadata;
+using ICSharpCode.ILSpy.Commands;
-namespace ICSharpCode.ILSpy
+namespace ICSharpCode.ILSpy.ViewModels
{
- ///
- /// Interaction logic for OpenListDialog.xaml
- ///
- public partial class OpenListDialog : Window
+ public class ManageAssemblyListsViewModel : ViewModelBase
{
-
public const string DotNet4List = ".NET 4 (WPF)";
public const string DotNet35List = ".NET 3.5";
public const string ASPDotNetMVC3List = "ASP.NET (MVC3)";
- readonly AssemblyListManager manager;
+ private readonly AssemblyListManager manager;
- public OpenListDialog()
+ public ManageAssemblyListsViewModel()
{
- InitializeComponent();
- manager = MainWindow.Instance.assemblyListManager;
+ this.manager = MainWindow.Instance.AssemblyListManager;
+ CreateDefaultAssemblyLists();
+
+ NewCommand = new DelegateCommand(ExecuteNew);
+ CloneCommand = new DelegateCommand(ExecuteClone, CanExecuteClone);
+ ResetCommand = new DelegateCommand(ExecuteReset);
+ DeleteCommand = new DelegateCommand(ExecuteDelete, CanExecuteDelete);
}
- private void listView_Loaded(object sender, RoutedEventArgs e)
- {
- listView.ItemsSource = manager.AssemblyLists;
- CreateDefaultAssemblyLists();
+ public ObservableCollection AssemblyLists => manager.AssemblyLists;
+
+ private string selectedAssemblyList;
+
+ public string SelectedAssemblyList {
+ get => selectedAssemblyList;
+ set {
+ if (selectedAssemblyList != value) {
+ selectedAssemblyList = value;
+ RaisePropertyChanged();
+ }
+ }
}
- void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ public ICommand NewCommand { get; }
+ public ICommand CloneCommand { get; }
+ public ICommand ResetCommand { get; }
+ public ICommand DeleteCommand { get; }
+
+ private void ExecuteNew(ManageAssemblyListsDialog dialog)
{
- okButton.IsEnabled = listView.SelectedItem != null;
- deleteButton.IsEnabled = listView.SelectedItem != null;
+ CreateListDialog dlg = new CreateListDialog();
+ dlg.Owner = dialog;
+ dlg.Closing += (s, args) => {
+ if (dlg.DialogResult == true) {
+ if (manager.AssemblyLists.Contains(dlg.NewListName)) {
+ args.Cancel = true;
+ MessageBox.Show(Properties.Resources.ListExistsAlready, null, MessageBoxButton.OK);
+ }
+ }
+ };
+ if (dlg.ShowDialog() == true) {
+ manager.CreateList(new AssemblyList(dlg.NewListName));
+ }
}
- void OKButton_Click(object sender, RoutedEventArgs e)
+ private bool CanExecuteClone(ManageAssemblyListsDialog _)
{
- this.DialogResult = true;
+ return selectedAssemblyList != null;
}
- public string SelectedListName
+ private void ExecuteClone(ManageAssemblyListsDialog dialog)
{
- get
- {
- return listView.SelectedItem.ToString();
+ CreateListDialog dlg = new CreateListDialog();
+ dlg.Owner = dialog;
+ dlg.Closing += (s, args) => {
+ if (dlg.DialogResult == true) {
+ if (manager.AssemblyLists.Contains(dlg.NewListName)) {
+ args.Cancel = true;
+ MessageBox.Show(Properties.Resources.ListExistsAlready, null, MessageBoxButton.OK);
+ }
+ }
+ };
+ if (dlg.ShowDialog() == true) {
+ manager.CloneList(SelectedAssemblyList, dlg.NewListName);
}
}
+ private void ExecuteReset(ManageAssemblyListsDialog dialog)
+ {
+ if (MessageBox.Show(dialog, Properties.Resources.ListsResetConfirmation,
+ "ILSpy", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No, MessageBoxOptions.None) != MessageBoxResult.Yes)
+ return;
+ manager.ClearAll();
+ CreateDefaultAssemblyLists();
+ MainWindow.Instance.SessionSettings.ActiveAssemblyList = manager.AssemblyLists[0];
+ }
+
+ private void ExecuteDelete(ManageAssemblyListsDialog dialog)
+ {
+ if (MessageBox.Show(dialog, Properties.Resources.ListDeleteConfirmation,
+"ILSpy", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No, MessageBoxOptions.None) != MessageBoxResult.Yes)
+ return;
+ manager.DeleteList(SelectedAssemblyList);
+ }
+
+ private bool CanExecuteDelete(ManageAssemblyListsDialog _)
+ {
+ return selectedAssemblyList != null;
+ }
+
private void CreateDefaultAssemblyLists()
{
- if (!manager.AssemblyLists.Contains(DotNet4List))
- {
+ if (!manager.AssemblyLists.Contains(DotNet4List)) {
AssemblyList dotnet4 = new AssemblyList(DotNet4List);
AddToList(dotnet4, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
AddToList(dotnet4, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
@@ -85,14 +141,12 @@ namespace ICSharpCode.ILSpy
AddToList(dotnet4, "PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
AddToList(dotnet4, "WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
- if (dotnet4.assemblies.Count > 0)
- {
+ if (dotnet4.assemblies.Count > 0) {
manager.CreateList(dotnet4);
}
}
- if (!manager.AssemblyLists.Contains(DotNet35List))
- {
+ if (!manager.AssemblyLists.Contains(DotNet35List)) {
AssemblyList dotnet35 = new AssemblyList(DotNet35List);
AddToList(dotnet35, "mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
AddToList(dotnet35, "System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
@@ -105,14 +159,12 @@ namespace ICSharpCode.ILSpy
AddToList(dotnet35, "PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
AddToList(dotnet35, "WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
- if (dotnet35.assemblies.Count > 0)
- {
+ if (dotnet35.assemblies.Count > 0) {
manager.CreateList(dotnet35);
}
}
- if (!manager.AssemblyLists.Contains(ASPDotNetMVC3List))
- {
+ if (!manager.AssemblyLists.Contains(ASPDotNetMVC3List)) {
AssemblyList mvc = new AssemblyList(ASPDotNetMVC3List);
AddToList(mvc, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
AddToList(mvc, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
@@ -139,8 +191,7 @@ namespace ICSharpCode.ILSpy
AddToList(mvc, "System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
AddToList(mvc, "Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
- if (mvc.assemblies.Count > 0)
- {
+ if (mvc.assemblies.Count > 0) {
manager.CreateList(mvc);
}
}
@@ -153,52 +204,5 @@ namespace ICSharpCode.ILSpy
if (file != null)
list.OpenAssembly(file);
}
-
- private void CreateButton_Click(object sender, RoutedEventArgs e)
- {
- CreateListDialog dlg = new CreateListDialog();
- dlg.Owner = this;
- dlg.Closing += (s, args) =>
- {
- if (dlg.DialogResult == true)
- {
- if (manager.AssemblyLists.Contains(dlg.NewListName))
- {
- args.Cancel = true;
- MessageBox.Show("A list with the same name was found.", null, MessageBoxButton.OK);
- }
- }
- };
- if (dlg.ShowDialog() == true)
- {
- manager.CreateList(new AssemblyList(dlg.NewListName));
- }
- }
-
- private void DeleteButton_Click(object sender, RoutedEventArgs e)
- {
- if (listView.SelectedItem == null)
- return;
- if (MessageBox.Show(this, "Are you sure that you want to delete the selected assembly list?",
-"ILSpy", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No, MessageBoxOptions.None) != MessageBoxResult.Yes)
- return;
- manager.DeleteList(listView.SelectedItem.ToString());
- }
-
- private void ResetButton_Click(object sender, RoutedEventArgs e)
- {
- if (MessageBox.Show(this, "Are you sure that you want to remove all assembly lists and recreate the default assembly lists?",
- "ILSpy", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No, MessageBoxOptions.None) != MessageBoxResult.Yes)
- return;
- manager.ClearAll();
- CreateDefaultAssemblyLists();
- }
-
- private void listView_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
- {
- if (e.ChangedButton == MouseButton.Left && listView.SelectedItem != null)
- this.DialogResult = true;
- }
-
}
}
diff --git a/ILSpy/ViewModels/ToolPaneModel.cs b/ILSpy/ViewModels/ToolPaneModel.cs
index 8843fdd1a..d25e44bc7 100644
--- a/ILSpy/ViewModels/ToolPaneModel.cs
+++ b/ILSpy/ViewModels/ToolPaneModel.cs
@@ -20,6 +20,10 @@ namespace ICSharpCode.ILSpy.ViewModels
{
public abstract class ToolPaneModel : PaneModel
{
-
+ public void Show()
+ {
+ this.IsActive = true;
+ this.IsVisible = true;
+ }
}
}
diff --git a/ILSpy/ViewModels/ViewModelBase.cs b/ILSpy/ViewModels/ViewModelBase.cs
new file mode 100644
index 000000000..e0aa4d6e7
--- /dev/null
+++ b/ILSpy/ViewModels/ViewModelBase.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ICSharpCode.ILSpy.ViewModels
+{
+ public abstract class ViewModelBase : INotifyPropertyChanged
+ {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+}
diff --git a/ILSpy/CreateListDialog.xaml b/ILSpy/Views/CreateListDialog.xaml
similarity index 100%
rename from ILSpy/CreateListDialog.xaml
rename to ILSpy/Views/CreateListDialog.xaml
diff --git a/ILSpy/CreateListDialog.xaml.cs b/ILSpy/Views/CreateListDialog.xaml.cs
similarity index 100%
rename from ILSpy/CreateListDialog.xaml.cs
rename to ILSpy/Views/CreateListDialog.xaml.cs
diff --git a/ILSpy/DebugSteps.xaml b/ILSpy/Views/DebugSteps.xaml
similarity index 100%
rename from ILSpy/DebugSteps.xaml
rename to ILSpy/Views/DebugSteps.xaml
diff --git a/ILSpy/DebugSteps.xaml.cs b/ILSpy/Views/DebugSteps.xaml.cs
similarity index 97%
rename from ILSpy/DebugSteps.xaml.cs
rename to ILSpy/Views/DebugSteps.xaml.cs
index cf879db29..1c7d9e5fb 100644
--- a/ILSpy/DebugSteps.xaml.cs
+++ b/ILSpy/Views/DebugSteps.xaml.cs
@@ -79,11 +79,6 @@ namespace ICSharpCode.ILSpy
#endif
}
- public static void Show()
- {
- DebugStepsPaneModel.Instance.IsVisible = true;
- }
-
void IPane.Closed()
{
#if DEBUG
diff --git a/ILSpy/Views/ManageAssemblyLIstsDialog.xaml.cs b/ILSpy/Views/ManageAssemblyLIstsDialog.xaml.cs
new file mode 100644
index 000000000..d57384d5b
--- /dev/null
+++ b/ILSpy/Views/ManageAssemblyLIstsDialog.xaml.cs
@@ -0,0 +1,35 @@
+// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System.Windows;
+using ICSharpCode.ILSpy.ViewModels;
+
+namespace ICSharpCode.ILSpy
+{
+ ///
+ /// Interaction logic for ManageAssemblyListsDialog.xaml
+ ///
+ public partial class ManageAssemblyListsDialog : Window
+ {
+ public ManageAssemblyListsDialog()
+ {
+ InitializeComponent();
+ DataContext = new ManageAssemblyListsViewModel();
+ }
+ }
+}
diff --git a/ILSpy/Views/ManageAssemblyListsDialog.xaml b/ILSpy/Views/ManageAssemblyListsDialog.xaml
new file mode 100644
index 000000000..d4b34b34b
--- /dev/null
+++ b/ILSpy/Views/ManageAssemblyListsDialog.xaml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ILSpy/NugetPackageBrowserDialog.xaml b/ILSpy/Views/NugetPackageBrowserDialog.xaml
similarity index 100%
rename from ILSpy/NugetPackageBrowserDialog.xaml
rename to ILSpy/Views/NugetPackageBrowserDialog.xaml
diff --git a/ILSpy/NugetPackageBrowserDialog.xaml.cs b/ILSpy/Views/NugetPackageBrowserDialog.xaml.cs
similarity index 100%
rename from ILSpy/NugetPackageBrowserDialog.xaml.cs
rename to ILSpy/Views/NugetPackageBrowserDialog.xaml.cs
diff --git a/ILSpy/OpenFromGacDialog.xaml b/ILSpy/Views/OpenFromGacDialog.xaml
similarity index 100%
rename from ILSpy/OpenFromGacDialog.xaml
rename to ILSpy/Views/OpenFromGacDialog.xaml
diff --git a/ILSpy/OpenFromGacDialog.xaml.cs b/ILSpy/Views/OpenFromGacDialog.xaml.cs
similarity index 100%
rename from ILSpy/OpenFromGacDialog.xaml.cs
rename to ILSpy/Views/OpenFromGacDialog.xaml.cs
diff --git a/README.md b/README.md
index 0df9ab7d4..5833728fc 100644
--- a/README.md
+++ b/README.md
@@ -48,8 +48,8 @@ How to build
------------
Windows:
-- Install Visual Studio (documented version: 16.3) with the following components:
- - Workload ".NET Desktop Development". This includes by default .NET Framework 4.8 SDK and the .NET Framework 4.7.2 targeting pack, as well as the [.NET Core 3.0 SDK](https://dotnet.microsoft.com/download/dotnet-core/3.0) (ILSpy.csproj targets .NET 4.7.2, and ILSpy.sln uses SDK-style projects).
+- Install Visual Studio (documented version: 16.4) with the following components:
+ - Workload ".NET Desktop Development". This includes by default .NET Framework 4.8 SDK and the .NET Framework 4.7.2 targeting pack, as well as the [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download/dotnet-core/3.1) (ILSpy.csproj targets .NET 4.7.2, and ILSpy.sln uses SDK-style projects).
- 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.
@@ -60,11 +60,13 @@ Windows:
- Run project "ILSpy" for the ILSpy UI
- Use the Visual Studio "Test Explorer" to see/run the tests
-**Note:** Visual Studio 16.3 and later include a version of the .NET Core SDK that is managed by the Visual Studio installer, once you update to 16.4 it may get upgraded to version 3.1. Please note that ILSpy is not compatible with the .NET Core 3.1 SDK and Visual Studio will refuse to load some projects in the solution. If this problem occurs, please manually install the .NET Core 3.0 SDK from [here](https://dotnet.microsoft.com/download/dotnet-core/3.0).
+**Note:** Visual Studio 16.3 and later include a version of the .NET Core SDK that is managed by the Visual Studio installer - once you update, it may get upgraded too.
+Please note that ILSpy is only compatible with the .NET Core 3.1 SDK and Visual Studio will refuse to load some projects in the solution (and unit tests will fail).
+If this problem occurs, please manually install the .NET Core 3.1 SDK from [here](https://dotnet.microsoft.com/download/dotnet-core/3.1).
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.0 SDK](https://dotnet.microsoft.com/download/dotnet-core/3.0) is installed.
+- Make sure [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download/dotnet-core/3.1) is installed.
- 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).
diff --git a/TestPlugin/CustomOptionPage.xaml.cs b/TestPlugin/CustomOptionPage.xaml.cs
index f069719ec..ad675a6f9 100644
--- a/TestPlugin/CustomOptionPage.xaml.cs
+++ b/TestPlugin/CustomOptionPage.xaml.cs
@@ -30,7 +30,12 @@ namespace TestPlugin
s.UselessOption2 = (double?)e.Attribute("useless2") ?? s.UselessOption2;
this.DataContext = s;
}
-
+
+ public void LoadDefaults()
+ {
+ this.DataContext = new Options();
+ }
+
public void Save(XElement root)
{
Options s = (Options)this.DataContext;
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 9ad3ed4c8..c3399d8bb 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -37,9 +37,13 @@ jobs:
- checkout: self
submodules: recursive
- - task: DotNetCoreInstaller@0
+
+ - task: UseDotNet@2
+ displayName: 'Install .NET Core 3.1'
inputs:
- version: '3.0.100'
+ packageType: sdk
+ version: '3.1.100'
+ installationPath: $(Agent.ToolsDirectory)/dotnet
- powershell: .\BuildTools\pipelines-install.ps1
displayName: Install
diff --git a/debugbuild.bat b/debugbuild.bat
index 90325ccab..fdce6272c 100644
--- a/debugbuild.bat
+++ b/debugbuild.bat
@@ -1,6 +1,6 @@
@setlocal enabledelayedexpansion
@set MSBUILD=
-@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2019"\*) do (
+@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2019"\*) do @(
@if exist "%%M\MSBuild\Current\Bin\MSBuild.exe" (
@set "MSBUILD=%%M\MSBuild\Current\Bin\MSBuild.exe"
)
@@ -9,9 +9,5 @@
@echo Could not find VS2019 MSBuild
@exit /b 1
)
-"%MSBUILD%" ILSpy.sln /p:Configuration=Debug "/p:Platform=Any CPU"
-@IF %ERRORLEVEL% NEQ 0 (
- @pause
- @exit /b 1
-)
-@exit /b 0
+@nuget restore ILSpy.sln || (pause && exit /b 1)
+"%MSBUILD%" ILSpy.sln /p:Configuration=Debug "/p:Platform=Any CPU" || (pause && exit /b 1)
diff --git a/global.json b/global.json
index 6e5ed6274..289df0eba 100644
--- a/global.json
+++ b/global.json
@@ -3,6 +3,6 @@
"MSBuild.Sdk.Extras": "2.0.54"
},
"sdk": {
- "version": "3.0.100"
+ "version": "3.1.100"
}
}
diff --git a/preparerelease.bat b/preparerelease.bat
deleted file mode 100644
index d2b141357..000000000
--- a/preparerelease.bat
+++ /dev/null
@@ -1,29 +0,0 @@
-@setlocal enabledelayedexpansion
-@set MSBUILD=
-@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2019"\*) do (
- @if exist "%%M\MSBuild\Current\Bin\MSBuild.exe" (
- @set "MSBUILD=%%M\MSBuild\Current\Bin\MSBuild.exe"
- )
-)
-@if "%MSBUILD%" == "" (
- @echo Could not find VS2019 MSBuild
- @exit /b 1
-)
-@del ICSharpCode.Decompiler\bin\Release\*.nupkg
-"%MSBUILD%" ILSpy.sln /p:Configuration=Release "/p:Platform=Any CPU"
-@IF %ERRORLEVEL% NEQ 0 (
- @pause
- @exit /b 1
-)
-@if not exist "%ProgramFiles%\7-zip\7z.exe" (
- @echo Could not find 7zip
- @exit /b 1
-)
-@del artifacts.zip
-@rmdir /Q /S artifacts
-@mkdir artifacts
-"%ProgramFiles%\7-zip\7z.exe" a artifacts\ILSpy_binaries.zip %cd%\ILSpy\bin\Release\net46\*.dll %cd%\ILSpy\bin\Release\net46\*.exe %cd%\ILSpy\bin\Release\net46\*.config
-@copy ILSpy.AddIn\bin\Release\net46\ILSpy.AddIn.vsix artifacts\
-@copy ICSharpCode.Decompiler\bin\Release\*.nupkg artifacts\
-"%ProgramFiles%\7-zip\7z.exe" a artifacts.zip %cd%\artifacts\*
-@exit /b 0
diff --git a/releasebuild.bat b/releasebuild.bat
index cd881dd67..d8cc7595b 100644
--- a/releasebuild.bat
+++ b/releasebuild.bat
@@ -1,6 +1,6 @@
@setlocal enabledelayedexpansion
@set MSBUILD=
-@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2019"\*) do (
+@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2019"\*) do @(
@if exist "%%M\MSBuild\Current\Bin\MSBuild.exe" (
@set "MSBUILD=%%M\MSBuild\Current\Bin\MSBuild.exe"
)
@@ -9,9 +9,5 @@
@echo Could not find VS2019 MSBuild
@exit /b 1
)
-"%MSBUILD%" ILSpy.sln /p:Configuration=Release "/p:Platform=Any CPU"
-@IF %ERRORLEVEL% NEQ 0 (
- @pause
- @exit /b 1
-)
-@exit /b 0
+@nuget restore ILSpy.sln || (pause && exit /b 1)
+"%MSBUILD%" ILSpy.sln /p:Configuration=Release "/p:Platform=Any CPU" || (pause && exit /b 1)