Browse Source

Merge branch 'master' of https://github.com/icsharpcode/ILSpy into avalondock

pull/1801/head
Siegfried Pammer 6 years ago
parent
commit
f282a48d70
  1. 5
      ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj
  2. BIN
      ICSharpCode.Decompiler.Console/ILSpyCmdNuGetPackageIcon.png
  3. 80
      ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs
  4. 22
      ICSharpCode.Decompiler.Console/README.md
  5. 15
      ICSharpCode.Decompiler.PowerShell/GetDecompilerCmdlet.cs
  6. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  7. 32
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/OverloadResolution.cs
  8. 10
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs
  9. 16
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstructorInitializers.cs
  10. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes2.cs
  11. 43
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs
  12. 7
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs
  13. 15
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs
  14. 53
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs
  15. 80
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs
  16. 43
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.Expected.cs
  17. 23
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.cs
  18. 156
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.il
  19. 128
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.opt.il
  20. 126
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.opt.roslyn.il
  21. 145
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.roslyn.il
  22. 14
      ICSharpCode.Decompiler.Tests/UglyTestRunner.cs
  23. 2
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  24. 4
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  25. 4
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  26. 4
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs
  27. 6
      ICSharpCode.Decompiler/CSharp/OutputVisitor/ITokenWriter.cs
  28. 122
      ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs
  29. 40
      ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs
  30. 2
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs
  31. 9
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  32. 28
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  33. 71
      ICSharpCode.Decompiler/CSharp/Transforms/ConvertConstructorCallIntoInitializer.cs
  34. 80
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs
  35. 3
      ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
  36. BIN
      ICSharpCode.Decompiler/DecompilerNuGetPackageIcon.png
  37. 240
      ICSharpCode.Decompiler/Documentation/XmlDocumentationElement.cs
  38. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  39. 13
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template
  40. 2
      ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs
  41. 2
      ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs
  42. 5
      ICSharpCode.Decompiler/IL/Instructions/StringToInt.cs
  43. 3
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  44. 8
      ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs
  45. 2
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs
  46. 2
      ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs
  47. 63
      ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs
  48. 4
      ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs
  49. 19
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
  50. 2
      ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs
  51. 2
      ICSharpCode.Decompiler/Metadata/LightJson/JsonObject.cs
  52. 9
      ICSharpCode.Decompiler/NRExtensions.cs
  53. 6
      ILSpy.AddIn/SyntaxNodeExtensions.cs
  54. 40
      ILSpy/ExtensionMethods.cs
  55. 5
      ILSpy/ILSpy.csproj
  56. 2
      ILSpy/ISmartTextOutput.cs
  57. 74
      ILSpy/Languages/CSharpHighlightingTokenWriter.cs
  58. 14
      ILSpy/Languages/CSharpLanguage.cs
  59. 10
      ILSpy/Languages/Language.cs
  60. 3
      ILSpy/LoadedAssembly.cs
  61. 4
      ILSpy/MainWindow.xaml.cs
  62. 1
      ILSpy/Options/DisplaySettingsPanel.xaml.cs
  63. 247
      ILSpy/TextView/DecompilerTextView.cs
  64. 2
      ILSpy/TextView/DecompilerTextView.xaml
  65. 551
      ILSpy/TextView/DocumentationUIBuilder.cs
  66. 137
      ILSpy/TextView/XmlDocRenderer.cs
  67. 6
      README.md
  68. 8
      SharpTreeView/EditTextBox.cs

5
ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj

@ -12,7 +12,7 @@ @@ -12,7 +12,7 @@
<Copyright>Copyright 2011-2019 AlphaSierraPapa</Copyright>
<PackageProjectUrl>https://github.com/icsharpcode/ILSpy/</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIconUrl>https://ilspy.net/images/icon32.png</PackageIconUrl>
<PackageIcon>ILSpyCmdNuGetPackageIcon.png</PackageIcon>
<RepositoryUrl>https://github.com/icsharpcode/ILSpy/</RepositoryUrl>
<Company />
<AssemblyVersion>5.0.0.0</AssemblyVersion>
@ -26,6 +26,9 @@ @@ -26,6 +26,9 @@
<WarningsAsErrors>NU1605</WarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<None Include="ILSpyCmdNuGetPackageIcon.png" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
<ProjectReference Include="..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj" />

BIN
ICSharpCode.Decompiler.Console/ILSpyCmdNuGetPackageIcon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

80
ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs

@ -22,7 +22,7 @@ Remarks: @@ -22,7 +22,7 @@ Remarks:
-o is valid with every option and required when using -p.
")]
[HelpOption("-h|--help")]
[ProjectOptionRequiresOutputDirectoryValidationAttribute]
[ProjectOptionRequiresOutputDirectoryValidation]
class ILSpyCmdProgram
{
public static int Main(string[] args) => CommandLineApplication.Execute<ILSpyCmdProgram>(args);
@ -54,18 +54,27 @@ Remarks: @@ -54,18 +54,27 @@ Remarks:
[Option("-v|--version", "Show version of ICSharpCode.Decompiler used.", CommandOptionType.NoValue)]
public bool ShowVersion { get; }
[Option("-lv|--languageversion", "C# Language version: CSharp1, CSharp2, CSharp3, CSharp4, CSharp5, CSharp6, CSharp7_0, CSharp7_1, CSharp7_2, CSharp7_3, CSharp8_0 or Latest", CommandOptionType.SingleValue)]
public LanguageVersion LanguageVersion { get; } = LanguageVersion.Latest;
[DirectoryExists]
[Option("-r|--referencepath <path>", "Path to a directory containing dependencies of the assembly that is being decompiled.", CommandOptionType.MultipleValue)]
public string[] ReferencePaths { get; } = new string[0];
[Option("--no-dead-code", "Remove dead code.", CommandOptionType.NoValue)]
public bool RemoveDeadCode { get; }
[Option("--no-dead-stores", "Remove dead stores.", CommandOptionType.NoValue)]
public bool RemoveDeadStores { get; }
private int OnExecute(CommandLineApplication app)
{
TextWriter output = System.Console.Out;
bool outputDirectorySpecified = !String.IsNullOrEmpty(OutputDirectory);
bool outputDirectorySpecified = !string.IsNullOrEmpty(OutputDirectory);
try {
if (CreateCompilableProjectFlag) {
DecompileAsProject(InputAssemblyName, OutputDirectory, ReferencePaths);
return DecompileAsProject(InputAssemblyName, OutputDirectory);
} else if (EntityTypes.Any()) {
var values = EntityTypes.SelectMany(v => v.Split(',', ';')).ToArray();
HashSet<TypeKind> kinds = TypesParser.ParseSelection(values);
@ -74,14 +83,14 @@ Remarks: @@ -74,14 +83,14 @@ Remarks:
output = File.CreateText(Path.Combine(OutputDirectory, outputName) + ".list.txt");
}
ListContent(InputAssemblyName, output, kinds, ReferencePaths);
return ListContent(InputAssemblyName, output, kinds);
} else if (ShowILCodeFlag) {
if (outputDirectorySpecified) {
string outputName = Path.GetFileNameWithoutExtension(InputAssemblyName);
output = File.CreateText(Path.Combine(OutputDirectory, outputName) + ".il");
}
ShowIL(InputAssemblyName, output, ReferencePaths);
return ShowIL(InputAssemblyName, output);
} else if (CreateDebugInfoFlag) {
string pdbFileName = null;
if (outputDirectorySpecified) {
@ -91,7 +100,7 @@ Remarks: @@ -91,7 +100,7 @@ Remarks:
pdbFileName = Path.ChangeExtension(InputAssemblyName, ".pdb");
}
return GeneratePdbForAssembly(InputAssemblyName, pdbFileName, ReferencePaths, app);
return GeneratePdbForAssembly(InputAssemblyName, pdbFileName, app);
} else if (ShowVersion) {
string vInfo = "ilspycmd: " + typeof(ILSpyCmdProgram).Assembly.GetName().Version.ToString() +
Environment.NewLine
@ -102,10 +111,10 @@ Remarks: @@ -102,10 +111,10 @@ Remarks:
if (outputDirectorySpecified) {
string outputName = Path.GetFileNameWithoutExtension(InputAssemblyName);
output = File.CreateText(Path.Combine(OutputDirectory,
(String.IsNullOrEmpty(TypeName) ? outputName : TypeName) + ".decompiled.cs"));
(string.IsNullOrEmpty(TypeName) ? outputName : TypeName) + ".decompiled.cs"));
}
Decompile(InputAssemblyName, output, ReferencePaths, TypeName);
return Decompile(InputAssemblyName, output, TypeName);
}
} catch (Exception ex) {
app.Error.WriteLine(ex.ToString());
@ -117,56 +126,62 @@ Remarks: @@ -117,56 +126,62 @@ Remarks:
return 0;
}
static CSharpDecompiler GetDecompiler(string assemblyFileName, string[] referencePaths)
DecompilerSettings GetSettings()
{
return new DecompilerSettings(LanguageVersion) {
ThrowOnAssemblyResolveErrors = false,
RemoveDeadCode = RemoveDeadCode,
RemoveDeadStores = RemoveDeadStores
};
}
CSharpDecompiler GetDecompiler(string assemblyFileName)
{
var module = new PEFile(assemblyFileName);
var resolver = new UniversalAssemblyResolver(assemblyFileName, false, module.Reader.DetectTargetFrameworkId());
foreach (var path in referencePaths) {
foreach (var path in ReferencePaths) {
resolver.AddSearchDirectory(path);
}
return new CSharpDecompiler(assemblyFileName, resolver, new DecompilerSettings());
return new CSharpDecompiler(assemblyFileName, resolver, GetSettings());
}
static void ListContent(string assemblyFileName, TextWriter output, ISet<TypeKind> kinds, string[] referencePaths)
int ListContent(string assemblyFileName, TextWriter output, ISet<TypeKind> kinds)
{
CSharpDecompiler decompiler = GetDecompiler(assemblyFileName, referencePaths);
CSharpDecompiler decompiler = GetDecompiler(assemblyFileName);
foreach (var type in decompiler.TypeSystem.MainModule.TypeDefinitions) {
if (!kinds.Contains(type.Kind))
continue;
output.WriteLine($"{type.Kind} {type.FullName}");
}
return 0;
}
static void ShowIL(string assemblyFileName, TextWriter output, string[] referencePaths)
int ShowIL(string assemblyFileName, TextWriter output)
{
CSharpDecompiler decompiler = GetDecompiler(assemblyFileName, referencePaths);
ITextOutput textOutput = new PlainTextOutput();
ReflectionDisassembler disassembler = new ReflectionDisassembler(textOutput, CancellationToken.None);
disassembler.DisassembleNamespace(decompiler.TypeSystem.MainModule.RootNamespace.Name,
decompiler.TypeSystem.MainModule.PEFile,
decompiler.TypeSystem.MainModule.TypeDefinitions.Select(x => (TypeDefinitionHandle)x.MetadataToken));
output.WriteLine($"// IL code: {decompiler.TypeSystem.MainModule.AssemblyName}");
output.WriteLine(textOutput.ToString());
var module = new PEFile(assemblyFileName);
output.WriteLine($"// IL code: {module.Name}");
var disassembler = new ReflectionDisassembler(new PlainTextOutput(output), CancellationToken.None);
disassembler.WriteModuleContents(module);
return 0;
}
static void DecompileAsProject(string assemblyFileName, string outputDirectory, string[] referencePaths)
int DecompileAsProject(string assemblyFileName, string outputDirectory)
{
var decompiler = new WholeProjectDecompiler();
var decompiler = new WholeProjectDecompiler() { Settings = GetSettings() };
var module = new PEFile(assemblyFileName);
var resolver = new UniversalAssemblyResolver(assemblyFileName, false, module.Reader.DetectTargetFrameworkId());
foreach (var path in referencePaths) {
foreach (var path in ReferencePaths) {
resolver.AddSearchDirectory(path);
}
decompiler.AssemblyResolver = resolver;
decompiler.DecompileProject(module, outputDirectory);
return 0;
}
static void Decompile(string assemblyFileName, TextWriter output, string[] referencePaths, string typeName = null)
int Decompile(string assemblyFileName, TextWriter output, string typeName = null)
{
CSharpDecompiler decompiler = GetDecompiler(assemblyFileName, referencePaths);
CSharpDecompiler decompiler = GetDecompiler(assemblyFileName);
if (typeName == null) {
output.Write(decompiler.DecompileWholeModuleAsString());
@ -174,9 +189,10 @@ Remarks: @@ -174,9 +189,10 @@ Remarks:
var name = new FullTypeName(typeName);
output.Write(decompiler.DecompileTypeAsString(name));
}
return 0;
}
static int GeneratePdbForAssembly(string assemblyFileName, string pdbFileName, string[] referencePaths, CommandLineApplication app)
int GeneratePdbForAssembly(string assemblyFileName, string pdbFileName, CommandLineApplication app)
{
var module = new PEFile(assemblyFileName,
new FileStream(assemblyFileName, FileMode.Open, FileAccess.Read),
@ -189,8 +205,8 @@ Remarks: @@ -189,8 +205,8 @@ Remarks:
}
using (FileStream stream = new FileStream(pdbFileName, FileMode.OpenOrCreate, FileAccess.Write)) {
var decompiler = GetDecompiler(assemblyFileName, referencePaths);
PortablePdbWriter.WritePdb(module, decompiler, new DecompilerSettings() { ThrowOnAssemblyResolveErrors = false }, stream);
var decompiler = GetDecompiler(assemblyFileName);
PortablePdbWriter.WritePdb(module, decompiler, GetSettings(), stream);
}
return 0;

22
ICSharpCode.Decompiler.Console/README.md

@ -14,17 +14,21 @@ dotnet tool for decompiling .NET assemblies and generating portable PDBs @@ -14,17 +14,21 @@ dotnet tool for decompiling .NET assemblies and generating portable PDBs
Usage: ilspycmd [arguments] [options]
Arguments:
Assembly file name The assembly that is being decompiled. This argument is mandatory.
Assembly file name The assembly that is being decompiled. This argument is mandatory.
Options:
-h|--help Show help information
-o|--outputdir <directory> The output directory, if omitted decompiler output is written to standard out.
-p|--project Decompile assembly as compilable project. This requires the output directory option.
-t|--type <type-name> The fully qualified name of the type to decompile.
-il|--ilcode Show IL code.
-d|--debuginfo Generate PDB.
-l|--list <entity-type(s)> Lists all entities of the specified type(s). Valid types: c(lass), i(interface), s(truct), d(elegate), e(num)
-v|--version Show version of ICSharpCode.Decompiler used.
-h|--help Show help information
-o|--outputdir <directory> The output directory, if omitted decompiler output is written to standard out.
-p|--project Decompile assembly as compilable project. This requires the output directory option.
-t|--type <type-name> The fully qualified name of the type to decompile.
-il|--ilcode Show IL code.
-d|--debuginfo Generate PDB.
-l|--list <entity-type(s)> Lists all entities of the specified type(s). Valid types: c(lass), i(interface), s(truct), d(elegate), e(num)
-v|--version Show version of ICSharpCode.Decompiler used.
-lv|--languageversion <version> C# Language version: CSharp1, CSharp2, CSharp3, CSharp4, CSharp5, CSharp6, CSharp7_0, CSharp7_1, CSharp7_2, CSharp7_3, CSharp8_0 or Latest
-r|--referencepath <path> Path to a directory containing dependencies of the assembly that is being decompiled.
--no-dead-code Remove dead code.
--no-dead-stores Remove dead stores.
Remarks:
-o is valid with every option and required when using -p.

15
ICSharpCode.Decompiler.PowerShell/GetDecompilerCmdlet.cs

@ -15,13 +15,24 @@ namespace ICSharpCode.Decompiler.PowerShell @@ -15,13 +15,24 @@ namespace ICSharpCode.Decompiler.PowerShell
[ValidateNotNullOrEmpty]
public string LiteralPath { get; set; }
[Parameter(HelpMessage = "C# Language version to be used by the decompiler")]
public LanguageVersion LanguageVersion { get; set; } = LanguageVersion.Latest;
[Parameter(HelpMessage = "Remove dead stores")]
public bool RemoveDeadStores { get; set; }
[Parameter(HelpMessage = "Remove dead code")]
public bool RemoveDeadCode { get; set; }
protected override void ProcessRecord()
{
string path = GetUnresolvedProviderPathFromPSPath(LiteralPath);
try {
var decompiler = new CSharpDecompiler(path, new DecompilerSettings() {
ThrowOnAssemblyResolveErrors = false
var decompiler = new CSharpDecompiler(path, new DecompilerSettings(LanguageVersion) {
ThrowOnAssemblyResolveErrors = false,
RemoveDeadCode = RemoveDeadCode,
RemoveDeadStores = RemoveDeadStores
});
WriteObject(decompiler);

2
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -83,6 +83,8 @@ @@ -83,6 +83,8 @@
<Compile Include="TestCases\ILPretty\ConstantBlobs.cs" />
<Compile Include="TestCases\Pretty\OutVariables.cs" />
<Compile Include="TestCases\Pretty\CustomTaskType.cs" />
<None Include="TestCases\Ugly\NoForEachStatement.Expected.cs" />
<Compile Include="TestCases\Ugly\NoForEachStatement.cs" />
<None Include="TestCases\Ugly\NoExtensionMethods.Expected.cs" />
<Compile Include="TestCases\Ugly\NoExtensionMethods.cs" />
<None Include="TestCases\VBPretty\VBCompoundAssign.cs" />

32
ICSharpCode.Decompiler.Tests/TestCases/Correctness/OverloadResolution.cs

@ -33,6 +33,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -33,6 +33,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
ConstructorTest();
TestIndexer();
Issue1281();
Issue1747();
}
#region ConstructorTest
@ -203,6 +204,37 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -203,6 +204,37 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
}
#endregion
#region NullableValueTypes
private static void Issue1747()
{
Console.WriteLine("Issue1747:");
M1747(null);
M1747(true);
M1747(false);
M1747((bool?)true);
M1747((bool?)false);
Console.WriteLine("Issue1747, non-constant:");
bool b = Get<bool>();
M1747(b);
M1747((bool?)b);
}
private static void M1747(bool b)
{
Console.WriteLine("bool=" + b);
}
private static void M1747(bool? b)
{
Console.WriteLine("bool?=" + b);
}
static T Get<T>()
{
return default(T);
}
#endregion
#region IndexerTests
static void TestIndexer()
{

10
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs

@ -4721,6 +4721,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -4721,6 +4721,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return (*GetPointer())++;
}
public float PostIncrementFloat(float f)
{
return f++;
}
public double PostIncrementDouble(double d)
{
return d++;
}
public void Issue1552Pre(CustomStruct a, CustomStruct b)
{
CustomStruct customStruct = a + b;

16
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstructorInitializers.cs

@ -20,6 +20,22 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -20,6 +20,22 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class ConstructorInitializers
{
public struct Issue1743
{
public int Leet;
public Issue1743(int dummy)
: this(dummy, dummy)
{
Leet += dummy;
}
public Issue1743(int dummy1, int dummy2)
{
Leet = dummy1 + dummy2;
}
}
public struct SimpleStruct
{
public int Field1;

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes2.cs

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
using System;
namespace CustomAttributes2
{
public static class CustomAtributes
public static class CustomAttributes
{
[Flags]
public enum EnumWithFlag

43
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs

@ -151,6 +151,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -151,6 +151,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
void M3();
}
public class BaseClass : IM3
{
protected virtual void M1()
@ -163,6 +164,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -163,6 +164,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
}
}
public class SubClass : BaseClass
{
protected override void M2()
@ -193,6 +195,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -193,6 +195,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Noop("M3", M3);
#endif
}
public void Test2()
{
Noop("M3.new", new BaseClass().M3);
@ -204,6 +207,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -204,6 +207,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
public class GenericTest<TNonCaptured, TCaptured>
{
public Func<TCaptured> GetFunc(Func<TNonCaptured, TCaptured> f)
{
TCaptured captured = f(default(TNonCaptured));
return delegate {
Console.WriteLine(captured.GetType().FullName);
return captured;
};
}
public Func<TNonCaptured, TNonCapturedMP, TCaptured> GetFunc<TNonCapturedMP>(Func<TCaptured> f)
{
TCaptured captured = f();
return delegate(TNonCaptured a, TNonCapturedMP d) {
Console.WriteLine(a.GetHashCode());
Console.WriteLine(captured.GetType().FullName);
return captured;
};
}
}
public static Func<string, string, bool> test0 = (string a, string b) => string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b);
public static Func<string, string, bool> test1 = (string a, string b) => string.IsNullOrEmpty(a) || !string.IsNullOrEmpty(b);
public static Func<string, string, bool> test2 = (string a, string b) => !string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b);
@ -342,5 +367,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -342,5 +367,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
return (int b) => (int c) => (int d) => a + b + c + d;
}
public static Func<TCaptured> CapturedTypeParameter1<TNonCaptured, TCaptured>(TNonCaptured a, Func<TNonCaptured, TCaptured> f)
{
TCaptured captured = f(a);
return delegate {
Console.WriteLine(captured.GetType().FullName);
return captured;
};
}
public static Func<TCaptured> CapturedTypeParameter2<TNonCaptured, TCaptured>(TNonCaptured a, Func<TNonCaptured, List<TCaptured>> f)
{
List<TCaptured> captured = f(a);
return delegate {
Console.WriteLine(captured.GetType().FullName);
return captured.FirstOrDefault();
};
}
}
}

7
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs

@ -24,10 +24,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -24,10 +24,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
private class GenericClass<T>
{
private readonly T issue1760;
public void M(out GenericClass<T> self)
{
self = this;
}
public void Issue1760()
{
Console.WriteLine(", " + issue1760);
}
}
public class BaseClass

15
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs

@ -17,6 +17,8 @@ @@ -17,6 +17,8 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections;
using System.Collections.Generic;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
@ -182,7 +184,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -182,7 +184,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
Use(GetMyClass()?.Text ?? "Hello");
}
public void CallOnValueTypeField()
{
Use(GetMyClass()?.IntVal.ToString());
@ -258,6 +260,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -258,6 +260,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return t?.Int();
}
public int? Issue1709(object obj)
{
return (obj as ICollection)?.Count + (obj as ICollection<int>)?.Count;
}
private static void Issue1689(List<byte[]> setsOfNumbers)
{
Console.WriteLine(setsOfNumbers?[0]?[1].ToString() == "2");
Console.WriteLine(setsOfNumbers?[1]?[1].ToString() == null);
}
private static dynamic DynamicNullProp(dynamic a)
{
return a?.b.c(1)?.d[10];

53
ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs

@ -128,9 +128,45 @@ namespace ICSharpCode.Decompiler.Tests.Pretty @@ -128,9 +128,45 @@ namespace ICSharpCode.Decompiler.Tests.Pretty
}
}
private class i
{
public static void Test()
{
}
}
private class value
{
public static int item;
public static void Test()
{
}
}
private int fieldConflict;
private int innerConflict;
private static int PropertyValueParameterConflictsWithTypeName {
get {
return value.item;
}
set {
QualifierTests.value.item = value;
}
}
private int this[string[] Array] {
get {
System.Array.Sort(Array);
return 0;
}
set {
System.Array.Sort(Array);
QualifierTests.value.item = value;
}
}
private void NoParameters()
{
Delegate(Parameter);
@ -209,6 +245,23 @@ namespace ICSharpCode.Decompiler.Tests.Pretty @@ -209,6 +245,23 @@ namespace ICSharpCode.Decompiler.Tests.Pretty
{
}
private void ParameterConflictsWithTypeName(string[] Array)
{
System.Array.Sort(Array);
}
private void LocalConflictsWithTypeName()
{
for (int i = 0; i < 10; i++) {
QualifierTests.i.Test();
}
}
public QualifierTests(string[] Array)
{
System.Array.Sort(Array);
}
}
internal static class ZExt

80
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs

@ -47,6 +47,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -47,6 +47,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Null
}
private static char ch1767;
#if !ROSLYN
public static State SwitchOverNullableBool(bool? value)
{
@ -465,7 +467,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -465,7 +467,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
Console.WriteLine("End of method");
}
public static void SwitchWithGotoComplex(string s)
{
Console.WriteLine("SwitchWithGotoComplex: " + s);
@ -640,7 +642,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -640,7 +642,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine();
}
}
// while condition, return and break cases
public static void SwitchWithContinue2(int i, bool b)
{
@ -681,7 +683,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -681,7 +683,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
i++;
}
}
// for loop version
public static void SwitchWithContinue3(bool b)
{
@ -721,7 +723,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -721,7 +723,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine("loop-tail");
}
}
// foreach version
public static void SwitchWithContinue4(bool b)
{
@ -843,7 +845,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -843,7 +845,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
continue;
}
Console.WriteLine("loop-tail");
} while (++i < 10);
} while (++i < 10);
}
// double break from switch to loop exit requires additional pattern matching in HighLevelLoopTransform
@ -918,7 +920,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -918,7 +920,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
// These decompile poorly into switch statements and should be left as is
#region Overagressive Switch Use
#region Overagressive Switch Use
#if ROSLYN || OPT
public static void SingleIf1(int i, bool a)
@ -929,7 +931,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -929,7 +931,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine(2);
}
#endif
public static void SingleIf2(int i, bool a, bool b)
{
if (i == 1 || (i == 2 && a) || (i == 3 && b)) {
@ -937,7 +939,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -937,7 +939,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
Console.WriteLine(2);
}
public static void SingleIf3(int i, bool a, bool b)
{
if (a || i == 1 || (i == 2 && b)) {
@ -945,7 +947,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -945,7 +947,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
Console.WriteLine(2);
}
public static void SingleIf4(int i, bool a)
{
if (i == 1 || i == 2 || (i != 3 && a) || i != 4) {
@ -964,7 +966,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -964,7 +966,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
Console.WriteLine();
}
public static void IfChainWithCondition(int i)
{
if (i == 0) {
@ -985,7 +987,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -985,7 +987,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine();
}
public static bool SwitchlikeIf(int i, int j)
{
if (i != 0 && j != 0) {
@ -1023,7 +1025,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -1023,7 +1025,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
return false;
}
return true;
}
@ -1052,7 +1054,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -1052,7 +1054,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
Console.WriteLine("end");
}
public static bool Loop8(char c, bool b, Func<char> getChar)
{
if (b) {
@ -1071,8 +1073,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -1071,8 +1073,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
c = getChar();
} while (c != -1 && c != '\n' && c != '\u2028' && c != '\u2029');
}
#endregion
#endregion
// Ensure correctness of SwitchDetection.UseCSharpSwitch control flow heuristics
public static void SwitchWithBreakCase(int i, bool b)
{
@ -1108,7 +1110,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -1108,7 +1110,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
Console.WriteLine();
}
public static int SwitchWithReturnAndBreak2(int i, bool b)
{
switch (i) {
@ -1130,7 +1132,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -1130,7 +1132,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine();
return 0;
}
public static void SwitchWithReturnAndBreak3(int i)
{
switch (i) {
@ -1193,6 +1195,26 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -1193,6 +1195,26 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
public static void Issue1745(string aaa)
{
switch (aaa) {
case "a":
case "b":
case "c":
case "d":
case "e":
case "f":
Console.WriteLine(aaa);
break;
case null:
Console.WriteLine("<null>");
break;
case "":
Console.WriteLine("<empty>");
break;
}
}
public static bool DoNotRemoveAssignmentBeforeSwitch(string x, out ConsoleKey key)
{
key = (ConsoleKey)0;
@ -1209,5 +1231,29 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -1209,5 +1231,29 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
return key != (ConsoleKey)0;
}
public static void Issue1767(string s)
{
switch (s) {
case "a":
ch1767 = s[0];
break;
case "b":
ch1767 = s[0];
break;
case "c":
ch1767 = s[0];
break;
case "d":
ch1767 = s[0];
break;
case "e":
ch1767 = s[0];
break;
case "f":
ch1767 = s[0];
break;
}
}
}
}

43
ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.Expected.cs

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly
{
internal class NoForEachStatement
{
public static void SimpleNonGenericForeach(IEnumerable enumerable)
{
IEnumerator enumerator = enumerable.GetEnumerator();
try {
while (enumerator.MoveNext()) {
#if ROSLYN && OPT
Console.WriteLine(enumerator.Current);
#else
object current = enumerator.Current;
Console.WriteLine(current);
#endif
}
} finally {
IDisposable disposable = enumerator as IDisposable;
if (disposable != null) {
disposable.Dispose();
}
}
}
public static void SimpleForeachOverInts(IEnumerable<int> enumerable)
{
using (IEnumerator<int> enumerator = enumerable.GetEnumerator()) {
while (enumerator.MoveNext()) {
#if ROSLYN && OPT
Console.WriteLine(enumerator.Current);
#else
int current = enumerator.Current;
Console.WriteLine(current);
#endif
}
}
}
}
}

23
ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.cs

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly
{
internal class NoForEachStatement
{
public static void SimpleNonGenericForeach(IEnumerable enumerable)
{
foreach (object item in enumerable) {
Console.WriteLine(item);
}
}
public static void SimpleForeachOverInts(IEnumerable<int> enumerable)
{
foreach (int item in enumerable) {
Console.WriteLine(item);
}
}
}
}

156
ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.il

@ -0,0 +1,156 @@ @@ -0,0 +1,156 @@
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly rerff2f0
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
.permissionset reqmin
= {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}}
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module rerff2f0.dll
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// =============== CLASS MEMBERS DECLARATION ===================
.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement
extends [mscorlib]System.Object
{
.method public hidebysig static void SimpleNonGenericForeach(class [mscorlib]System.Collections.IEnumerable enumerable) cil managed
{
// Code size 64 (0x40)
.maxstack 2
.locals init (object V_0,
class [mscorlib]System.Collections.IEnumerator V_1,
bool V_2,
class [mscorlib]System.IDisposable V_3)
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.0
IL_0003: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator()
IL_0008: stloc.1
.try
{
IL_0009: br.s IL_001b
IL_000b: ldloc.1
IL_000c: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
IL_0011: stloc.0
IL_0012: nop
IL_0013: ldloc.0
IL_0014: call void [mscorlib]System.Console::WriteLine(object)
IL_0019: nop
IL_001a: nop
IL_001b: ldloc.1
IL_001c: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0021: stloc.2
IL_0022: ldloc.2
IL_0023: brtrue.s IL_000b
IL_0025: leave.s IL_003e
} // end .try
finally
{
IL_0027: ldloc.1
IL_0028: isinst [mscorlib]System.IDisposable
IL_002d: stloc.3
IL_002e: ldloc.3
IL_002f: ldnull
IL_0030: ceq
IL_0032: stloc.2
IL_0033: ldloc.2
IL_0034: brtrue.s IL_003d
IL_0036: ldloc.3
IL_0037: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_003c: nop
IL_003d: endfinally
} // end handler
IL_003e: nop
IL_003f: ret
} // end of method NoForEachStatement::SimpleNonGenericForeach
.method public hidebysig static void SimpleForeachOverInts(class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> enumerable) cil managed
{
// Code size 57 (0x39)
.maxstack 2
.locals init (int32 V_0,
class [mscorlib]System.Collections.Generic.IEnumerator`1<int32> V_1,
bool V_2)
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.0
IL_0003: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
IL_0008: stloc.1
.try
{
IL_0009: br.s IL_001b
IL_000b: ldloc.1
IL_000c: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()
IL_0011: stloc.0
IL_0012: nop
IL_0013: ldloc.0
IL_0014: call void [mscorlib]System.Console::WriteLine(int32)
IL_0019: nop
IL_001a: nop
IL_001b: ldloc.1
IL_001c: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0021: stloc.2
IL_0022: ldloc.2
IL_0023: brtrue.s IL_000b
IL_0025: leave.s IL_0037
} // end .try
finally
{
IL_0027: ldloc.1
IL_0028: ldnull
IL_0029: ceq
IL_002b: stloc.2
IL_002c: ldloc.2
IL_002d: brtrue.s IL_0036
IL_002f: ldloc.1
IL_0030: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0035: nop
IL_0036: endfinally
} // end handler
IL_0037: nop
IL_0038: ret
} // end of method NoForEachStatement::SimpleForeachOverInts
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method NoForEachStatement::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************

128
ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.opt.il

@ -0,0 +1,128 @@ @@ -0,0 +1,128 @@
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly prqfqkbt
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
.permissionset reqmin
= {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}}
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module prqfqkbt.dll
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// =============== CLASS MEMBERS DECLARATION ===================
.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement
extends [mscorlib]System.Object
{
.method public hidebysig static void SimpleNonGenericForeach(class [mscorlib]System.Collections.IEnumerable enumerable) cil managed
{
// Code size 50 (0x32)
.maxstack 1
.locals init (object V_0,
class [mscorlib]System.Collections.IEnumerator V_1,
class [mscorlib]System.IDisposable V_2)
IL_0000: ldarg.0
IL_0001: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator()
IL_0006: stloc.1
.try
{
IL_0007: br.s IL_0016
IL_0009: ldloc.1
IL_000a: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
IL_000f: stloc.0
IL_0010: ldloc.0
IL_0011: call void [mscorlib]System.Console::WriteLine(object)
IL_0016: ldloc.1
IL_0017: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_001c: brtrue.s IL_0009
IL_001e: leave.s IL_0031
} // end .try
finally
{
IL_0020: ldloc.1
IL_0021: isinst [mscorlib]System.IDisposable
IL_0026: stloc.2
IL_0027: ldloc.2
IL_0028: brfalse.s IL_0030
IL_002a: ldloc.2
IL_002b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0030: endfinally
} // end handler
IL_0031: ret
} // end of method NoForEachStatement::SimpleNonGenericForeach
.method public hidebysig static void SimpleForeachOverInts(class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> enumerable) cil managed
{
// Code size 43 (0x2b)
.maxstack 1
.locals init (int32 V_0,
class [mscorlib]System.Collections.Generic.IEnumerator`1<int32> V_1)
IL_0000: ldarg.0
IL_0001: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
IL_0006: stloc.1
.try
{
IL_0007: br.s IL_0016
IL_0009: ldloc.1
IL_000a: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()
IL_000f: stloc.0
IL_0010: ldloc.0
IL_0011: call void [mscorlib]System.Console::WriteLine(int32)
IL_0016: ldloc.1
IL_0017: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_001c: brtrue.s IL_0009
IL_001e: leave.s IL_002a
} // end .try
finally
{
IL_0020: ldloc.1
IL_0021: brfalse.s IL_0029
IL_0023: ldloc.1
IL_0024: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0029: endfinally
} // end handler
IL_002a: ret
} // end of method NoForEachStatement::SimpleForeachOverInts
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method NoForEachStatement::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************

126
ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.opt.roslyn.il

@ -0,0 +1,126 @@ @@ -0,0 +1,126 @@
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly NoForEachStatement
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
// --- The following custom attribute is added automatically, do not uncomment -------
// .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 )
.permissionset reqmin
= {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}}
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module NoForEachStatement.dll
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// =============== CLASS MEMBERS DECLARATION ===================
.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement
extends [mscorlib]System.Object
{
.method public hidebysig static void SimpleNonGenericForeach(class [mscorlib]System.Collections.IEnumerable enumerable) cil managed
{
// Code size 48 (0x30)
.maxstack 1
.locals init (class [mscorlib]System.Collections.IEnumerator V_0,
class [mscorlib]System.IDisposable V_1)
IL_0000: ldarg.0
IL_0001: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator()
IL_0006: stloc.0
.try
{
IL_0007: br.s IL_0014
IL_0009: ldloc.0
IL_000a: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
IL_000f: call void [mscorlib]System.Console::WriteLine(object)
IL_0014: ldloc.0
IL_0015: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_001a: brtrue.s IL_0009
IL_001c: leave.s IL_002f
} // end .try
finally
{
IL_001e: ldloc.0
IL_001f: isinst [mscorlib]System.IDisposable
IL_0024: stloc.1
IL_0025: ldloc.1
IL_0026: brfalse.s IL_002e
IL_0028: ldloc.1
IL_0029: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_002e: endfinally
} // end handler
IL_002f: ret
} // end of method NoForEachStatement::SimpleNonGenericForeach
.method public hidebysig static void SimpleForeachOverInts(class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> enumerable) cil managed
{
// Code size 41 (0x29)
.maxstack 1
.locals init (class [mscorlib]System.Collections.Generic.IEnumerator`1<int32> V_0)
IL_0000: ldarg.0
IL_0001: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
IL_0006: stloc.0
.try
{
IL_0007: br.s IL_0014
IL_0009: ldloc.0
IL_000a: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()
IL_000f: call void [mscorlib]System.Console::WriteLine(int32)
IL_0014: ldloc.0
IL_0015: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_001a: brtrue.s IL_0009
IL_001c: leave.s IL_0028
} // end .try
finally
{
IL_001e: ldloc.0
IL_001f: brfalse.s IL_0027
IL_0021: ldloc.0
IL_0022: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0027: endfinally
} // end handler
IL_0028: ret
} // end of method NoForEachStatement::SimpleForeachOverInts
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method NoForEachStatement::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************

145
ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoForEachStatement.roslyn.il

@ -0,0 +1,145 @@ @@ -0,0 +1,145 @@
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly NoForEachStatement
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
// --- The following custom attribute is added automatically, do not uncomment -------
// .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 )
.permissionset reqmin
= {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}}
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module NoForEachStatement.dll
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// =============== CLASS MEMBERS DECLARATION ===================
.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement
extends [mscorlib]System.Object
{
.method public hidebysig static void SimpleNonGenericForeach(class [mscorlib]System.Collections.IEnumerable enumerable) cil managed
{
// Code size 56 (0x38)
.maxstack 1
.locals init (class [mscorlib]System.Collections.IEnumerator V_0,
object V_1,
class [mscorlib]System.IDisposable V_2)
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.0
IL_0003: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator()
IL_0008: stloc.0
.try
{
IL_0009: br.s IL_001b
IL_000b: ldloc.0
IL_000c: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
IL_0011: stloc.1
IL_0012: nop
IL_0013: ldloc.1
IL_0014: call void [mscorlib]System.Console::WriteLine(object)
IL_0019: nop
IL_001a: nop
IL_001b: ldloc.0
IL_001c: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0021: brtrue.s IL_000b
IL_0023: leave.s IL_0037
} // end .try
finally
{
IL_0025: ldloc.0
IL_0026: isinst [mscorlib]System.IDisposable
IL_002b: stloc.2
IL_002c: ldloc.2
IL_002d: brfalse.s IL_0036
IL_002f: ldloc.2
IL_0030: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0035: nop
IL_0036: endfinally
} // end handler
IL_0037: ret
} // end of method NoForEachStatement::SimpleNonGenericForeach
.method public hidebysig static void SimpleForeachOverInts(class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> enumerable) cil managed
{
// Code size 49 (0x31)
.maxstack 1
.locals init (class [mscorlib]System.Collections.Generic.IEnumerator`1<int32> V_0,
int32 V_1)
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.0
IL_0003: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
IL_0008: stloc.0
.try
{
IL_0009: br.s IL_001b
IL_000b: ldloc.0
IL_000c: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()
IL_0011: stloc.1
IL_0012: nop
IL_0013: ldloc.1
IL_0014: call void [mscorlib]System.Console::WriteLine(int32)
IL_0019: nop
IL_001a: nop
IL_001b: ldloc.0
IL_001c: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0021: brtrue.s IL_000b
IL_0023: leave.s IL_0030
} // end .try
finally
{
IL_0025: ldloc.0
IL_0026: brfalse.s IL_002f
IL_0028: ldloc.0
IL_0029: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_002e: nop
IL_002f: endfinally
} // end handler
IL_0030: ret
} // end of method NoForEachStatement::SimpleForeachOverInts
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method NoForEachStatement::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoForEachStatement
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************

14
ICSharpCode.Decompiler.Tests/UglyTestRunner.cs

@ -68,7 +68,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -68,7 +68,7 @@ namespace ICSharpCode.Decompiler.Tests
};
[Test]
public void NoArrayInitializers([ValueSource("roslynOnlyOptions")] CompilerOptions cscOptions)
public void NoArrayInitializers([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
ArrayInitializers = false
@ -76,7 +76,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -76,7 +76,7 @@ namespace ICSharpCode.Decompiler.Tests
}
[Test]
public void NoDecimalConstants([ValueSource("roslynOnlyOptions")] CompilerOptions cscOptions)
public void NoDecimalConstants([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
DecimalConstants = false
@ -84,13 +84,21 @@ namespace ICSharpCode.Decompiler.Tests @@ -84,13 +84,21 @@ namespace ICSharpCode.Decompiler.Tests
}
[Test]
public void NoExtensionMethods([ValueSource("roslynOnlyOptions")] CompilerOptions cscOptions)
public void NoExtensionMethods([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
ExtensionMethods = false
});
}
[Test]
public void NoForEachStatement([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
ForEachStatement = false
});
}
void RunForLibrary([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, DecompilerSettings decompilerSettings = null)
{
Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, decompilerSettings);

2
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -783,6 +783,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -783,6 +783,7 @@ namespace ICSharpCode.Decompiler.CSharp
syntaxTree = new SyntaxTree();
foreach (var type in types) {
CancellationToken.ThrowIfCancellationRequested();
if (type.IsNil)
throw new ArgumentException("types contains null element");
RequiredNamespaceCollector.CollectNamespaces(type, module, decompileRun.Namespaces);
@ -1297,6 +1298,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1297,6 +1298,7 @@ namespace ICSharpCode.Decompiler.CSharp
parameter.AddAnnotation(new ILVariableResolveResult(v, method.Parameters[i].Type));
i++;
}
entityDecl.AddAnnotation(function);
}
var localSettings = settings.Clone();

4
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -1246,6 +1246,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1246,6 +1246,10 @@ namespace ICSharpCode.Decompiler.CSharp
private bool CanUseDelegateConstruction(IMethod targetMethod, ILInstruction thisArg, IMethod invokeMethod)
{
// Accessors cannot be directly referenced as method group in C#
// see https://github.com/icsharpcode/ILSpy/issues/1741#issuecomment-540179101
if (targetMethod.IsAccessor)
return false;
if (targetMethod.IsStatic) {
// If the invoke method is known, we can compare the parameter counts to figure out whether the
// delegate is static or binds the first argument

4
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -203,7 +203,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -203,7 +203,7 @@ namespace ICSharpCode.Decompiler.CSharp
return true;
}
foreach (var f in function.LocalFunctions.OfType<ILFunction>()) {
foreach (var f in function.LocalFunctions) {
if (f.Name == name)
return true;
}
@ -1511,7 +1511,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1511,7 +1511,7 @@ namespace ICSharpCode.Decompiler.CSharp
TranslatedExpression resultExpr;
if (inst.EvalMode == CompoundEvalMode.EvaluatesToOldValue) {
Debug.Assert(op == AssignmentOperatorType.Add || op == AssignmentOperatorType.Subtract);
Debug.Assert(inst.Value.MatchLdcI(1));
Debug.Assert(inst.Value.MatchLdcI(1) || inst.Value.MatchLdcF4(1) || inst.Value.MatchLdcF8(1));
UnaryOperatorType unary;
ExpressionType exprType;
if (op == AssignmentOperatorType.Add) {

4
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs

@ -56,6 +56,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -56,6 +56,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
TypeSystemAstBuilder astBuilder = CreateAstBuilder();
AstNode node = astBuilder.ConvertSymbol(symbol);
writer.StartNode(node);
EntityDeclaration entityDecl = node as EntityDeclaration;
if (entityDecl != null)
PrintModifiers(entityDecl.Modifiers, writer);
@ -167,6 +168,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -167,6 +168,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
} else {
writer.WriteToken(Roles.Semicolon, ";");
}
writer.EndNode(node);
}
}
@ -189,7 +191,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -189,7 +191,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
TypeSystemAstBuilder CreateAstBuilder()
{
TypeSystemAstBuilder astBuilder = new TypeSystemAstBuilder();
astBuilder.AddTypeReferenceAnnotations = true;
astBuilder.AddResolveResultAnnotations = true;
astBuilder.ShowTypeParametersForUnboundTypes = true;
astBuilder.ShowModifiers = (ConversionFlags & ConversionFlags.ShowModifiers) == ConversionFlags.ShowModifiers;
astBuilder.ShowAccessibility = (ConversionFlags & ConversionFlags.ShowAccessibility) == ConversionFlags.ShowAccessibility;

6
ICSharpCode.Decompiler/CSharp/OutputVisitor/ITokenWriter.cs

@ -67,6 +67,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -67,6 +67,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
var target = new TextWriterTokenWriter(writer) { IndentationString = indentation };
return new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(new InsertMissingTokensDecorator(target, target)));
}
public static TokenWriter InsertRequiredSpaces(TokenWriter writer)
{
return new InsertRequiredSpacesDecorator(writer);
}
public static TokenWriter WrapInWriterThatSetsLocationsInAST(TokenWriter writer)
{
@ -79,6 +84,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -79,6 +84,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
public interface ILocatable
{
TextLocation Location { get; }
int Length { get; }
}
public abstract class DecoratingTokenWriter : TokenWriter

122
ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs

@ -30,22 +30,20 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -30,22 +30,20 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
public class TextWriterTokenWriter : TokenWriter, ILocatable
{
readonly TextWriter textWriter;
int indentation;
bool needsIndent = true;
bool isAtStartOfLine = true;
int line, column;
public int Indentation {
get { return this.indentation; }
set { this.indentation = value; }
}
public int Indentation { get; set; }
public TextLocation Location {
get { return new TextLocation(line, column + (needsIndent ? indentation * IndentationString.Length : 0)); }
get { return new TextLocation(line, column + (needsIndent ? Indentation * IndentationString.Length : 0)); }
}
public string IndentationString { get; set; }
public int Length { get; private set; }
public TextWriterTokenWriter(TextWriter textWriter)
{
if (textWriter == null)
@ -55,73 +53,80 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -55,73 +53,80 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
this.line = 1;
this.column = 1;
}
public override void WriteIdentifier(Identifier identifier)
{
WriteIndentation();
if (identifier.IsVerbatim || CSharpOutputVisitor.IsKeyword(identifier.Name, identifier)) {
textWriter.Write('@');
column++;
Length++;
}
string name = EscapeIdentifier(identifier.Name);
textWriter.Write(name);
column += name.Length;
Length += name.Length;
isAtStartOfLine = false;
}
public override void WriteKeyword(Role role, string keyword)
{
WriteIndentation();
column += keyword.Length;
Length += keyword.Length;
textWriter.Write(keyword);
isAtStartOfLine = false;
}
public override void WriteToken(Role role, string token)
{
WriteIndentation();
column += token.Length;
Length += token.Length;
textWriter.Write(token);
isAtStartOfLine = false;
}
public override void Space()
{
WriteIndentation();
column++;
Length++;
textWriter.Write(' ');
}
protected void WriteIndentation()
{
if (needsIndent) {
needsIndent = false;
for (int i = 0; i < indentation; i++) {
for (int i = 0; i < Indentation; i++) {
textWriter.Write(this.IndentationString);
}
column += indentation * IndentationString.Length;
column += Indentation * IndentationString.Length;
Length += Indentation * IndentationString.Length;
}
}
public override void NewLine()
{
textWriter.WriteLine();
column = 1;
line++;
Length += textWriter.NewLine.Length;
needsIndent = true;
isAtStartOfLine = true;
}
public override void Indent()
{
indentation++;
Indentation++;
}
public override void Unindent()
{
indentation--;
Indentation--;
}
public override void WriteComment(CommentType commentType, string content)
{
WriteIndentation();
@ -129,6 +134,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -129,6 +134,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
case CommentType.SingleLine:
textWriter.Write("//");
textWriter.WriteLine(content);
Length += 2 + content.Length + textWriter.NewLine.Length;
column = 1;
line++;
needsIndent = true;
@ -138,6 +144,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -138,6 +144,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
textWriter.Write("/*");
textWriter.Write(content);
textWriter.Write("*/");
Length += 4 + content.Length;
column += 2;
UpdateEndLocation(content, ref line, ref column);
column += 2;
@ -146,6 +153,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -146,6 +153,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
case CommentType.Documentation:
textWriter.Write("///");
textWriter.WriteLine(content);
Length += 3 + content.Length + textWriter.NewLine.Length;
column = 1;
line++;
needsIndent = true;
@ -155,6 +163,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -155,6 +163,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
textWriter.Write("/**");
textWriter.Write(content);
textWriter.Write("*/");
Length += 5 + content.Length;
column += 3;
UpdateEndLocation(content, ref line, ref column);
column += 2;
@ -163,10 +172,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -163,10 +172,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
default:
textWriter.Write(content);
column += content.Length;
Length += content.Length;
break;
}
}
static void UpdateEndLocation(string content, ref int line, ref int column)
{
if (string.IsNullOrEmpty(content))
@ -186,7 +196,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -186,7 +196,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
column++;
}
}
public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument)
{
// pre-processor directive must start on its own line
@ -197,14 +207,16 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -197,14 +207,16 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
string directive = type.ToString().ToLowerInvariant();
textWriter.Write(directive);
column += 1 + directive.Length;
Length += 1 + directive.Length;
if (!string.IsNullOrEmpty(argument)) {
textWriter.Write(' ');
textWriter.Write(argument);
column += 1 + argument.Length;
Length += 1 + argument.Length;
}
NewLine();
}
public static string PrintPrimitiveValue(object value)
{
TextWriter writer = new StringWriter();
@ -212,48 +224,55 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -212,48 +224,55 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
tokenWriter.WritePrimitiveValue(value);
return writer.ToString();
}
public override void WritePrimitiveValue(object value, string literalValue = null)
{
if (literalValue != null) {
textWriter.Write(literalValue);
column += literalValue.Length;
Length += literalValue.Length;
return;
}
if (value == null) {
// usually NullReferenceExpression should be used for this, but we'll handle it anyways
textWriter.Write("null");
column += 4;
Length += 4;
return;
}
if (value is bool) {
if ((bool)value) {
textWriter.Write("true");
column += 4;
Length += 4;
} else {
textWriter.Write("false");
column += 5;
Length += 5;
}
return;
}
if (value is string) {
string tmp = ConvertString(value.ToString());
column += tmp.Length + 2;
Length += tmp.Length + 2;
textWriter.Write('"');
textWriter.Write(tmp);
textWriter.Write('"');
} else if (value is char) {
string tmp = ConvertCharLiteral((char)value);
column += tmp.Length + 2;
Length += tmp.Length + 2;
textWriter.Write('\'');
textWriter.Write(tmp);
textWriter.Write('\'');
} else if (value is decimal) {
string str = ((decimal)value).ToString(NumberFormatInfo.InvariantInfo) + "m";
column += str.Length;
Length += str.Length;
textWriter.Write(str);
} else if (value is float) {
float f = (float)value;
@ -262,16 +281,20 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -262,16 +281,20 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
// but we still support writing these to make life easier for code generators.
textWriter.Write("float");
column += 5;
Length += 5;
WriteToken(Roles.Dot, ".");
if (float.IsPositiveInfinity(f)) {
textWriter.Write("PositiveInfinity");
column += "PositiveInfinity".Length;
Length += "PositiveInfinity".Length;
} else if (float.IsNegativeInfinity(f)) {
textWriter.Write("NegativeInfinity");
column += "NegativeInfinity".Length;
Length += "NegativeInfinity".Length;
} else {
textWriter.Write("NaN");
column += 3;
Length += 3;
}
return;
}
@ -281,9 +304,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -281,9 +304,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
// the special case here than to do it in all code generators)
textWriter.Write("-");
column++;
Length++;
}
var str = f.ToString("R", NumberFormatInfo.InvariantInfo) + "f";
column += str.Length;
Length += str.Length;
textWriter.Write(str);
} else if (value is double) {
double f = (double)value;
@ -292,16 +317,20 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -292,16 +317,20 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
// but we still support writing these to make life easier for code generators.
textWriter.Write("double");
column += 6;
Length += 6;
WriteToken(Roles.Dot, ".");
if (double.IsPositiveInfinity(f)) {
textWriter.Write("PositiveInfinity");
column += "PositiveInfinity".Length;
Length += "PositiveInfinity".Length;
} else if (double.IsNegativeInfinity(f)) {
textWriter.Write("NegativeInfinity");
column += "NegativeInfinity".Length;
Length += "NegativeInfinity".Length;
} else {
textWriter.Write("NaN");
column += 3;
Length += 3;
}
return;
}
@ -310,20 +339,22 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -310,20 +339,22 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
// (again, not a primitive expression, but it's better to handle
// the special case here than to do it in all code generators)
textWriter.Write("-");
Length++;
}
string number = f.ToString("R", NumberFormatInfo.InvariantInfo);
if (number.IndexOf('.') < 0 && number.IndexOf('E') < 0) {
number += ".0";
}
textWriter.Write(number);
Length += number.Length;
} else if (value is IFormattable) {
StringBuilder b = new StringBuilder ();
// if (primitiveExpression.LiteralFormat == LiteralFormat.HexadecimalNumber) {
// b.Append("0x");
// b.Append(((IFormattable)val).ToString("x", NumberFormatInfo.InvariantInfo));
// } else {
b.Append(((IFormattable)value).ToString(null, NumberFormatInfo.InvariantInfo));
// }
StringBuilder b = new StringBuilder();
// if (primitiveExpression.LiteralFormat == LiteralFormat.HexadecimalNumber) {
// b.Append("0x");
// b.Append(((IFormattable)val).ToString("x", NumberFormatInfo.InvariantInfo));
// } else {
b.Append(((IFormattable)value).ToString(null, NumberFormatInfo.InvariantInfo));
// }
if (value is uint || value is ulong) {
b.Append("u");
}
@ -332,12 +363,15 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -332,12 +363,15 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
}
textWriter.Write(b.ToString());
column += b.Length;
Length += b.Length;
} else {
textWriter.Write(value.ToString());
column += value.ToString().Length;
int length = value.ToString().Length;
column += length;
Length += length;
}
}
/// <summary>
/// Gets the escape sequence for the specified character within a char literal.
/// Does not include the single quotes surrounding the char literal.
@ -349,7 +383,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -349,7 +383,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
}
return ConvertChar(ch) ?? ch.ToString();
}
/// <summary>
/// Gets the escape sequence for the specified character.
/// </summary>
@ -412,7 +446,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -412,7 +446,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
/// </summary>
public static string ConvertString(string str)
{
StringBuilder sb = new StringBuilder ();
StringBuilder sb = new StringBuilder();
foreach (char ch in str) {
string s = ch == '"' ? "\\\"" : ConvertChar(ch);
if (s != null) sb.Append(s);
@ -477,17 +511,19 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -477,17 +511,19 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
return true;
}
}
public override void WritePrimitiveType(string type)
{
textWriter.Write(type);
column += type.Length;
Length += type.Length;
if (type == "new") {
textWriter.Write("()");
column += 2;
Length += 2;
}
}
public override void StartNode(AstNode node)
{
// Write out the indentation, so that overrides of this method
@ -495,7 +531,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -495,7 +531,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
// in the output.
WriteIndentation();
}
public override void EndNode(AstNode node)
{
}

40
ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs

@ -55,10 +55,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -55,10 +55,10 @@ namespace ICSharpCode.Decompiler.CSharp
{
if (entity == null || entity.MetadataToken.IsNil)
return;
if (mappingInfo == null)
mappingInfo = CSharpDecompiler.GetCodeMappingInfo(entity.ParentModule.PEFile, entity.MetadataToken);
switch (entity) {
case ITypeDefinition td:
if (mappingInfo == null)
mappingInfo = CSharpDecompiler.GetCodeMappingInfo(entity.ParentModule.PEFile, entity.MetadataToken);
namespaces.Add(td.Namespace);
HandleAttributes(td.GetAttributes());
HandleTypeParameters(td.TypeParameters);
@ -100,35 +100,31 @@ namespace ICSharpCode.Decompiler.CSharp @@ -100,35 +100,31 @@ namespace ICSharpCode.Decompiler.CSharp
CollectNamespacesForTypeReference(param.Type);
}
HandleTypeParameters(method.TypeParameters);
if (!method.MetadataToken.IsNil) {
if (mappingInfo == null)
mappingInfo = CSharpDecompiler.GetCodeMappingInfo(entity.ParentModule.PEFile, entity.MetadataToken);
var reader = module.PEFile.Reader;
var parts = mappingInfo.GetMethodParts((MethodDefinitionHandle)method.MetadataToken).ToList();
foreach (var part in parts) {
HandleOverrides(part.GetMethodImplementations(module.metadata), module);
var methodDef = module.metadata.GetMethodDefinition(part);
if (method.HasBody) {
MethodBodyBlock body;
try {
body = reader.GetMethodBody(methodDef.RelativeVirtualAddress);
} catch (BadImageFormatException) {
continue;
}
CollectNamespacesFromMethodBody(body, module);
var reader = module.PEFile.Reader;
var parts = mappingInfo.GetMethodParts((MethodDefinitionHandle)method.MetadataToken).ToList();
foreach (var part in parts) {
HandleOverrides(part.GetMethodImplementations(module.metadata), module);
var methodDef = module.metadata.GetMethodDefinition(part);
if (method.HasBody) {
MethodBodyBlock body;
try {
body = reader.GetMethodBody(methodDef.RelativeVirtualAddress);
} catch (BadImageFormatException) {
continue;
}
CollectNamespacesFromMethodBody(body, module);
}
}
break;
case IProperty property:
HandleAttributes(property.GetAttributes());
CollectNamespaces(property.Getter, module);
CollectNamespaces(property.Setter, module);
CollectNamespaces(property.Getter, module, mappingInfo);
CollectNamespaces(property.Setter, module, mappingInfo);
break;
case IEvent @event:
HandleAttributes(@event.GetAttributes());
CollectNamespaces(@event.AddAccessor, module);
CollectNamespaces(@event.RemoveAccessor, module);
CollectNamespaces(@event.AddAccessor, module, mappingInfo);
CollectNamespaces(@event.RemoveAccessor, module, mappingInfo);
break;
}
}

2
ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs

@ -238,7 +238,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -238,7 +238,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
throw new NotSupportedException();
}
public UnaryOperatorMethod(ICompilation compilaton) : base(compilaton)
public UnaryOperatorMethod(ICompilation compilation) : base(compilation)
{
}
}

9
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -406,7 +406,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -406,7 +406,8 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override Statement VisitUsingInstruction(UsingInstruction inst)
{
var transformed = TransformToForeach(inst, out var resource);
var resource = exprBuilder.Translate(inst.ResourceExpression).Expression;
var transformed = TransformToForeach(inst, resource);
if (transformed != null)
return transformed;
AstNode usingInit = resource;
@ -446,14 +447,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -446,14 +447,12 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
Statement TransformToForeach(UsingInstruction inst, out Expression resource)
Statement TransformToForeach(UsingInstruction inst, Expression resource)
{
if (!settings.ForEachStatement) {
resource = null;
return null;
}
// Check if the using resource matches the GetEnumerator pattern.
resource = exprBuilder.Translate(inst.ResourceExpression);
var m = getEnumeratorPattern.Match(resource);
// The using body must be a BlockContainer.
if (!(inst.Body is BlockContainer container) || !m.Success)
@ -791,7 +790,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -791,7 +790,7 @@ namespace ICSharpCode.Decompiler.CSharp
bool ParentIsCurrentGetter(ILInstruction inst)
{
return inst.Parent is CallInstruction cv && cv.Method.IsAccessor &&
cv.Method.AccessorOwner is IProperty p && p.Getter.Equals(cv.Method);
cv.Method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter;
}
#endregion

28
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -212,8 +212,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -212,8 +212,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
private void AddTypeAnnotation(AstType astType, IType type)
{
if (AddTypeReferenceAnnotations)
astType.AddAnnotation(type);
if (AddResolveResultAnnotations)
astType.AddAnnotation(new TypeResolveResult(type));
}
@ -564,14 +562,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -564,14 +562,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return attr;
}
private IEnumerable<AttributeSection> ConvertAttributes(IEnumerable<IAttribute> attibutes)
private IEnumerable<AttributeSection> ConvertAttributes(IEnumerable<IAttribute> attributes)
{
return attibutes.Select(a => new AttributeSection(ConvertAttribute(a)));
return attributes.Select(a => new AttributeSection(ConvertAttribute(a)));
}
private IEnumerable<AttributeSection> ConvertAttributes(IEnumerable<IAttribute> attibutes, string target)
private IEnumerable<AttributeSection> ConvertAttributes(IEnumerable<IAttribute> attributes, string target)
{
return attibutes.Select(a => new AttributeSection(ConvertAttribute(a)) {
return attributes.Select(a => new AttributeSection(ConvertAttribute(a)) {
AttributeTarget = target
});
}
@ -1408,13 +1406,19 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1408,13 +1406,19 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
if (this.ShowBaseTypes) {
foreach (IType baseType in typeDefinition.DirectBaseTypes) {
if (baseType.IsKnownType (KnownTypeCode.Enum)) {
if (!typeDefinition.EnumUnderlyingType.IsKnownType (KnownTypeCode.Int32)) {
decl.BaseTypes.Add (ConvertType (typeDefinition.EnumUnderlyingType));
// if the declared type is an enum, replace all references to System.Enum with the enum-underlying type
if (typeDefinition.Kind == TypeKind.Enum && baseType.IsKnownType(KnownTypeCode.Enum)) {
if (!typeDefinition.EnumUnderlyingType.IsKnownType(KnownTypeCode.Int32)) {
decl.BaseTypes.Add(ConvertType(typeDefinition.EnumUnderlyingType));
}
} else if (!baseType.IsKnownType (KnownTypeCode.Object) &&
!baseType.IsKnownType (KnownTypeCode.ValueType)) {
decl.BaseTypes.Add (ConvertType (baseType));
// if the declared type is a struct, ignore System.ValueType
} else if (typeDefinition.Kind == TypeKind.Struct && baseType.IsKnownType(KnownTypeCode.ValueType)) {
continue;
// always ignore System.Object
} else if (baseType.IsKnownType(KnownTypeCode.Object)) {
continue;
} else {
decl.BaseTypes.Add(ConvertType(baseType));
}
}
}

71
ICSharpCode.Decompiler/CSharp/Transforms/ConvertConstructorCallIntoInitializer.cs

@ -58,30 +58,55 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -58,30 +58,55 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration)
{
var stmt = constructorDeclaration.Body.Statements.FirstOrDefault() as ExpressionStatement;
if (stmt == null)
if (!(constructorDeclaration.Body.Statements.FirstOrDefault() is ExpressionStatement stmt))
return;
if (!(stmt.Expression is InvocationExpression invocation))
return;
if (invocation.Target is MemberReferenceExpression mre && mre.MemberName == ".ctor") {
ConstructorInitializer ci = new ConstructorInitializer();
var target = mre.Target;
// Ignore casts, those might be added if references are missing.
if (target is CastExpression cast)
target = cast.Expression;
if (target is ThisReferenceExpression)
ci.ConstructorInitializerType = ConstructorInitializerType.This;
else if (target is BaseReferenceExpression)
ci.ConstructorInitializerType = ConstructorInitializerType.Base;
else
return;
// Move arguments from invocation to initializer:
invocation.Arguments.MoveTo(ci.Arguments);
// Add the initializer: (unless it is the default 'base()')
if (!(ci.ConstructorInitializerType == ConstructorInitializerType.Base && ci.Arguments.Count == 0))
constructorDeclaration.Initializer = ci.CopyAnnotationsFrom(invocation);
// Remove the statement:
stmt.Remove();
var currentCtor = (IMethod)constructorDeclaration.GetSymbol();
ConstructorInitializer ci;
switch (stmt.Expression) {
// Pattern for reference types:
// this..ctor(...);
case InvocationExpression invocation:
if (!(invocation.Target is MemberReferenceExpression mre) || mre.MemberName != ".ctor")
return;
if (!(invocation.GetSymbol() is IMethod ctor && ctor.IsConstructor))
return;
ci = new ConstructorInitializer();
var target = mre.Target;
// Ignore casts, those might be added if references are missing.
if (target is CastExpression cast)
target = cast.Expression;
if (target is ThisReferenceExpression)
ci.ConstructorInitializerType = ConstructorInitializerType.This;
else if (target is BaseReferenceExpression)
ci.ConstructorInitializerType = ConstructorInitializerType.Base;
else
return;
// Move arguments from invocation to initializer:
invocation.Arguments.MoveTo(ci.Arguments);
// Add the initializer: (unless it is the default 'base()')
if (!(ci.ConstructorInitializerType == ConstructorInitializerType.Base && ci.Arguments.Count == 0))
constructorDeclaration.Initializer = ci.CopyAnnotationsFrom(invocation);
// Remove the statement:
stmt.Remove();
break;
// Pattern for value types:
// this = new TSelf(...);
case AssignmentExpression assignment:
if (!(assignment.Right is ObjectCreateExpression oce && oce.GetSymbol() is IMethod ctor2 && ctor2.DeclaringTypeDefinition == currentCtor.DeclaringTypeDefinition))
return;
ci = new ConstructorInitializer();
if (assignment.Left is ThisReferenceExpression)
ci.ConstructorInitializerType = ConstructorInitializerType.This;
else
return;
// Move arguments from invocation to initializer:
oce.Arguments.MoveTo(ci.Arguments);
// Add the initializer: (unless it is the default 'base()')
if (!(ci.ConstructorInitializerType == ConstructorInitializerType.Base && ci.Arguments.Count == 0))
constructorDeclaration.Initializer = ci.CopyAnnotationsFrom(oce);
// Remove the statement:
stmt.Remove();
break;
}
}

80
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs

@ -25,6 +25,7 @@ using ICSharpCode.Decompiler.CSharp.Syntax; @@ -25,6 +25,7 @@ using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.Semantics;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
namespace ICSharpCode.Decompiler.CSharp.Transforms
{
@ -55,8 +56,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -55,8 +56,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
for (int i = 1; i < parts.Length; i++) {
nsType = new MemberType { Target = nsType, MemberName = parts[i] };
}
var reference = nsType.ToTypeReference(NameLookupMode.TypeInUsingDeclaration) as TypeOrNamespaceReference;
if (reference != null)
if (nsType.ToTypeReference(NameLookupMode.TypeInUsingDeclaration) is TypeOrNamespaceReference reference)
usingScope.Usings.Add(reference);
rootNode.InsertChildAfter(insertionPoint, new UsingDeclaration { Import = nsType }, SyntaxTree.MemberRole);
}
@ -109,28 +109,15 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -109,28 +109,15 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
base.VisitNamespaceDeclaration(namespaceDeclaration);
currentNamespace = oldNamespace;
}/*
public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration)
{
string oldNamespace = currentNamespace;
if (!(typeDeclaration.Parent is NamespaceDeclaration || typeDeclaration.Parent is TypeDeclaration)) {
var symbol = typeDeclaration.GetSymbol() as ITypeDefinition;
if (symbol != null) {
currentNamespace = symbol.Namespace;
DeclaredNamespaces.Add(currentNamespace);
}
}
base.VisitTypeDeclaration(typeDeclaration);
currentNamespace = oldNamespace;
}*/
}
}
sealed class FullyQualifyAmbiguousTypeNamesVisitor : DepthFirstAstVisitor
{
Stack<CSharpTypeResolveContext> context;
readonly Stack<CSharpTypeResolveContext> context;
readonly bool ignoreUsingScope;
TypeSystemAstBuilder astBuilder;
bool ignoreUsingScope;
public FullyQualifyAmbiguousTypeNamesVisitor(TransformContext context, UsingScope usingScope)
{
@ -152,9 +139,17 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -152,9 +139,17 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
this.astBuilder = CreateAstBuilder(currentContext);
}
static TypeSystemAstBuilder CreateAstBuilder(CSharpTypeResolveContext context)
static TypeSystemAstBuilder CreateAstBuilder(CSharpTypeResolveContext context, IL.ILFunction function = null)
{
return new TypeSystemAstBuilder(new CSharpResolver(context)) {
CSharpResolver resolver = new CSharpResolver(context);
if (function != null) {
foreach (var v in function.Variables) {
if (v.Kind != IL.VariableKind.Parameter)
resolver = resolver.AddVariable(new DefaultVariable(v.Type, v.Name));
}
}
return new TypeSystemAstBuilder(resolver) {
AddResolveResultAnnotations = true,
UseAliases = true
};
@ -201,24 +196,55 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -201,24 +196,55 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration)
{
Visit(methodDeclaration, base.VisitMethodDeclaration);
}
public override void VisitAccessor(Accessor accessor)
{
Visit(accessor, base.VisitAccessor);
}
public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration)
{
Visit(constructorDeclaration, base.VisitConstructorDeclaration);
}
public override void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration)
{
Visit(destructorDeclaration, base.VisitDestructorDeclaration);
}
public override void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration)
{
Visit(operatorDeclaration, base.VisitOperatorDeclaration);
}
void Visit<T>(T entityDeclaration, Action<T> baseCall) where T : EntityDeclaration
{
if (ignoreUsingScope) {
base.VisitMethodDeclaration(methodDeclaration);
baseCall(entityDeclaration);
return;
}
if (methodDeclaration.GetSymbol() is IMethod method && CSharpDecompiler.IsWindowsFormsInitializeComponentMethod(method)) {
if (entityDeclaration.GetSymbol() is IMethod method) {
var previousContext = context.Peek();
var currentContext = new CSharpTypeResolveContext(previousContext.CurrentModule);
CSharpTypeResolveContext currentContext;
if (CSharpDecompiler.IsWindowsFormsInitializeComponentMethod(method)) {
currentContext = new CSharpTypeResolveContext(previousContext.CurrentModule);
} else {
currentContext = previousContext.WithCurrentMember(method);
}
context.Push(currentContext);
try {
astBuilder = CreateAstBuilder(currentContext);
base.VisitMethodDeclaration(methodDeclaration);
var function = entityDeclaration.Annotation<IL.ILFunction>();
astBuilder = CreateAstBuilder(currentContext, function);
baseCall(entityDeclaration);
} finally {
astBuilder = CreateAstBuilder(previousContext);
context.Pop();
}
} else {
base.VisitMethodDeclaration(methodDeclaration);
baseCall(entityDeclaration);
}
}

3
ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

@ -285,7 +285,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -285,7 +285,8 @@ namespace ICSharpCode.Decompiler.CSharp
}
IType utype = NullableType.GetUnderlyingType(type);
IType targetUType = NullableType.GetUnderlyingType(targetType);
if (type.IsKnownType(KnownTypeCode.Boolean) && targetUType.GetStackType().IsIntegerType()) {
if (type.IsKnownType(KnownTypeCode.Boolean) && !targetUType.IsKnownType(KnownTypeCode.Boolean)
&& targetUType.GetStackType().IsIntegerType()) {
// convert from boolean to integer (or enum)
return new ConditionalExpression(
this.Expression,

BIN
ICSharpCode.Decompiler/DecompilerNuGetPackageIcon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

240
ICSharpCode.Decompiler/Documentation/XmlDocumentationElement.cs

@ -0,0 +1,240 @@ @@ -0,0 +1,240 @@
// Copyright (c) 2009-2013 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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Xml.Linq;
using ICSharpCode.Decompiler.Documentation;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.Documentation
{
/// <summary>
/// Represents an element in the XML documentation.
/// Any occurrences of "&lt;inheritdoc/>" are replaced with the inherited documentation.
/// </summary>
public class XmlDocumentationElement
{
static XmlDocumentationElement Create(string documentationComment, IEntity declaringEntity)
{
return new XmlDocumentationElement(XElement.Parse(documentationComment), declaringEntity, null);
}
readonly XElement element;
readonly IEntity declaringEntity;
readonly Func<string, IEntity> crefResolver;
volatile string textContent;
/// <summary>
/// Inheritance level; used to prevent cyclic doc inheritance.
/// </summary>
int nestingLevel;
/// <summary>
/// Creates a new documentation element.
/// </summary>
public XmlDocumentationElement(XElement element, IEntity declaringEntity, Func<string, IEntity> crefResolver)
{
if (element == null)
throw new ArgumentNullException("element");
this.element = element;
this.declaringEntity = declaringEntity;
this.crefResolver = crefResolver;
}
/// <summary>
/// Creates a new documentation element.
/// </summary>
public XmlDocumentationElement(string text, IEntity declaringEntity)
{
if (text == null)
throw new ArgumentNullException("text");
this.declaringEntity = declaringEntity;
this.textContent = text;
}
/// <summary>
/// Gets the entity on which this documentation was originally declared.
/// May return null.
/// </summary>
public IEntity DeclaringEntity {
get { return declaringEntity; }
}
IEntity referencedEntity;
volatile bool referencedEntityInitialized;
/// <summary>
/// Gets the entity referenced by the 'cref' attribute.
/// May return null.
/// </summary>
public IEntity ReferencedEntity {
get {
if (!referencedEntityInitialized) {
string cref = GetAttribute("cref");
if (!string.IsNullOrEmpty(cref) && crefResolver != null)
referencedEntity = crefResolver(cref);
referencedEntityInitialized = true;
}
return referencedEntity;
}
}
/// <summary>
/// Gets the element name.
/// </summary>
public string Name {
get {
return element != null ? element.Name.LocalName : string.Empty;
}
}
/// <summary>
/// Gets the attribute value.
/// </summary>
public string GetAttribute(string name)
{
return element?.Attribute(name)?.Value ?? string.Empty;
}
/// <summary>
/// Gets whether this is a pure text node.
/// </summary>
public bool IsTextNode {
get { return element == null; }
}
/// <summary>
/// Gets the text content.
/// </summary>
public string TextContent {
get {
if (textContent == null) {
StringBuilder b = new StringBuilder();
foreach (var child in this.Children)
b.Append(child.TextContent);
textContent = b.ToString();
}
return textContent;
}
}
IList<XmlDocumentationElement> children;
/// <summary>
/// Gets the child elements.
/// </summary>
public IList<XmlDocumentationElement> Children {
get {
if (element == null)
return EmptyList<XmlDocumentationElement>.Instance;
return LazyInitializer.EnsureInitialized(
ref this.children,
() => CreateElements(element.Nodes(), declaringEntity, crefResolver, nestingLevel));
}
}
static readonly string[] doNotInheritIfAlreadyPresent = {
"example", "exclude", "filterpriority", "preliminary", "summary",
"remarks", "returns", "threadsafety", "value"
};
static List<XmlDocumentationElement> CreateElements(IEnumerable<XObject> childObjects, IEntity declaringEntity, Func<string, IEntity> crefResolver, int nestingLevel)
{
List<XmlDocumentationElement> list = new List<XmlDocumentationElement>();
foreach (var child in childObjects) {
var childText = child as XText;
var childTag = child as XCData;
var childElement = child as XElement;
if (childText != null) {
list.Add(new XmlDocumentationElement(childText.Value, declaringEntity));
} else if (childTag != null) {
list.Add(new XmlDocumentationElement(childTag.Value, declaringEntity));
} else if (childElement != null) {
if (nestingLevel < 5 && childElement.Name == "inheritdoc") {
string cref = childElement.Attribute("cref").Value;
IEntity inheritedFrom = null;
string inheritedDocumentation = null;
if (cref != null) {
inheritedFrom = crefResolver(cref);
if (inheritedFrom != null)
inheritedDocumentation = inheritedFrom.GetDocumentation();
} else {
foreach (IMember baseMember in InheritanceHelper.GetBaseMembers((IMember)declaringEntity, includeImplementedInterfaces: true)) {
inheritedDocumentation = baseMember.GetDocumentation();
if (inheritedDocumentation != null) {
inheritedFrom = baseMember;
break;
}
}
}
if (inheritedDocumentation != null) {
var doc = XDocument.Parse(inheritedDocumentation);
// XPath filter not yet implemented
if (childElement.Parent == null && childElement.Attribute("select").Value == null) {
// Inheriting documentation at the root level
List<string> doNotInherit = new List<string>();
doNotInherit.Add("overloads");
doNotInherit.AddRange(childObjects.OfType<XElement>().Select(e => e.Name.LocalName).Intersect(
doNotInheritIfAlreadyPresent));
var inheritedChildren = doc.Nodes().Where(
inheritedObject => {
XElement inheritedElement = inheritedObject as XElement;
return !(inheritedElement != null && doNotInherit.Contains(inheritedElement.Name.LocalName));
});
list.AddRange(CreateElements(inheritedChildren, inheritedFrom, crefResolver, nestingLevel + 1));
}
}
} else {
list.Add(new XmlDocumentationElement(childElement, declaringEntity, crefResolver) { nestingLevel = nestingLevel });
}
}
}
if (list.Count > 0 && list[0].IsTextNode) {
if (string.IsNullOrWhiteSpace(list[0].textContent))
list.RemoveAt(0);
else
list[0].textContent = list[0].textContent.TrimStart();
}
if (list.Count > 0 && list[list.Count - 1].IsTextNode) {
if (string.IsNullOrWhiteSpace(list[list.Count - 1].textContent))
list.RemoveAt(list.Count - 1);
else
list[list.Count - 1].textContent = list[list.Count - 1].textContent.TrimEnd();
}
return list;
}
/// <inheritdoc/>
public override string ToString()
{
if (element != null)
return "<" + element.Name + ">";
else
return this.TextContent;
}
}
}

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -63,6 +63,7 @@ @@ -63,6 +63,7 @@
<Compile Include="CSharp\CSharpLanguageVersion.cs" />
<Compile Include="CSharp\RequiredNamespaceCollector.cs" />
<Compile Include="CSharp\SequencePointBuilder.cs" />
<Compile Include="Documentation\XmlDocumentationElement.cs" />
<Compile Include="Solution\ProjectId.cs" />
<Compile Include="Solution\ProjectItem.cs" />
<Compile Include="Solution\SolutionCreator.cs" />

13
ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template

@ -8,20 +8,23 @@ @@ -8,20 +8,23 @@
<owners>Daniel Grunwald, SharpDevelop</owners>
<license type="expression">MIT</license>
<projectUrl>https://github.com/icsharpcode/ILSpy/</projectUrl>
<iconUrl>https://ilspy.net/images/icon32.png</iconUrl>
<icon>images\DecompilerNuGetPackageIcon.png</icon>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>ICSharpCode.Decompiler is the decompiler engine used in ILSpy.</description>
<!--<releaseNotes></releaseNotes>-->
<copyright>Copyright 2011-$INSERTYEAR$ AlphaSierraPapa</copyright>
<tags>C# Decompiler ILSpy</tags>
<dependencies>
<dependency id="Humanizer.Core" version="2.2.0" />
<dependency id="System.Collections.Immutable" version="1.5.0" />
<dependency id="System.Reflection.Metadata" version="1.6.0" />
<dependency id="System.ValueTuple" version="4.3.0" />
<group targetFramework=".NETStandard2.0">
<dependency id="Humanizer.Core" version="2.2.0" />
<dependency id="System.Collections.Immutable" version="1.5.0" />
<dependency id="System.Reflection.Metadata" version="1.6.0" />
<dependency id="System.ValueTuple" version="4.3.0" />
</group>
</dependencies>
</metadata>
<files>
<file src="DecompilerNuGetPackageIcon.png" target="images\" />
<file src="bin\$Configuration$\netstandard2.0\ICSharpCode.Decompiler.dll" target="lib\netstandard2.0" />
<file src="bin\$Configuration$\netstandard2.0\ICSharpCode.Decompiler.pdb" target="lib\netstandard2.0" />
<file src="bin\$Configuration$\netstandard2.0\ICSharpCode.Decompiler.xml" target="lib\netstandard2.0" />

2
ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs

@ -580,7 +580,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -580,7 +580,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
List<ControlFlowNode> additionalNodes = new List<ControlFlowNode>();
// Find additionalNodes nodes and mark them as visited.
candidate.TraversePreOrder(n => n.Predecessors, additionalNodes.Add);
// This means Visited now represents the candiate extended loop.
// This means Visited now represents the candidate extended loop.
// Determine new exit points that are reachable from the additional nodes
// (note: some of these might have previously been exit points, too)
var newExitPoints = additionalNodes.SelectMany(n => n.Successors).Where(n => !n.Visited).ToHashSet();

2
ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs

@ -396,7 +396,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -396,7 +396,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// <summary>
/// Does some of the analysis of SwitchOnNullableTransform to add the null case control flow
/// to the results of SwitchAnaylsis
/// to the results of SwitchAnalysis
/// </summary>
private void AddNullCase(List<ControlFlowNode> flowNodes, List<ControlFlowNode> caseNodes)
{

5
ICSharpCode.Decompiler/IL/Instructions/StringToInt.cs

@ -55,7 +55,10 @@ namespace ICSharpCode.Decompiler.IL @@ -55,7 +55,10 @@ namespace ICSharpCode.Decompiler.IL
int i = 0;
foreach (var entry in Map) {
if (i > 0) output.Write(", ");
output.Write($"[\"{entry.Key}\"] = {entry.Value}");
if (entry.Key is null)
output.Write($"[null] = {entry.Value}");
else
output.Write($"[\"{entry.Key}\"] = {entry.Value}");
i++;
}
output.Write(" })");

3
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -269,6 +269,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -269,6 +269,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
internal static bool MethodRequiresCopyForReadonlyLValue(IMethod method)
{
if (method == null)
return true;
var type = method.DeclaringType;
if (type.IsReferenceType == true)
return false; // reference types are never implicitly copied
@ -303,6 +305,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -303,6 +305,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
return !method.IsStatic;
case OpCode.Await:
method = ((Await)inst.Parent).GetAwaiterMethod;
return true;
case OpCode.NullableUnwrap:
return ((NullableUnwrap)inst.Parent).RefInput;

8
ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
using System;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
@ -211,6 +212,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -211,6 +212,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
} else if (inst is LdLen ldLen) {
inst = ldLen.Array;
} else if (inst is LdElema ldElema) {
inst = ldElema.Array;
// ensure the access chain does not contain any 'nullable.unwrap' that aren't directly part of the chain
if (ldElema.Indices.Any(i => i.HasFlag(InstructionFlags.MayUnwrapNull)))
return false;
} else if (inst is NullableUnwrap unwrap) {
inst = unwrap.Argument;
if (unwrap.RefInput && inst is AddressOf addressOf) {
@ -272,7 +278,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -272,7 +278,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
static bool IsGetter(IMethod method)
{
return method.AccessorOwner is IProperty p && p.Getter == method;
return method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter;
}
private void IntroduceUnwrap(ILVariable testedVar, ILInstruction varLoad, Mode mode)

2
ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

@ -69,7 +69,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -69,7 +69,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// "a.GetValueOrDefault() == b.GetValueOrDefault() &amp;&amp; (a.HasValue &amp; b.HasValue)"
/// to
/// "(a.GetValueOrDefault() == b.GetValueOrDefault()) &amp; (a.HasValue &amp; b.HasValue)"
/// so this secondary entry point analyses logic.and as-if it was a short-circuting &amp;&amp;.
/// so this secondary entry point analyses logic.and as-if it was a short-circuiting &amp;&amp;.
/// </summary>
public bool Run(BinaryNumericInstruction bni)
{

2
ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs

@ -29,7 +29,7 @@ namespace ICSharpCode.Decompiler.IL @@ -29,7 +29,7 @@ namespace ICSharpCode.Decompiler.IL
/// Improves code quality by duplicating keyword exits to reduce nesting and restoring IL order.
/// </summary>
/// <remarks>
/// ConditionDetection and DetectSwitchBody both have agressive inlining policies for else blocks and default cases respectively.
/// ConditionDetection and DetectSwitchBody both have aggressive inlining policies for else blocks and default cases respectively.
/// This can lead to excessive indentation when the entire rest of the method/loop is included in the else block/default case.
/// When an If/SwitchInstruction is followed immediately by a keyword exit, the exit can be moved into the child blocks
/// allowing the else block or default case to be moved after the if/switch as all prior cases exit.

63
ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs

@ -154,26 +154,33 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -154,26 +154,33 @@ namespace ICSharpCode.Decompiler.IL.Transforms
bool SimplifyCascadingIfStatements(InstructionCollection<ILInstruction> instructions, ref int i)
{
if (i < 1) return false;
// match first block: checking switch-value for null or first value (Roslyn)
// if (call op_Equality(ldloc switchValueVar, ldstr value)) br firstBlock
// -or-
// if (comp(ldloc switchValueVar == ldnull)) br defaultBlock
if (!(instructions[i].MatchIfInstruction(out var condition, out var firstBlockJump)))
if (!instructions[i].MatchIfInstruction(out var condition, out var firstBlockOrDefaultJump))
return false;
if (!firstBlockJump.MatchBranch(out var firstBlock))
if (firstBlockOrDefaultJump.MatchBranch(out var firstBlock)) {
// success
} else if (firstBlockOrDefaultJump.MatchLeave(out _)) {
firstBlock = null;
// success
} else {
return false;
List<(string, Block)> values = new List<(string, Block)>();
}
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));
values.Add((firstBlockValue, firstBlock ?? firstBlockOrDefaultJump));
bool extraLoad = false;
bool keepAssignmentBefore = false;
if (instructions[i - 1].MatchStLoc(switchValueVar, out switchValue)) {
if (i >= 1 && instructions[i - 1].MatchStLoc(switchValueVar, out switchValue)) {
// stloc switchValueVar(switchValue)
// if (call op_Equality(ldloc switchValueVar, ldstr value)) br firstBlock
@ -183,7 +190,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -183,7 +190,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
switchValue = newSwitchValue;
extraLoad = true;
}
} else if (instructions[i - 1] is StLoc stloc) {
} else if (i >= 1 && instructions[i - 1] is StLoc stloc) {
if (stloc.Value.MatchLdLoc(switchValueVar)) {
// in case of optimized legacy code there are two stlocs:
// stloc otherSwitchValueVar(ldloc switchValue)
@ -212,10 +219,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -212,10 +219,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
// extract all cases and add them to the values list.
Block currentCaseBlock = nextCaseJump.TargetBlock;
Block nextCaseBlock;
ILInstruction nextCaseBlock;
while ((nextCaseBlock = MatchCaseBlock(currentCaseBlock, switchValueVar, out string value, out Block block)) != null) {
values.Add((value, block));
currentCaseBlock = nextCaseBlock;
currentCaseBlock = nextCaseBlock as Block;
if (currentCaseBlock == null)
break;
}
// We didn't find enough cases, exit
if (values.Count < 3)
@ -225,9 +234,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -225,9 +234,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
keepAssignmentBefore = true;
switchValue = new LdLoc(switchValueVar);
}
var sections = new List<SwitchSection>(values.SelectWithIndex((index, b) => new SwitchSection { Labels = new LongSet(index), Body = new Branch(b.Item2) }));
sections.Add(new SwitchSection { Labels = new LongSet(new LongInterval(0, sections.Count)).Invert(), Body = new Branch(currentCaseBlock) });
var stringToInt = new StringToInt(switchValue, values.SelectArray(item => item.Item1));
int offset = firstBlock == null ? 1 : 0;
var sections = new List<SwitchSection>(values.Skip(offset).SelectWithIndex((index, b) => new SwitchSection { Labels = new LongSet(index), Body = new Branch((Block)b.Item2) }));
sections.Add(new SwitchSection { Labels = new LongSet(new LongInterval(0, sections.Count)).Invert(), Body = currentCaseBlock != null ? (ILInstruction)new Branch(currentCaseBlock) : new Leave((BlockContainer)nextCaseBlock) });
var stringToInt = new StringToInt(switchValue, values.Skip(offset).Select(item => item.Item1).ToArray());
var inst = new SwitchInstruction(stringToInt);
inst.Sections.AddRange(sections);
if (extraLoad) {
@ -357,7 +367,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -357,7 +367,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// The <paramref name="switchVariable"/> is updated if the value gets copied to a different variable.
/// See comments below for more info.
/// </summary>
Block MatchCaseBlock(Block currentBlock, ILVariable switchVariable, out string value, out Block caseBlock)
ILInstruction MatchCaseBlock(Block currentBlock, ILVariable switchVariable, out string value, out Block caseBlock)
{
value = null;
caseBlock = null;
@ -369,19 +379,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -369,19 +379,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
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;
} else {
if (!currentBlock.Instructions[1].MatchBranch(out nextBlock))
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;
return nextBlock ?? (ILInstruction)blockContainer;
}
/// <summary>
@ -449,7 +465,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -449,7 +465,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (!tryGetValueBlock.Instructions[0].MatchIfInstruction(out condition, out var defaultBlockJump))
return false;
if (!defaultBlockJump.MatchBranch(out var defaultBlock) && !defaultBlockJump.MatchLeave(leaveContainer))
if (!defaultBlockJump.MatchBranch(out var defaultBlock) && !((leaveContainer != null && defaultBlockJump.MatchLeave(leaveContainer)) || defaultBlockJump.MatchLeave(out _)))
return false;
if (!(condition.MatchLogicNot(out var arg) && arg is CallInstruction c && c.Method.Name == "TryGetValue" &&
MatchDictionaryFieldLoad(c.Arguments[0], IsStringToIntDictionary, out var dictField2, out _) && dictField2.Equals(dictField)))
@ -557,9 +573,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -557,9 +573,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
bool AddNullSection(List<SwitchSection> sections, List<(string, int)> stringValues, Block nullValueCaseBlock)
bool AddNullSection(List<SwitchSection> sections, List<(string Value, int Index)> stringValues, Block nullValueCaseBlock)
{
var label = new LongSet(sections.Count);
var label = new LongSet(stringValues.Max(item => item.Index) + 1);
var possibleConflicts = sections.Where(sec => sec.Labels.Overlaps(label)).ToArray();
if (possibleConflicts.Length > 1)
return false;
@ -639,8 +655,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -639,8 +655,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
index = -1;
if (!(inst is CallInstruction c && c.Method.Name == "Add" && c.Arguments.Count == 3))
return false;
if (!(c.Arguments[0].MatchLdLoc(dictVar) && c.Arguments[1].MatchLdStr(out value)))
if (!c.Arguments[0].MatchLdLoc(dictVar))
return false;
if (!c.Arguments[1].MatchLdStr(out value)) {
if (c.Arguments[1].MatchLdsFld(out var field) && field.DeclaringType.IsKnownType(KnownTypeCode.String) && field.Name == "Empty") {
value = "";
} else {
return false;
}
}
if (!(c.Method.DeclaringType.Equals(dictionaryType) && !c.Method.IsStatic))
return false;
return (c.Arguments[2].MatchLdcI4(out index) || (c.Arguments[2].MatchBox(out var arg, out _) && arg.MatchLdcI4(out index)));
@ -984,7 +1007,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -984,7 +1007,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (!MatchStringLengthCall(lengthCheckCondition, switchValueVar))
return false;
if (!(exitBranch.MatchBranch(out defaultOrExitBlock) && exitBranch2.MatchBranch(defaultOrExitBlock)))
if (!exitBranch.Match(exitBranch2).Success)
return false;
if (bodyBranch.MatchLeave(out _)) {
bodyOrLeave = bodyBranch;

4
ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

@ -643,7 +643,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -643,7 +643,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
StLoc stloc;
var binary = UnwrapSmallIntegerConv(value, out var conv) as BinaryNumericInstruction;
if (binary != null && binary.Right.MatchLdcI(1)) {
if (binary != null && (binary.Right.MatchLdcI(1) || binary.Right.MatchLdcF4(1) || binary.Right.MatchLdcF8(1))) {
if (!(binary.Operator == BinaryNumericOperator.Add || binary.Operator == BinaryNumericOperator.Sub))
return false;
if (!ValidateCompoundAssign(binary, conv, targetType))
@ -717,7 +717,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -717,7 +717,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
}
if (UnwrapSmallIntegerConv(value, out var conv) is BinaryNumericInstruction binary) {
if (!binary.Left.MatchLdLoc(tmpVar) || !binary.Right.MatchLdcI(1))
if (!binary.Left.MatchLdLoc(tmpVar) || !(binary.Right.MatchLdcI(1) || binary.Right.MatchLdcF4(1) || binary.Right.MatchLdcF8(1)))
return false;
if (!(binary.Operator == BinaryNumericOperator.Add || binary.Operator == BinaryNumericOperator.Sub))
return false;

19
ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

@ -57,9 +57,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -57,9 +57,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Inner functions are transformed before outer functions
foreach (var f in function.Descendants.OfType<ILFunction>()) {
foreach (var v in f.Variables.ToArray()) {
if (HandleMonoStateMachine(function, v, decompilationContext, f))
if (context.Settings.YieldReturn && HandleMonoStateMachine(function, v, decompilationContext, f))
continue;
if (IsClosure(v, out ITypeDefinition closureType, out var inst)) {
if ((context.Settings.AnonymousMethods || context.Settings.ExpressionTrees) && IsClosure(v, out ITypeDefinition closureType, out var inst)) {
AddOrUpdateDisplayClass(f, v, closureType, inst, localFunctionClosureParameter: false);
}
if (context.Settings.LocalFunctions && f.Kind == ILFunctionKind.LocalFunction && v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index.Value] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p, decompilationContext)) {
@ -278,11 +278,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -278,11 +278,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
inst.Value.AcceptVisitor(this);
if (IsParameterAssignment(inst, out var displayClass, out var field, out var parameter)) {
context.Step($"Detected parameter assignment {parameter.Name}", inst);
displayClass.Variables.Add(field, parameter);
displayClass.Variables.Add((IField)field.MemberDefinition, parameter);
instructionsToRemove.Add(inst);
} else if (IsDisplayClassAssignment(inst, out displayClass, out field, out var variable)) {
context.Step($"Detected display-class assignment {variable.Name}", inst);
displayClass.Variables.Add(field, variable);
displayClass.Variables.Add((IField)field.MemberDefinition, variable);
instructionsToRemove.Add(inst);
} else {
inst.Target.AcceptVisitor(this);
@ -324,7 +324,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -324,7 +324,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (!(inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && v.Function == currentFunction))
return false;
if (displayClass.Variables.ContainsKey(field))
if (displayClass.Variables.ContainsKey((IField)field.MemberDefinition))
return false;
if (displayClassVar.Function != currentFunction)
return false;
@ -339,7 +339,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -339,7 +339,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
field = null;
if (!(inst is LdFlda ldflda))
return false;
field = (IField)ldflda.Field.MemberDefinition;
field = ldflda.Field;
return IsDisplayClassLoad(ldflda.Target, out displayClassVar)
&& displayClasses.TryGetValue(displayClassVar, out displayClass);
}
@ -350,11 +350,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -350,11 +350,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Get display class info
if (!IsDisplayClassFieldAccess(inst, out _, out DisplayClass displayClass, out IField field))
return;
// We want the specialized version, so that display-class type parameters are
// substituted with the type parameters from the use-site.
var fieldType = field.Type;
// However, use the unspecialized member definition to make reference comparisons in dictionary possible.
field = (IField)field.MemberDefinition;
if (!displayClass.Variables.TryGetValue(field, out var v)) {
context.Step($"Introduce captured variable for {field.FullName}", inst);
// Introduce a fresh variable for the display class field.
Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition);
v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name);
v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, fieldType, field.Name);
v.HasInitialValue = true;
v.CaptureScope = displayClass.CaptureScope;
inst.ReplaceWith(new LdLoca(v).WithILRange(inst));

2
ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs

@ -135,6 +135,8 @@ namespace ICSharpCode.Decompiler.Metadata @@ -135,6 +135,8 @@ namespace ICSharpCode.Decompiler.Metadata
return null;
var basePaths = RuntimePacks.Select(pack => Path.Combine(dotnetBasePath, "shared", pack));
foreach (var basePath in basePaths) {
if (!Directory.Exists(basePath))
continue;
var closestVersion = GetClosestVersionFolder(basePath, version);
if (File.Exists(Path.Combine(basePath, closestVersion, name.Name + ".dll"))) {
return Path.Combine(basePath, closestVersion, name.Name + ".dll");

2
ICSharpCode.Decompiler/Metadata/LightJson/JsonObject.cs

@ -39,7 +39,7 @@ namespace LightJson @@ -39,7 +39,7 @@ namespace LightJson
/// </summary>
/// <param name="key">The key of the property to get or set.</param>
/// <remarks>
/// The getter will return JsonValue.Null if the given key is not assosiated with any value.
/// The getter will return JsonValue.Null if the given key is not associated with any value.
/// </remarks>
public JsonValue this[string key] {
get {

9
ICSharpCode.Decompiler/NRExtensions.cs

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using ICSharpCode.Decompiler.Documentation;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler
@ -86,5 +87,13 @@ namespace ICSharpCode.Decompiler @@ -86,5 +87,13 @@ namespace ICSharpCode.Decompiler
return base.VisitTypeDefinition(type);
}
}
internal static string GetDocumentation(this IEntity entity)
{
var docProvider = XmlDocLoader.LoadDocumentation(entity.ParentModule.PEFile);
if (docProvider == null)
return null;
return docProvider.GetDocumentation(entity);
}
}
}

6
ILSpy.AddIn/SyntaxNodeExtensions.cs

@ -404,15 +404,15 @@ namespace ICSharpCode.ILSpy.AddIn @@ -404,15 +404,15 @@ namespace ICSharpCode.ILSpy.AddIn
public static NamespaceDeclarationSyntax GetInnermostNamespaceDeclarationWithUsings(this SyntaxNode contextNode)
{
var usingDirectiveAncsestor = contextNode.GetAncestor<UsingDirectiveSyntax>();
if (usingDirectiveAncsestor == null)
var usingDirectiveAncestor = contextNode.GetAncestor<UsingDirectiveSyntax>();
if (usingDirectiveAncestor == null)
{
return contextNode.GetAncestorsOrThis<NamespaceDeclarationSyntax>().FirstOrDefault(n => n.Usings.Count > 0);
}
else
{
// We are inside a using directive. In this case, we should find and return the first 'parent' namespace with usings.
var containingNamespace = usingDirectiveAncsestor.GetAncestor<NamespaceDeclarationSyntax>();
var containingNamespace = usingDirectiveAncestor.GetAncestor<NamespaceDeclarationSyntax>();
if (containingNamespace == null)
{
// We are inside a top level using directive (i.e. one that's directly in the compilation unit).

40
ILSpy/ExtensionMethods.cs

@ -18,6 +18,8 @@ @@ -18,6 +18,8 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using ICSharpCode.ILSpy.Options;
namespace ICSharpCode.ILSpy
@ -161,5 +163,43 @@ namespace ICSharpCode.ILSpy @@ -161,5 +163,43 @@ namespace ICSharpCode.ILSpy
}
return result;
}
#region DPI independence
public static Rect TransformToDevice(this Rect rect, Visual visual)
{
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice;
return Rect.Transform(rect, matrix);
}
public static Rect TransformFromDevice(this Rect rect, Visual visual)
{
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformFromDevice;
return Rect.Transform(rect, matrix);
}
public static Size TransformToDevice(this Size size, Visual visual)
{
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice;
return new Size(size.Width * matrix.M11, size.Height * matrix.M22);
}
public static Size TransformFromDevice(this Size size, Visual visual)
{
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformFromDevice;
return new Size(size.Width * matrix.M11, size.Height * matrix.M22);
}
public static Point TransformToDevice(this Point point, Visual visual)
{
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice;
return matrix.Transform(point);
}
public static Point TransformFromDevice(this Point point, Visual visual)
{
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformFromDevice;
return matrix.Transform(point);
}
#endregion
}
}

5
ILSpy/ILSpy.csproj

@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
<LangVersion>7.2</LangVersion>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
<GenerateSupportedRuntime>false</GenerateSupportedRuntime>
<EnableDefaultItems>false</EnableDefaultItems>
<UseWpf>true</UseWpf>
<ExtrasEnableDefaultPageItems>false</ExtrasEnableDefaultPageItems>
@ -39,6 +39,7 @@ @@ -39,6 +39,7 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System.IO.Compression" />
@ -237,7 +238,7 @@ @@ -237,7 +238,7 @@
<Compile Include="TextView\EditorCommands.cs" />
<Compile Include="TextView\FoldingCommands.cs" />
<Compile Include="Commands\SaveCodeContextMenuEntry.cs" />
<Compile Include="TextView\XmlDocRenderer.cs" />
<Compile Include="TextView\DocumentationUIBuilder.cs" />
<Compile Include="Analyzers\AnalyzeCommand.cs" />
<Compile Include="Analyzers\Builtin\FieldAccessAnalyzer.cs" />
<Compile Include="Analyzers\TreeNodes\AnalyzedFieldTreeNode.cs" />

2
ILSpy/ISmartTextOutput.cs

@ -39,7 +39,7 @@ namespace ICSharpCode.ILSpy @@ -39,7 +39,7 @@ namespace ICSharpCode.ILSpy
void BeginSpan(HighlightingColor highlightingColor);
void EndSpan();
}
public static class SmartTextOutputExtensions
{
/// <summary>

74
ILSpy/Languages/CSharpHighlightingTokenWriter.cs

@ -29,8 +29,6 @@ namespace ICSharpCode.ILSpy @@ -29,8 +29,6 @@ namespace ICSharpCode.ILSpy
{
class CSharpHighlightingTokenWriter : DecoratingTokenWriter
{
ISmartTextOutput textOutput;
HighlightingColor visibilityKeywordsColor;
HighlightingColor namespaceKeywordsColor;
HighlightingColor structureKeywordsColor;
@ -65,12 +63,15 @@ namespace ICSharpCode.ILSpy @@ -65,12 +63,15 @@ namespace ICSharpCode.ILSpy
HighlightingColor trueKeywordColor;
HighlightingColor typeKeywordsColor;
public CSharpHighlightingTokenWriter(TokenWriter decoratedWriter, ISmartTextOutput textOutput) : base(decoratedWriter)
public RichTextModel HighlightingModel { get; } = new RichTextModel();
public CSharpHighlightingTokenWriter(TokenWriter decoratedWriter, ISmartTextOutput textOutput = null, ILocatable locatable = null)
: base(decoratedWriter)
{
this.textOutput = textOutput;
var highlighting = HighlightingManager.Instance.GetDefinition("C#");
//this.defaultTextColor = ???;
this.locatable = locatable;
this.textOutput = textOutput;
this.visibilityKeywordsColor = highlighting.GetNamedColor("Visibility");
this.namespaceKeywordsColor = highlighting.GetNamedColor("NamespaceKeywords");
@ -264,11 +265,11 @@ namespace ICSharpCode.ILSpy @@ -264,11 +265,11 @@ namespace ICSharpCode.ILSpy
if (nodeStack.PeekOrDefault() is AttributeSection)
color = attributeKeywordsColor;
if (color != null) {
textOutput.BeginSpan(color);
BeginSpan(color);
}
base.WriteKeyword(role, keyword);
if (color != null) {
textOutput.EndSpan();
EndSpan();
}
}
@ -307,19 +308,25 @@ namespace ICSharpCode.ILSpy @@ -307,19 +308,25 @@ namespace ICSharpCode.ILSpy
break;
}
if (color != null) {
textOutput.BeginSpan(color);
BeginSpan(color);
}
base.WritePrimitiveType(type);
if (color != null) {
textOutput.EndSpan();
EndSpan();
}
}
public override void WriteIdentifier(Identifier identifier)
{
HighlightingColor color = null;
if (identifier.Name == "value" && identifier.Ancestors.OfType<Accessor>().FirstOrDefault() is Accessor accessor && accessor.Role != PropertyDeclaration.GetterRole)
if (identifier.Name == "value"
&& identifier.Parent?.GetResolveResult() is ILVariableResolveResult rr
&& rr.Variable.Kind == Decompiler.IL.VariableKind.Parameter
&& identifier.Ancestors.OfType<Accessor>().FirstOrDefault() is Accessor accessor
&& accessor.Role != PropertyDeclaration.GetterRole)
{
color = valueKeywordColor;
}
if ((identifier.Name == "dynamic" || identifier.Name == "var") && identifier.Parent is AstType)
color = queryKeywordsColor;
switch (GetCurrentDefinition()) {
@ -377,11 +384,11 @@ namespace ICSharpCode.ILSpy @@ -377,11 +384,11 @@ namespace ICSharpCode.ILSpy
break;
}
if (color != null) {
textOutput.BeginSpan(color);
BeginSpan(color);
}
base.WriteIdentifier(identifier);
if (color != null) {
textOutput.EndSpan();
EndSpan();
}
}
@ -395,11 +402,11 @@ namespace ICSharpCode.ILSpy @@ -395,11 +402,11 @@ namespace ICSharpCode.ILSpy
color = trueKeywordColor;
}
if (color != null) {
textOutput.BeginSpan(color);
BeginSpan(color);
}
base.WritePrimitiveValue(value, literalValue);
if (color != null) {
textOutput.EndSpan();
EndSpan();
}
}
@ -419,6 +426,9 @@ namespace ICSharpCode.ILSpy @@ -419,6 +426,9 @@ namespace ICSharpCode.ILSpy
ISymbol GetCurrentMemberReference()
{
if (nodeStack == null || nodeStack.Count == 0)
return null;
AstNode node = nodeStack.Peek();
var symbol = node.GetSymbol();
if (symbol == null && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression) {
@ -435,7 +445,7 @@ namespace ICSharpCode.ILSpy @@ -435,7 +445,7 @@ namespace ICSharpCode.ILSpy
return symbol;
}
Stack<AstNode> nodeStack = new Stack<AstNode>();
readonly Stack<AstNode> nodeStack = new Stack<AstNode>();
public override void StartNode(AstNode node)
{
@ -448,5 +458,39 @@ namespace ICSharpCode.ILSpy @@ -448,5 +458,39 @@ namespace ICSharpCode.ILSpy
base.EndNode(node);
nodeStack.Pop();
}
readonly Stack<HighlightingColor> colorStack = new Stack<HighlightingColor>();
HighlightingColor currentColor = new HighlightingColor();
int currentColorBegin = -1;
readonly ILocatable locatable;
readonly ISmartTextOutput textOutput;
private void BeginSpan(HighlightingColor highlightingColor)
{
if (textOutput != null) {
textOutput.BeginSpan(highlightingColor);
return;
}
if (currentColorBegin > -1)
HighlightingModel.SetHighlighting(currentColorBegin, locatable.Length - currentColorBegin, currentColor);
colorStack.Push(currentColor);
currentColor = currentColor.Clone();
currentColorBegin = locatable.Length;
currentColor.MergeWith(highlightingColor);
currentColor.Freeze();
}
private void EndSpan()
{
if (textOutput != null) {
textOutput.EndSpan();
return;
}
HighlightingModel.SetHighlighting(currentColorBegin, locatable.Length - currentColorBegin, currentColor);
currentColor = colorStack.Pop();
currentColorBegin = locatable.Length;
}
}
}

14
ILSpy/Languages/CSharpLanguage.cs

@ -26,7 +26,9 @@ using System.Reflection.Metadata; @@ -26,7 +26,9 @@ using System.Reflection.Metadata;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.OutputVisitor;
@ -485,7 +487,7 @@ namespace ICSharpCode.ILSpy @@ -485,7 +487,7 @@ namespace ICSharpCode.ILSpy
// HACK : UnknownType is not supported by CSharpAmbience.
} else if (type.Kind == TypeKind.Unknown) {
return (includeNamespace ? type.FullName : type.Name)
+ (type.TypeParameterCount > 0 ? "<" + string.Join(",", type.TypeArguments.Select(t => t.Name)) + ">" : "");
+ (type.TypeParameterCount > 0 ? "<" + string.Join(", ", type.TypeArguments.Select(t => t.Name)) + ">" : "");
} else {
return ambience.ConvertType(type);
}
@ -625,10 +627,14 @@ namespace ICSharpCode.ILSpy @@ -625,10 +627,14 @@ namespace ICSharpCode.ILSpy
return showAllMembers || !CSharpDecompiler.MemberIsHidden(assembly, member.MetadataToken, new DecompilationOptions().DecompilerSettings);
}
public override string GetTooltip(IEntity entity)
public override RichText GetRichTextTooltip(IEntity entity)
{
var flags = ConversionFlags.All & ~(ConversionFlags.ShowBody | ConversionFlags.PlaceReturnTypeAfterParameterList);
return new CSharpAmbience() { ConversionFlags = flags }.ConvertSymbol(entity);
var output = new StringWriter();
var decoratedWriter = new TextWriterTokenWriter(output);
var writer = new CSharpHighlightingTokenWriter(TokenWriter.InsertRequiredSpaces(decoratedWriter), locatable: decoratedWriter);
new CSharpAmbience() { ConversionFlags = flags }.ConvertSymbol(entity, writer, new DecompilerSettings().CSharpFormattingOptions);
return new RichText(output.ToString(), writer.HighlightingModel);
}
public override CodeMappingInfo GetCodeMappingInfo(PEFile module, EntityHandle member)

10
ILSpy/Languages/Language.cs

@ -21,6 +21,7 @@ using System.Collections.Generic; @@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Text;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution;
@ -349,6 +350,15 @@ namespace ICSharpCode.ILSpy @@ -349,6 +350,15 @@ namespace ICSharpCode.ILSpy
return GetDisplayName(entity, true, true, true);
}
/// <summary>
/// Converts a member signature to a string.
/// This is used for displaying the tooltip on a member reference.
/// </summary>
public virtual RichText GetRichTextTooltip(IEntity entity)
{
return GetTooltip(entity);
}
public virtual string FieldToString(IField field, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName)
{
if (field == null)

3
ILSpy/LoadedAssembly.cs

@ -156,7 +156,8 @@ namespace ICSharpCode.ILSpy @@ -156,7 +156,8 @@ namespace ICSharpCode.ILSpy
// runs on background thread
if (state is Stream stream) {
// Read the module from a precrafted stream
module = new PEFile(fileName, stream, metadataOptions: options);
var streamOptions = stream is MemoryStream ? PEStreamOptions.PrefetchEntireImage : PEStreamOptions.Default;
module = new PEFile(fileName, stream, streamOptions, metadataOptions: options);
} else {
// Read the module from disk (by default)
stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);

4
ILSpy/MainWindow.xaml.cs

@ -410,7 +410,7 @@ namespace ICSharpCode.ILSpy @@ -410,7 +410,7 @@ namespace ICSharpCode.ILSpy
}
}
private IEntity FindEntityInRelevantAssemblies(string navigateTo, IEnumerable<LoadedAssembly> relevantAssemblies)
internal static IEntity FindEntityInRelevantAssemblies(string navigateTo, IEnumerable<LoadedAssembly> relevantAssemblies)
{
ITypeReference typeRef = null;
IMemberReference memberRef = null;
@ -434,7 +434,7 @@ namespace ICSharpCode.ILSpy @@ -434,7 +434,7 @@ namespace ICSharpCode.ILSpy
return null;
}
private bool CanResolveTypeInPEFile(PEFile module, ITypeReference typeRef, out EntityHandle typeHandle)
static bool CanResolveTypeInPEFile(PEFile module, ITypeReference typeRef, out EntityHandle typeHandle)
{
switch (typeRef) {
case GetPotentiallyNestedClassTypeReference topLevelType:

1
ILSpy/Options/DisplaySettingsPanel.xaml.cs

@ -134,6 +134,7 @@ namespace ICSharpCode.ILSpy.Options @@ -134,6 +134,7 @@ namespace ICSharpCode.ILSpy.Options
section.SetAttributeValue("SortResults", s.SortResults);
section.SetAttributeValue("FoldBraces", s.FoldBraces);
section.SetAttributeValue("ExpandMemberDefinitions", s.ExpandMemberDefinitions);
section.SetAttributeValue("ExpandUsingDeclarations", s.ExpandUsingDeclarations);
section.SetAttributeValue("IndentationUseTabs", s.IndentationUseTabs);
section.SetAttributeValue("IndentationSize", s.IndentationSize);
section.SetAttributeValue("IndentationTabSize", s.IndentationTabSize);

247
ILSpy/TextView/DecompilerTextView.cs

@ -27,6 +27,7 @@ using System.Threading; @@ -27,6 +27,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
@ -44,8 +45,10 @@ using ICSharpCode.AvalonEdit.Rendering; @@ -44,8 +45,10 @@ using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Search;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.OutputVisitor;
using ICSharpCode.Decompiler.Documentation;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.AvalonEdit;
using ICSharpCode.ILSpy.Options;
@ -63,7 +66,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -63,7 +66,7 @@ namespace ICSharpCode.ILSpy.TextView
{
readonly ReferenceElementGenerator referenceElementGenerator;
readonly UIElementGenerator uiElementGenerator;
List<VisualLineElementGenerator> activeCustomElementGenerators = new List<VisualLineElementGenerator>();
readonly List<VisualLineElementGenerator> activeCustomElementGenerators = new List<VisualLineElementGenerator>();
RichTextColorizer activeRichTextColorizer;
BracketHighlightRenderer bracketHighlightRenderer;
FoldingManager foldingManager;
@ -112,6 +115,8 @@ namespace ICSharpCode.ILSpy.TextView @@ -112,6 +115,8 @@ namespace ICSharpCode.ILSpy.TextView
textEditor.TextArea.PreviewMouseDown += TextAreaMouseDown;
textEditor.TextArea.PreviewMouseUp += TextAreaMouseUp;
textEditor.TextArea.Caret.PositionChanged += HighlightBrackets;
textEditor.MouseMove += TextEditorMouseMove;
textEditor.MouseLeave += TextEditorMouseLeave;
textEditor.SetBinding(Control.FontFamilyProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("SelectedFont") });
textEditor.SetBinding(Control.FontSizeProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("SelectedFontSize") });
textEditor.SetBinding(TextEditor.WordWrapProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("EnableWordWrap") });
@ -166,20 +171,18 @@ namespace ICSharpCode.ILSpy.TextView @@ -166,20 +171,18 @@ namespace ICSharpCode.ILSpy.TextView
}
}
}
#endregion
#region Tooltip support
ToolTip tooltip;
void TextViewMouseHoverStopped(object sender, MouseEventArgs e)
{
if (tooltip != null)
tooltip.IsOpen = false;
}
ToolTip toolTip;
Popup popupToolTip;
void TextViewMouseHover(object sender, MouseEventArgs e)
{
if (!TryCloseExistingPopup(false)) {
return;
}
TextViewPosition? position = GetPositionFromMousePosition();
if (position == null)
return;
@ -190,35 +193,166 @@ namespace ICSharpCode.ILSpy.TextView @@ -190,35 +193,166 @@ namespace ICSharpCode.ILSpy.TextView
if (seg == null)
return;
object content = GenerateTooltip(seg);
if (tooltip != null)
tooltip.IsOpen = false;
if (content != null)
tooltip = new ToolTip() { Content = content, IsOpen = true };
if (content != null) {
popupToolTip = content as Popup;
if (popupToolTip != null) {
var popupPosition = GetPopupPosition(e);
popupToolTip.Closed += ToolTipClosed;
popupToolTip.HorizontalOffset = popupPosition.X;
popupToolTip.VerticalOffset = popupPosition.Y;
popupToolTip.StaysOpen = true; // We will close it ourselves
e.Handled = true;
popupToolTip.IsOpen = true;
distanceToPopupLimit = double.PositiveInfinity; // reset limit; we'll re-calculate it on the next mouse movement
} else {
if (toolTip == null) {
toolTip = new ToolTip();
toolTip.Closed += ToolTipClosed;
}
toolTip.PlacementTarget = this; // required for property inheritance
if (content is string s) {
toolTip.Content = new TextBlock {
Text = s,
TextWrapping = TextWrapping.Wrap
};
} else
toolTip.Content = content;
e.Handled = true;
toolTip.IsOpen = true;
}
}
}
bool TryCloseExistingPopup(bool mouseClick)
{
if (popupToolTip != null) {
if (popupToolTip.IsOpen && !mouseClick && popupToolTip is FlowDocumentTooltip t && !t.CloseWhenMouseMovesAway) {
return false; // Popup does not want to be closed yet
}
popupToolTip.IsOpen = false;
popupToolTip = null;
}
return true;
}
/// <summary> Returns Popup position based on mouse position, in device independent units </summary>
Point GetPopupPosition(MouseEventArgs mouseArgs)
{
Point mousePos = mouseArgs.GetPosition(this);
Point positionInPixels;
// align Popup with line bottom
TextViewPosition? logicalPos = textEditor.GetPositionFromPoint(mousePos);
if (logicalPos.HasValue) {
var textView = textEditor.TextArea.TextView;
positionInPixels =
textView.PointToScreen(
textView.GetVisualPosition(logicalPos.Value, VisualYPosition.LineBottom) - textView.ScrollOffset);
positionInPixels.X -= 4;
} else {
positionInPixels = PointToScreen(mousePos + new Vector(-4, 6));
}
// use device independent units, because Popup Left/Top are in independent units
return positionInPixels.TransformFromDevice(this);
}
void TextViewMouseHoverStopped(object sender, MouseEventArgs e)
{
// Non-popup tooltips get closed as soon as the mouse starts moving again
if (toolTip != null) {
toolTip.IsOpen = false;
e.Handled = true;
}
}
double distanceToPopupLimit;
const double MaxMovementAwayFromPopup = 5;
void TextEditorMouseMove(object sender, MouseEventArgs e)
{
if (popupToolTip != null) {
double distanceToPopup = GetDistanceToPopup(e);
if (distanceToPopup > distanceToPopupLimit) {
// Close popup if mouse moved away, exceeding the limit
TryCloseExistingPopup(false);
} else {
// reduce distanceToPopupLimit
distanceToPopupLimit = Math.Min(distanceToPopupLimit, distanceToPopup + MaxMovementAwayFromPopup);
}
}
}
double GetDistanceToPopup(MouseEventArgs e)
{
Point p = popupToolTip.Child.PointFromScreen(PointToScreen(e.GetPosition(this)));
Size size = popupToolTip.Child.RenderSize;
double x = 0;
if (p.X < 0)
x = -p.X;
else if (p.X > size.Width)
x = p.X - size.Width;
double y = 0;
if (p.Y < 0)
y = -p.Y;
else if (p.Y > size.Height)
y = p.Y - size.Height;
return Math.Sqrt(x * x + y * y);
}
void TextEditorMouseLeave(object sender, MouseEventArgs e)
{
if (popupToolTip != null && !popupToolTip.IsMouseOver) {
// do not close popup if mouse moved from editor to popup
TryCloseExistingPopup(false);
}
}
void OnUnloaded(object sender, EventArgs e)
{
// Close popup when another document gets selected
// TextEditorMouseLeave is not sufficient for this because the mouse might be over the popup when the document switch happens (e.g. Ctrl+Tab)
TryCloseExistingPopup(true);
}
void ToolTipClosed(object sender, EventArgs e)
{
if (toolTip == sender) {
toolTip = null;
}
if (popupToolTip == sender) {
// Because popupToolTip instances are created by the tooltip provider,
// they might be reused; so we should detach the event handler
popupToolTip.Closed -= ToolTipClosed;
popupToolTip = null;
}
}
object GenerateTooltip(ReferenceSegment segment)
{
if (segment.Reference is ICSharpCode.Decompiler.Disassembler.OpCodeInfo code) {
XmlDocumentationProvider docProvider = XmlDocLoader.MscorlibDocumentation;
if (docProvider != null){
DocumentationUIBuilder renderer = new DocumentationUIBuilder(new CSharpAmbience(), MainWindow.Instance.CurrentLanguage.SyntaxHighlighting);
renderer.AddSignatureBlock($"{code.Name} (0x{code.Code:x})");
if (docProvider != null) {
string documentation = docProvider.GetDocumentation("F:System.Reflection.Emit.OpCodes." + code.EncodedName);
if (documentation != null) {
XmlDocRenderer renderer = new XmlDocRenderer();
renderer.AppendText($"{code.Name} (0x{code.Code:x}) - ");
renderer.AddXmlDocumentation(documentation);
return renderer.CreateTextBlock();
renderer.AddXmlDocumentation(documentation, null, null);
}
}
return $"{code.Name} (0x{code.Code:x})";
return new FlowDocumentTooltip(renderer.CreateDocument());
} else if (segment.Reference is IEntity entity) {
return CreateTextBlockForEntity(entity);
return new FlowDocumentTooltip(CreateTooltipForEntity(entity));
} else if (segment.Reference is ValueTuple<PEFile, System.Reflection.Metadata.EntityHandle> unresolvedEntity) {
var typeSystem = new DecompilerTypeSystem(unresolvedEntity.Item1, unresolvedEntity.Item1.GetAssemblyResolver(), TypeSystemOptions.Default | TypeSystemOptions.Uncached);
try {
IEntity resolved = typeSystem.MainModule.ResolveEntity(unresolvedEntity.Item2);
if (resolved == null)
return null;
return CreateTextBlockForEntity(resolved);
return new FlowDocumentTooltip(CreateTooltipForEntity(resolved));
} catch (BadImageFormatException) {
return null;
}
@ -226,10 +360,12 @@ namespace ICSharpCode.ILSpy.TextView @@ -226,10 +360,12 @@ namespace ICSharpCode.ILSpy.TextView
return null;
}
static TextBlock CreateTextBlockForEntity(IEntity resolved)
static FlowDocument CreateTooltipForEntity(IEntity resolved)
{
XmlDocRenderer renderer = new XmlDocRenderer();
renderer.AppendText(MainWindow.Instance.CurrentLanguage.GetTooltip(resolved));
Language currentLanguage = MainWindow.Instance.CurrentLanguage;
DocumentationUIBuilder renderer = new DocumentationUIBuilder(new CSharpAmbience(), currentLanguage.SyntaxHighlighting);
RichText richText = currentLanguage.GetRichTextTooltip(resolved);
renderer.AddSignatureBlock(richText.Text, richText.ToRichTextModel());
try {
if (resolved.ParentModule == null || resolved.ParentModule.PEFile == null)
return null;
@ -237,14 +373,65 @@ namespace ICSharpCode.ILSpy.TextView @@ -237,14 +373,65 @@ namespace ICSharpCode.ILSpy.TextView
if (docProvider != null) {
string documentation = docProvider.GetDocumentation(resolved.GetIdString());
if (documentation != null) {
renderer.AppendText(Environment.NewLine);
renderer.AddXmlDocumentation(documentation);
renderer.AddXmlDocumentation(documentation, resolved, ResolveReference);
}
}
} catch (XmlException) {
// ignore
}
return renderer.CreateTextBlock();
return renderer.CreateDocument();
IEntity ResolveReference(string idString)
{
return MainWindow.FindEntityInRelevantAssemblies(idString, MainWindow.Instance.CurrentAssemblyList.GetAssemblies());
}
}
sealed class FlowDocumentTooltip : Popup
{
readonly FlowDocumentScrollViewer viewer;
public FlowDocumentTooltip(FlowDocument document)
{
TextOptions.SetTextFormattingMode(this, TextFormattingMode.Display);
double fontSize = DisplaySettingsPanel.CurrentDisplaySettings.SelectedFontSize;
viewer = new FlowDocumentScrollViewer() {
Width = document.MinPageWidth + fontSize * 5,
MaxWidth = MainWindow.Instance.ActualWidth
};
viewer.Document = document;
Border border = new Border {
Background = SystemColors.ControlBrush,
BorderBrush = SystemColors.ControlDarkBrush,
BorderThickness = new Thickness(1),
MaxHeight = 400,
Child = viewer
};
this.Child = border;
viewer.Foreground = SystemColors.InfoTextBrush;
document.FontSize = fontSize;
}
public bool CloseWhenMouseMovesAway {
get { return !this.IsKeyboardFocusWithin; }
}
protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
base.OnLostKeyboardFocus(e);
this.IsOpen = false;
}
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
// When the mouse is over the popup, it is possible for ILSpy to be minimized,
// or moved into the background, and yet the popup stays open.
// We don't have a good method here to check whether the mouse moved back into the text area
// or somewhere else, so we'll just close the popup.
if (CloseWhenMouseMovesAway)
this.IsOpen = false;
}
}
#endregion
@ -336,7 +523,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -336,7 +523,7 @@ namespace ICSharpCode.ILSpy.TextView
return tcs.Task;
}
void cancelButton_Click(object sender, RoutedEventArgs e)
void CancelButton_Click(object sender, RoutedEventArgs e)
{
if (currentCancellationTokenSource != null) {
currentCancellationTokenSource.Cancel();

2
ILSpy/TextView/DecompilerTextView.xaml

@ -21,7 +21,7 @@ @@ -21,7 +21,7 @@
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock FontSize="14pt" Text="{x:Static properties:Resources.Decompiling}"/>
<ProgressBar Name="progressBar" Height="16" Margin="0, 4" />
<Button Click="cancelButton_Click" HorizontalAlignment="Center" Content="{x:Static properties:Resources.Cancel}"/>
<Button Click="CancelButton_Click" HorizontalAlignment="Center" Content="{x:Static properties:Resources.Cancel}"/>
</StackPanel>
</Border>
</Grid>

551
ILSpy/TextView/DocumentationUIBuilder.cs

@ -0,0 +1,551 @@ @@ -0,0 +1,551 @@
// 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;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Xml;
using System.Xml.Linq;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.Decompiler.Documentation;
using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.Options;
namespace ICSharpCode.ILSpy.TextView
{
/// <summary>
/// Renders XML documentation into a WPF <see cref="FlowDocument"/>.
/// </summary>
public class DocumentationUIBuilder
{
readonly IAmbience ambience;
readonly IHighlightingDefinition highlightingDefinition;
readonly FlowDocument document;
BlockCollection blockCollection;
InlineCollection inlineCollection;
public DocumentationUIBuilder(IAmbience ambience, IHighlightingDefinition highlightingDefinition)
{
this.ambience = ambience;
this.highlightingDefinition = highlightingDefinition;
this.document = new FlowDocument();
this.blockCollection = document.Blocks;
this.ShowSummary = true;
this.ShowAllParameters = true;
this.ShowReturns = true;
this.ShowThreadSafety = true;
this.ShowExceptions = true;
this.ShowTypeParameters = true;
this.ShowExample = true;
this.ShowPreliminary = true;
this.ShowSeeAlso = true;
this.ShowValue = true;
this.ShowPermissions = true;
this.ShowRemarks = true;
}
public FlowDocument CreateDocument()
{
FlushAddedText(true);
return document;
}
public bool ShowExceptions { get; set; }
public bool ShowPermissions { get; set; }
public bool ShowExample { get; set; }
public bool ShowPreliminary { get; set; }
public bool ShowRemarks { get; set; }
public bool ShowSummary { get; set; }
public bool ShowReturns { get; set; }
public bool ShowSeeAlso { get; set; }
public bool ShowThreadSafety { get; set; }
public bool ShowTypeParameters { get; set; }
public bool ShowValue { get; set; }
public bool ShowAllParameters { get; set; }
public void AddCodeBlock(string textContent, bool keepLargeMargin = false)
{
var document = new TextDocument(textContent);
var highlighter = new DocumentHighlighter(document, highlightingDefinition);
var richText = DocumentPrinter.ConvertTextDocumentToRichText(document, highlighter).ToRichTextModel();
var block = new Paragraph();
block.Inlines.AddRange(richText.CreateRuns(document));
block.FontFamily = GetCodeFont();
if (!keepLargeMargin)
block.Margin = new Thickness(0, 6, 0, 6);
AddBlock(block);
}
public void AddSignatureBlock(string signature, RichTextModel highlighting = null)
{
var document = new TextDocument(signature);
var richText = highlighting ?? DocumentPrinter.ConvertTextDocumentToRichText(document, new DocumentHighlighter(document, highlightingDefinition)).ToRichTextModel();
var block = new Paragraph();
// HACK: measure width of signature using a TextBlock
// Paragraph sadly does not support TextWrapping.NoWrap
var text = new TextBlock {
FontFamily = GetCodeFont(),
FontSize = DisplaySettingsPanel.CurrentDisplaySettings.SelectedFontSize,
TextAlignment = TextAlignment.Left
};
text.Inlines.AddRange(richText.CreateRuns(document));
text.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
this.document.MinPageWidth = Math.Min(text.DesiredSize.Width, MainWindow.Instance.ActualWidth);
block.Inlines.AddRange(richText.CreateRuns(document));
block.FontFamily = GetCodeFont();
block.TextAlignment = TextAlignment.Left;
AddBlock(block);
}
public void AddXmlDocumentation(string xmlDocumentation, IEntity declaringEntity, Func<string, IEntity> resolver)
{
if (xmlDocumentation == null)
return;
Debug.WriteLine(xmlDocumentation);
var xml = XElement.Parse("<doc>" + xmlDocumentation + "</doc>");
AddDocumentationElement(new XmlDocumentationElement(xml, declaringEntity, resolver));
}
/// <summary>
/// Gets/Sets the name of the parameter that should be shown.
/// </summary>
public string ParameterName { get; set; }
public void AddDocumentationElement(XmlDocumentationElement element)
{
if (element == null)
throw new ArgumentNullException("element");
if (element.IsTextNode) {
AddText(element.TextContent);
return;
}
switch (element.Name) {
case "b":
AddSpan(new Bold(), element.Children);
break;
case "i":
AddSpan(new Italic(), element.Children);
break;
case "c":
AddSpan(new Span { FontFamily = GetCodeFont() }, element.Children);
break;
case "code":
AddCodeBlock(element.TextContent);
break;
case "example":
if (ShowExample)
AddSection("Example: ", element.Children);
break;
case "exception":
if (ShowExceptions)
AddException(element.ReferencedEntity, element.Children);
break;
case "list":
AddList(element.GetAttribute("type"), element.Children);
break;
//case "note":
// throw new NotImplementedException();
case "para":
AddParagraph(new Paragraph { Margin = new Thickness(0, 5, 0, 5) }, element.Children);
break;
case "param":
if (ShowAllParameters || (ParameterName != null && ParameterName == element.GetAttribute("name")))
AddParam(element.GetAttribute("name"), element.Children);
break;
case "paramref":
AddParamRef(element.GetAttribute("name"));
break;
case "permission":
if (ShowPermissions)
AddPermission(element.ReferencedEntity, element.Children);
break;
case "preliminary":
if (ShowPreliminary)
AddPreliminary(element.Children);
break;
case "remarks":
if (ShowRemarks)
AddSection("Remarks: ", element.Children);
break;
case "returns":
if (ShowReturns)
AddSection("Returns: ", element.Children);
break;
case "see":
AddSee(element);
break;
case "seealso":
if (inlineCollection != null)
AddSee(element);
else if (ShowSeeAlso)
AddSection(new Run("See also: "), () => AddSee(element));
break;
case "summary":
if (ShowSummary)
AddSection("Summary: ", element.Children);
break;
case "threadsafety":
if (ShowThreadSafety)
AddThreadSafety(ParseBool(element.GetAttribute("static")), ParseBool(element.GetAttribute("instance")), element.Children);
break;
case "typeparam":
if (ShowTypeParameters)
AddSection("Type parameter " + element.GetAttribute("name") + ": ", element.Children);
break;
case "typeparamref":
AddText(element.GetAttribute("name"));
break;
case "value":
if (ShowValue)
AddSection("Value: ", element.Children);
break;
case "exclude":
case "filterpriority":
case "overloads":
// ignore children
break;
case "br":
AddLineBreak();
break;
default:
foreach (var child in element.Children)
AddDocumentationElement(child);
break;
}
}
void AddList(string type, IEnumerable<XmlDocumentationElement> items)
{
List list = new List();
AddBlock(list);
list.Margin = new Thickness(0, 5, 0, 5);
if (type == "number")
list.MarkerStyle = TextMarkerStyle.Decimal;
else if (type == "bullet")
list.MarkerStyle = TextMarkerStyle.Disc;
var oldBlockCollection = blockCollection;
try {
foreach (var itemElement in items) {
if (itemElement.Name == "listheader" || itemElement.Name == "item") {
ListItem item = new ListItem();
blockCollection = item.Blocks;
inlineCollection = null;
foreach (var prop in itemElement.Children) {
AddDocumentationElement(prop);
}
FlushAddedText(false);
list.ListItems.Add(item);
}
}
} finally {
blockCollection = oldBlockCollection;
}
}
bool? ParseBool(string input)
{
if (bool.TryParse(input, out bool result))
return result;
else
return null;
}
void AddThreadSafety(bool? staticThreadSafe, bool? instanceThreadSafe, IEnumerable<XmlDocumentationElement> children)
{
AddSection(
new Run("Thread-safety: "),
delegate {
if (staticThreadSafe == true)
AddText("Any public static members of this type are thread safe. ");
else if (staticThreadSafe == false)
AddText("The static members of this type are not thread safe. ");
if (instanceThreadSafe == true)
AddText("Any public instance members of this type are thread safe. ");
else if (instanceThreadSafe == false)
AddText("Any instance members are not guaranteed to be thread safe. ");
foreach (var child in children)
AddDocumentationElement(child);
});
}
void AddException(IEntity referencedEntity, IList<XmlDocumentationElement> children)
{
Span span = new Span();
if (referencedEntity != null)
span.Inlines.Add(ConvertReference(referencedEntity));
else
span.Inlines.Add("Exception");
span.Inlines.Add(": ");
AddSection(span, children);
}
void AddPermission(IEntity referencedEntity, IList<XmlDocumentationElement> children)
{
Span span = new Span();
span.Inlines.Add("Permission");
if (referencedEntity != null) {
span.Inlines.Add(" ");
span.Inlines.Add(ConvertReference(referencedEntity));
}
span.Inlines.Add(": ");
AddSection(span, children);
}
Inline ConvertReference(IEntity referencedEntity)
{
var h = new Hyperlink(new Run(ambience.ConvertSymbol(referencedEntity)));
h.Click += (sender, e) => {
MainWindow.Instance.JumpToReference(referencedEntity);
};
return h;
}
void AddParam(string name, IEnumerable<XmlDocumentationElement> children)
{
Span span = new Span();
span.Inlines.Add(new Run(name ?? string.Empty) { FontStyle = FontStyles.Italic });
span.Inlines.Add(": ");
AddSection(span, children);
}
void AddParamRef(string name)
{
if (name != null) {
AddInline(new Run(name) { FontStyle = FontStyles.Italic });
}
}
void AddPreliminary(IEnumerable<XmlDocumentationElement> children)
{
if (children.Any()) {
foreach (var child in children)
AddDocumentationElement(child);
} else {
AddText("[This is preliminary documentation and subject to change.]");
}
}
void AddSee(XmlDocumentationElement element)
{
IEntity referencedEntity = element.ReferencedEntity;
if (referencedEntity != null) {
if (element.Children.Any()) {
Hyperlink link = new Hyperlink();
link.Click += (sender, e) => {
MainWindow.Instance.JumpToReference(referencedEntity);
};
AddSpan(link, element.Children);
} else {
AddInline(ConvertReference(referencedEntity));
}
} else if (element.GetAttribute("langword") != null) {
AddInline(new Run(element.GetAttribute("langword")) { FontFamily = GetCodeFont() });
} else if (element.GetAttribute("href") != null) {
Uri uri;
if (Uri.TryCreate(element.GetAttribute("href"), UriKind.Absolute, out uri)) {
if (element.Children.Any()) {
AddSpan(new Hyperlink { NavigateUri = uri }, element.Children);
} else {
AddInline(new Hyperlink(new Run(element.GetAttribute("href"))) { NavigateUri = uri });
}
}
} else {
// Invalid reference: print the cref value
AddText(element.GetAttribute("cref"));
}
}
static string GetCref(string cref)
{
if (cref == null || cref.Trim().Length==0) {
return "";
}
if (cref.Length < 2) {
return cref;
}
if (cref.Substring(1, 1) == ":") {
return cref.Substring(2, cref.Length - 2);
}
return cref;
}
FontFamily GetCodeFont()
{
return DisplaySettingsPanel.CurrentDisplaySettings.SelectedFont;
}
public void AddInline(Inline inline)
{
FlushAddedText(false);
if (inlineCollection == null) {
var para = new Paragraph();
para.Margin = new Thickness(0, 0, 0, 5);
inlineCollection = para.Inlines;
AddBlock(para);
}
inlineCollection.Add(inline);
ignoreWhitespace = false;
}
void AddSection(string title, IEnumerable<XmlDocumentationElement> children)
{
AddSection(new Run(title), children);
}
void AddSection(Inline title, IEnumerable<XmlDocumentationElement> children)
{
AddSection(
title, delegate {
foreach (var child in children)
AddDocumentationElement(child);
});
}
void AddSection(Inline title, Action addChildren)
{
var section = new Section();
AddBlock(section);
var oldBlockCollection = blockCollection;
try {
blockCollection = section.Blocks;
inlineCollection = null;
if (title != null)
AddInline(new Bold(title));
addChildren();
FlushAddedText(false);
} finally {
blockCollection = oldBlockCollection;
inlineCollection = null;
}
}
void AddParagraph(Paragraph para, IEnumerable<XmlDocumentationElement> children)
{
AddBlock(para);
try {
inlineCollection = para.Inlines;
foreach (var child in children)
AddDocumentationElement(child);
FlushAddedText(false);
} finally {
inlineCollection = null;
}
}
void AddSpan(Span span, IEnumerable<XmlDocumentationElement> children)
{
AddInline(span);
var oldInlineCollection = inlineCollection;
try {
inlineCollection = span.Inlines;
foreach (var child in children)
AddDocumentationElement(child);
FlushAddedText(false);
} finally {
inlineCollection = oldInlineCollection;
}
}
public void AddBlock(Block block)
{
FlushAddedText(true);
blockCollection.Add(block);
}
StringBuilder addedText = new StringBuilder();
bool ignoreWhitespace;
public void AddLineBreak()
{
TrimEndOfAddedText();
addedText.AppendLine();
ignoreWhitespace = true;
}
public void AddText(string textContent)
{
if (string.IsNullOrEmpty(textContent))
return;
for (int i = 0; i < textContent.Length; i++) {
char c = textContent[i];
if (c == '\n' && IsEmptyLineBefore(textContent, i)) {
AddLineBreak(); // empty line -> line break
} else if (char.IsWhiteSpace(c)) {
// any whitespace sequence gets converted to a single space (like HTML)
if (!ignoreWhitespace) {
addedText.Append(' ');
ignoreWhitespace = true;
}
} else {
addedText.Append(c);
ignoreWhitespace = false;
}
}
}
bool IsEmptyLineBefore(string text, int i)
{
// Skip previous whitespace
do {
i--;
} while (i >= 0 && (text[i] == ' ' || text[i] == '\r'));
// Check if previous non-whitespace char is \n
return i >= 0 && text[i] == '\n';
}
void TrimEndOfAddedText()
{
while (addedText.Length > 0 && addedText[addedText.Length - 1] == ' ') {
addedText.Length--;
}
}
void FlushAddedText(bool trim)
{
if (trim) // trim end of current text element
TrimEndOfAddedText();
if (addedText.Length == 0)
return;
string text = addedText.ToString();
addedText.Length = 0;
AddInline(new Run(text));
ignoreWhitespace = trim; // trim start of next text element
}
}
}

137
ILSpy/TextView/XmlDocRenderer.cs

@ -1,137 +0,0 @@ @@ -1,137 +0,0 @@
// 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;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Controls;
using System.Xml;
namespace ICSharpCode.ILSpy.TextView
{
/// <summary>
/// Renders XML documentation into a WPF <see cref="TextBlock"/>.
/// </summary>
public class XmlDocRenderer
{
readonly StringBuilder ret = new StringBuilder();
public void AppendText(string text)
{
ret.Append(text);
}
public void AddXmlDocumentation(string xmlDocumentation)
{
if (xmlDocumentation == null)
return;
Debug.WriteLine(xmlDocumentation);
try {
XmlTextReader r = new XmlTextReader(new StringReader("<docroot>" + xmlDocumentation + "</docroot>"));
r.XmlResolver = null;
AddXmlDocumentation(r);
} catch (XmlException) {
}
}
static readonly Regex whitespace = new Regex(@"\s+");
public void AddXmlDocumentation(XmlReader xml)
{
while (xml.Read()) {
if (xml.NodeType == XmlNodeType.Element) {
string elname = xml.Name.ToLowerInvariant();
switch (elname) {
case "filterpriority":
case "remarks":
xml.Skip();
break;
case "example":
ret.Append(Environment.NewLine);
ret.Append("Example:");
ret.Append(Environment.NewLine);
break;
case "exception":
ret.Append(Environment.NewLine);
ret.Append(GetCref(xml["cref"]));
ret.Append(": ");
break;
case "returns":
ret.Append(Environment.NewLine);
ret.Append("Returns: ");
break;
case "see":
ret.Append(GetCref(xml["cref"]));
ret.Append(xml["langword"]);
break;
case "seealso":
ret.Append(Environment.NewLine);
ret.Append("See also: ");
ret.Append(GetCref(xml["cref"]));
break;
case "paramref":
ret.Append(xml["name"]);
break;
case "param":
ret.Append(Environment.NewLine);
ret.Append(whitespace.Replace(xml["name"].Trim()," "));
ret.Append(": ");
break;
case "typeparam":
ret.Append(Environment.NewLine);
ret.Append(whitespace.Replace(xml["name"].Trim()," "));
ret.Append(": ");
break;
case "value":
ret.Append(Environment.NewLine);
ret.Append("Value: ");
ret.Append(Environment.NewLine);
break;
case "br":
case "para":
ret.Append(Environment.NewLine);
break;
}
} else if (xml.NodeType == XmlNodeType.Text) {
ret.Append(whitespace.Replace(xml.Value, " "));
}
}
}
static string GetCref(string cref)
{
if (cref == null || cref.Trim().Length==0) {
return "";
}
if (cref.Length < 2) {
return cref;
}
if (cref.Substring(1, 1) == ":") {
return cref.Substring(2, cref.Length - 2);
}
return cref;
}
public TextBlock CreateTextBlock()
{
return new TextBlock { Text = ret.ToString() };
}
}
}

6
README.md

@ -49,7 +49,7 @@ How to build @@ -49,7 +49,7 @@ 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 SDK (ILSpy.csproj targets .NET 4.7.2, and ILSpy.sln uses SDK-style projects).
- 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).
- 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,9 +60,11 @@ Windows: @@ -60,9 +60,11 @@ 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).
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 SDK](https://dotnet.microsoft.com/download/dotnet-core) is installed.
- Make sure [.NET Core 3.0 SDK](https://dotnet.microsoft.com/download/dotnet-core/3.0) 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).

8
SharpTreeView/EditTextBox.cs

@ -49,12 +49,12 @@ namespace ICSharpCode.TreeView @@ -49,12 +49,12 @@ namespace ICSharpCode.TreeView
}
}
bool commiting;
bool committing;
void Commit()
{
if (!commiting) {
commiting = true;
if (!committing) {
committing = true;
Node.IsEditing = false;
if (!Node.SaveEditText(Text)) {
@ -70,7 +70,7 @@ namespace ICSharpCode.TreeView @@ -70,7 +70,7 @@ namespace ICSharpCode.TreeView
// Init();
//}
commiting = false;
committing = false;
}
}
}

Loading…
Cancel
Save