Browse Source

Merge pull request #2191 from icsharpcode/nuget-tree-node-attempt

Open NuGet packages and .NET bundles in tree view
pull/2203/head
Daniel Grunwald 5 years ago committed by GitHub
parent
commit
b94a39e931
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs
  2. 4
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/IProjectInfoProvider.cs
  3. 2
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/ProjectFileWriterDefault.cs
  4. 4
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/ProjectFileWriterSdkStyle.cs
  5. 9
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs
  6. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  7. 24
      ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs
  8. 49
      ICSharpCode.Decompiler/Metadata/Dom.cs
  9. 2
      ICSharpCode.Decompiler/Metadata/PEFile.cs
  10. 9
      ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs
  11. 160
      ICSharpCode.Decompiler/SingleFileBundle.cs
  12. 8
      ILSpy.BamlDecompiler/BamlResourceEntryNode.cs
  13. 9
      ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs
  14. 11
      ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj
  15. 27
      ILSpy/AssemblyList.cs
  16. 21
      ILSpy/ILSpy.csproj
  17. 4
      ILSpy/Images/Images.cs
  18. BIN
      ILSpy/Images/NuGet.png
  19. 6
      ILSpy/Languages/CSharpLanguage.cs
  20. 215
      ILSpy/LoadedAssembly.cs
  21. 121
      ILSpy/LoadedNugetPackage.cs
  22. 281
      ILSpy/LoadedPackage.cs
  23. 65
      ILSpy/MainWindow.xaml.cs
  24. 4
      ILSpy/Properties/AssemblyInfo.template.cs
  25. 4
      ILSpy/Properties/app.config.template
  26. 58
      ILSpy/TreeNodes/AssemblyListTreeNode.cs
  27. 23
      ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs
  28. 105
      ILSpy/TreeNodes/AssemblyTreeNode.cs
  29. 92
      ILSpy/TreeNodes/PackageFolderTreeNode.cs
  30. 41
      ILSpy/TreeNodes/ResourceNodes/CursorResourceEntryNode.cs
  31. 1
      ILSpy/TreeNodes/ResourceNodes/IResourceNodeFactory.cs
  32. 26
      ILSpy/TreeNodes/ResourceNodes/IconResourceEntryNode.cs
  33. 5
      ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs
  34. 26
      ILSpy/TreeNodes/ResourceNodes/ImageResourceEntryNode.cs
  35. 99
      ILSpy/TreeNodes/ResourceNodes/JsonResourceNode.cs
  36. 40
      ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs
  37. 12
      ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs
  38. 9
      ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs
  39. 98
      ILSpy/TreeNodes/ResourceNodes/TextResourceNode.cs
  40. 20
      ILSpy/TreeNodes/ResourceNodes/XamlResourceNode.cs
  41. 24
      ILSpy/TreeNodes/ResourceNodes/XmlResourceNode.cs
  42. 40
      ILSpy/Views/NugetPackageBrowserDialog.xaml
  43. 86
      ILSpy/Views/NugetPackageBrowserDialog.xaml.cs
  44. 5
      packages.props

7
ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs

@ -30,7 +30,6 @@ using ICSharpCode.Decompiler.Metadata; @@ -30,7 +30,6 @@ using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Tests.Helpers;
using Microsoft.Build.Locator;
using Microsoft.Win32;
using NUnit.Framework;
@ -199,7 +198,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -199,7 +198,7 @@ namespace ICSharpCode.Decompiler.Tests
settings.UseSdkStyleProjectFormat = false;
}
var decompiler = new TestProjectDecompiler(projectGuid, resolver, settings);
var decompiler = new TestProjectDecompiler(projectGuid, resolver, resolver, settings);
if (snkFilePath != null)
{
@ -318,8 +317,8 @@ namespace ICSharpCode.Decompiler.Tests @@ -318,8 +317,8 @@ namespace ICSharpCode.Decompiler.Tests
class TestProjectDecompiler : WholeProjectDecompiler
{
public TestProjectDecompiler(Guid projecGuid, IAssemblyResolver resolver, DecompilerSettings settings)
: base(settings, projecGuid, resolver, debugInfoProvider: null)
public TestProjectDecompiler(Guid projecGuid, IAssemblyResolver resolver, AssemblyReferenceClassifier assemblyReferenceClassifier, DecompilerSettings settings)
: base(settings, projecGuid, resolver, assemblyReferenceClassifier, debugInfoProvider: null)
{
}
}

4
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/IProjectInfoProvider.cs

@ -32,6 +32,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -32,6 +32,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
/// </summary>
IAssemblyResolver AssemblyResolver { get; }
AssemblyReferenceClassifier AssemblyReferenceClassifier { get; }
/// <summary>
/// Gets the C# language version of the project.
/// </summary>
@ -52,4 +54,4 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -52,4 +54,4 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
/// </summary>
string StrongNameKeyFile { get; }
}
}
}

2
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/ProjectFileWriterDefault.cs

@ -153,7 +153,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -153,7 +153,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
w.WriteStartElement("Reference");
w.WriteAttributeString("Include", r.Name);
var asm = project.AssemblyResolver.Resolve(r);
if (asm != null && !project.AssemblyResolver.IsGacAssembly(r))
if (asm != null && !project.AssemblyReferenceClassifier.IsGacAssembly(r))
{
w.WriteElementString("HintPath", asm.FileName);
}

4
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/ProjectFileWriterSdkStyle.cs

@ -256,7 +256,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -256,7 +256,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
foreach (var reference in module.AssemblyReferences.Where(r => !ImplicitReferences.Contains(r.Name)))
{
if (isNetCoreApp && project.AssemblyResolver.IsSharedAssembly(reference, out string runtimePack) && targetPacks.Contains(runtimePack))
if (isNetCoreApp && project.AssemblyReferenceClassifier.IsSharedAssembly(reference, out string runtimePack) && targetPacks.Contains(runtimePack))
{
continue;
}
@ -265,7 +265,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -265,7 +265,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
xml.WriteAttributeString("Include", reference.Name);
var asembly = project.AssemblyResolver.Resolve(reference);
if (asembly != null && !project.AssemblyResolver.IsGacAssembly(reference))
if (asembly != null && !project.AssemblyReferenceClassifier.IsGacAssembly(reference))
{
xml.WriteElementString("HintPath", FileUtility.GetRelativePath(project.TargetDirectory, asembly.FileName));
}

9
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs

@ -66,6 +66,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -66,6 +66,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
public IAssemblyResolver AssemblyResolver { get; }
public AssemblyReferenceClassifier AssemblyReferenceClassifier { get; }
public IDebugInfoProvider DebugInfoProvider { get; }
/// <summary>
@ -94,15 +96,16 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -94,15 +96,16 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
#endregion
public WholeProjectDecompiler(IAssemblyResolver assemblyResolver)
: this(new DecompilerSettings(), assemblyResolver, debugInfoProvider: null)
: this(new DecompilerSettings(), assemblyResolver, assemblyReferenceClassifier: null, debugInfoProvider: null)
{
}
public WholeProjectDecompiler(
DecompilerSettings settings,
IAssemblyResolver assemblyResolver,
AssemblyReferenceClassifier assemblyReferenceClassifier,
IDebugInfoProvider debugInfoProvider)
: this(settings, Guid.NewGuid(), assemblyResolver, debugInfoProvider)
: this(settings, Guid.NewGuid(), assemblyResolver, assemblyReferenceClassifier, debugInfoProvider)
{
}
@ -110,11 +113,13 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -110,11 +113,13 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
DecompilerSettings settings,
Guid projectGuid,
IAssemblyResolver assemblyResolver,
AssemblyReferenceClassifier assemblyReferenceClassifier,
IDebugInfoProvider debugInfoProvider)
{
Settings = settings ?? throw new ArgumentNullException(nameof(settings));
ProjectGuid = projectGuid;
AssemblyResolver = assemblyResolver ?? throw new ArgumentNullException(nameof(assemblyResolver));
AssemblyReferenceClassifier = assemblyReferenceClassifier ?? new AssemblyReferenceClassifier();
DebugInfoProvider = debugInfoProvider;
projectWriter = Settings.UseSdkStyleProjectFormat ? ProjectFileWriterSdkStyle.Create() : ProjectFileWriterDefault.Create();
}

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -88,6 +88,7 @@ @@ -88,6 +88,7 @@
<Compile Include="IL\ControlFlow\AwaitInFinallyTransform.cs" />
<Compile Include="IL\Transforms\IntroduceNativeIntTypeOnLocals.cs" />
<Compile Include="Semantics\OutVarResolveResult.cs" />
<Compile Include="SingleFileBundle.cs" />
<Compile Include="Solution\ProjectId.cs" />
<Compile Include="Solution\ProjectItem.cs" />
<Compile Include="Solution\SolutionCreator.cs" />

24
ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs

@ -48,8 +48,28 @@ namespace ICSharpCode.Decompiler.Metadata @@ -48,8 +48,28 @@ namespace ICSharpCode.Decompiler.Metadata
PEFile Resolve(IAssemblyReference reference);
PEFile ResolveModule(PEFile mainModule, string moduleName);
#endif
bool IsGacAssembly(IAssemblyReference reference);
bool IsSharedAssembly(IAssemblyReference reference, out string runtimePack);
}
public class AssemblyReferenceClassifier
{
/// <summary>
/// For GAC assembly references, the WholeProjectDecompiler will omit the HintPath in the
/// generated .csproj file.
/// </summary>
public virtual bool IsGacAssembly(IAssemblyReference reference)
{
return UniversalAssemblyResolver.GetAssemblyInGac(reference) != null;
}
/// <summary>
/// For .NET Core framework references, the WholeProjectDecompiler will omit the
/// assembly reference if the runtimePack is already included as an SDK.
/// </summary>
public virtual bool IsSharedAssembly(IAssemblyReference reference, out string runtimePack)
{
runtimePack = null;
return false;
}
}
public interface IAssemblyReference

49
ICSharpCode.Decompiler/Metadata/Dom.cs

@ -1,16 +1,11 @@ @@ -1,16 +1,11 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Security.Cryptography;
using System.Text;
using ICSharpCode.Decompiler.Documentation;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
@ -32,13 +27,38 @@ namespace ICSharpCode.Decompiler.Metadata @@ -32,13 +27,38 @@ namespace ICSharpCode.Decompiler.Metadata
AssemblyLinked,
}
public struct Resource : IEquatable<Resource>
public abstract class Resource
{
public virtual ResourceType ResourceType => ResourceType.Embedded;
public virtual ManifestResourceAttributes Attributes => ManifestResourceAttributes.Public;
public abstract string Name { get; }
public abstract Stream TryOpenStream();
}
public class ByteArrayResource : Resource
{
public override string Name { get; }
byte[] data;
public ByteArrayResource(string name, byte[] data)
{
this.Name = name ?? throw new ArgumentNullException(nameof(name));
this.data = data ?? throw new ArgumentNullException(nameof(data));
}
public override Stream TryOpenStream()
{
return new MemoryStream(data);
}
}
sealed class MetadataResource : Resource
{
public PEFile Module { get; }
public ManifestResourceHandle Handle { get; }
public bool IsNil => Handle.IsNil;
public Resource(PEFile module, ManifestResourceHandle handle) : this()
public MetadataResource(PEFile module, ManifestResourceHandle handle)
{
this.Module = module ?? throw new ArgumentNullException(nameof(module));
this.Handle = handle;
@ -46,14 +66,14 @@ namespace ICSharpCode.Decompiler.Metadata @@ -46,14 +66,14 @@ namespace ICSharpCode.Decompiler.Metadata
ManifestResource This() => Module.Metadata.GetManifestResource(Handle);
public bool Equals(Resource other)
public bool Equals(MetadataResource other)
{
return Module == other.Module && Handle == other.Handle;
}
public override bool Equals(object obj)
{
if (obj is Resource res)
if (obj is MetadataResource res)
return Equals(res);
return false;
}
@ -63,14 +83,11 @@ namespace ICSharpCode.Decompiler.Metadata @@ -63,14 +83,11 @@ namespace ICSharpCode.Decompiler.Metadata
return unchecked(982451629 * Module.GetHashCode() + 982451653 * MetadataTokens.GetToken(Handle));
}
public static bool operator ==(Resource lhs, Resource rhs) => lhs.Equals(rhs);
public static bool operator !=(Resource lhs, Resource rhs) => !lhs.Equals(rhs);
public string Name => Module.Metadata.GetString(This().Name);
public override string Name => Module.Metadata.GetString(This().Name);
public ManifestResourceAttributes Attributes => This().Attributes;
public override ManifestResourceAttributes Attributes => This().Attributes;
public bool HasFlag(ManifestResourceAttributes flag) => (Attributes & flag) == flag;
public ResourceType ResourceType => GetResourceType();
public override ResourceType ResourceType => GetResourceType();
ResourceType GetResourceType()
{
@ -81,7 +98,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -81,7 +98,7 @@ namespace ICSharpCode.Decompiler.Metadata
return ResourceType.Linked;
}
public unsafe Stream TryOpenStream()
public override unsafe Stream TryOpenStream()
{
if (ResourceType != ResourceType.Embedded)
return null;

2
ICSharpCode.Decompiler/Metadata/PEFile.cs

@ -112,7 +112,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -112,7 +112,7 @@ namespace ICSharpCode.Decompiler.Metadata
var metadata = Metadata;
foreach (var h in metadata.ManifestResources)
{
yield return new Resource(this, h);
yield return new MetadataResource(this, h);
}
}

9
ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs

@ -43,7 +43,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -43,7 +43,7 @@ namespace ICSharpCode.Decompiler.Metadata
}
// This is inspired by Mono.Cecil's BaseAssemblyResolver/DefaultAssemblyResolver.
public class UniversalAssemblyResolver : IAssemblyResolver
public class UniversalAssemblyResolver : AssemblyReferenceClassifier, IAssemblyResolver
{
static UniversalAssemblyResolver()
{
@ -184,12 +184,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -184,12 +184,7 @@ namespace ICSharpCode.Decompiler.Metadata
}
#endif
public virtual bool IsGacAssembly(IAssemblyReference reference)
{
return GetAssemblyInGac(reference) != null;
}
public virtual bool IsSharedAssembly(IAssemblyReference reference, out string runtimePack)
public override bool IsSharedAssembly(IAssemblyReference reference, out string runtimePack)
{
return dotNetCorePathFinder.TryResolveDotNetCoreShared(reference, out runtimePack) != null;
}

160
ICSharpCode.Decompiler/SingleFileBundle.cs

@ -0,0 +1,160 @@ @@ -0,0 +1,160 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Immutable;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Text;
namespace ICSharpCode.Decompiler
{
/// <summary>
/// Class for dealing with .NET 5 single-file bundles.
///
/// Based on code from Microsoft.NET.HostModel.
/// </summary>
public static class SingleFileBundle
{
/// <summary>
/// Check if the memory-mapped data is a single-file bundle
/// </summary>
public static unsafe bool IsBundle(MemoryMappedViewAccessor view, out long bundleHeaderOffset)
{
var buffer = view.SafeMemoryMappedViewHandle;
byte* ptr = null;
buffer.AcquirePointer(ref ptr);
try
{
return IsBundle(ptr, checked((long)buffer.ByteLength), out bundleHeaderOffset);
}
finally
{
buffer.ReleasePointer();
}
}
public static unsafe bool IsBundle(byte* data, long size, out long bundleHeaderOffset)
{
ReadOnlySpan<byte> bundleSignature = new byte[] {
// 32 bytes represent the bundle signature: SHA-256 for ".net core bundle"
0x8b, 0x12, 0x02, 0xb9, 0x6a, 0x61, 0x20, 0x38,
0x72, 0x7b, 0x93, 0x02, 0x14, 0xd7, 0xa0, 0x32,
0x13, 0xf5, 0xb9, 0xe6, 0xef, 0xae, 0x33, 0x18,
0xee, 0x3b, 0x2d, 0xce, 0x24, 0xb3, 0x6a, 0xae
};
byte* end = data + (size - bundleSignature.Length);
for (byte* ptr = data; ptr < end; ptr++)
{
if (*ptr == 0x8b && bundleSignature.SequenceEqual(new ReadOnlySpan<byte>(ptr, bundleSignature.Length)))
{
bundleHeaderOffset = *(long*)(ptr - sizeof(long));
return true;
}
}
bundleHeaderOffset = 0;
return false;
}
public struct Header
{
public uint MajorVersion;
public uint MinorVersion;
public int FileCount;
public string BundleID;
// Fields introduced with v2:
public long DepsJsonOffset;
public long DepsJsonSize;
public long RuntimeConfigJsonOffset;
public long RuntimeConfigJsonSize;
public ulong Flags;
public ImmutableArray<Entry> Entries;
}
/// <summary>
/// FileType: Identifies the type of file embedded into the bundle.
///
/// The bundler differentiates a few kinds of files via the manifest,
/// with respect to the way in which they'll be used by the runtime.
/// </summary>
public enum FileType : byte
{
Unknown, // Type not determined.
Assembly, // IL and R2R Assemblies
NativeBinary, // NativeBinaries
DepsJson, // .deps.json configuration file
RuntimeConfigJson, // .runtimeconfig.json configuration file
Symbols // PDB Files
};
public struct Entry
{
public long Offset;
public long Size;
public FileType Type;
public string RelativePath; // Path of an embedded file, relative to the Bundle source-directory.
}
static UnmanagedMemoryStream AsStream(MemoryMappedViewAccessor view)
{
long size = checked((long)view.SafeMemoryMappedViewHandle.ByteLength);
return new UnmanagedMemoryStream(view.SafeMemoryMappedViewHandle, 0, size);
}
/// <summary>
/// Reads the manifest header from the memory mapping.
/// </summary>
public static Header ReadManifest(MemoryMappedViewAccessor view, long bundleHeaderOffset)
{
using var stream = AsStream(view);
stream.Seek(bundleHeaderOffset, SeekOrigin.Begin);
return ReadManifest(stream);
}
/// <summary>
/// Reads the manifest header from the stream.
/// </summary>
public static Header ReadManifest(Stream stream)
{
var header = new Header();
using var reader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true);
header.MajorVersion = reader.ReadUInt32();
header.MinorVersion = reader.ReadUInt32();
if (header.MajorVersion < 1 || header.MajorVersion > 2)
{
throw new InvalidDataException($"Unsupported manifest version: {header.MajorVersion}.{header.MinorVersion}");
}
header.FileCount = reader.ReadInt32();
header.BundleID = reader.ReadString();
if (header.MajorVersion >= 2)
{
header.DepsJsonOffset = reader.ReadInt64();
header.DepsJsonSize = reader.ReadInt64();
header.RuntimeConfigJsonOffset = reader.ReadInt64();
header.RuntimeConfigJsonSize = reader.ReadInt64();
header.Flags = reader.ReadUInt64();
}
var entries = ImmutableArray.CreateBuilder<Entry>(header.FileCount);
for (int i = 0; i < header.FileCount; i++)
{
entries.Add(ReadEntry(reader));
}
header.Entries = entries.MoveToImmutable();
return header;
}
private static Entry ReadEntry(BinaryReader reader)
{
Entry entry;
entry.Offset = reader.ReadInt64();
entry.Size = reader.ReadInt64();
entry.Type = (FileType)reader.ReadByte();
entry.RelativePath = reader.ReadString();
return entry;
}
}
}

8
ILSpy.BamlDecompiler/BamlResourceEntryNode.cs

@ -42,7 +42,7 @@ namespace ILSpy.BamlDecompiler @@ -42,7 +42,7 @@ namespace ILSpy.BamlDecompiler
{
public sealed class BamlResourceEntryNode : ResourceEntryNode
{
public BamlResourceEntryNode(string key, Stream data) : base(key, data)
public BamlResourceEntryNode(string key, Func<Stream> data) : base(key, data)
{
}
@ -74,8 +74,8 @@ namespace ILSpy.BamlDecompiler @@ -74,8 +74,8 @@ namespace ILSpy.BamlDecompiler
bool LoadBaml(AvalonEditTextOutput output, CancellationToken cancellationToken)
{
var asm = this.Ancestors().OfType<AssemblyTreeNode>().FirstOrDefault().LoadedAssembly;
Data.Position = 0;
XDocument xamlDocument = LoadIntoDocument(asm.GetPEFileOrNull(), asm.GetAssemblyResolver(), Data, cancellationToken);
using var data = OpenStream();
XDocument xamlDocument = LoadIntoDocument(asm.GetPEFileOrNull(), asm.GetAssemblyResolver(), data, cancellationToken);
output.Write(xamlDocument.ToString());
return true;
}
@ -203,4 +203,4 @@ namespace ILSpy.BamlDecompiler @@ -203,4 +203,4 @@ namespace ILSpy.BamlDecompiler
public new MetadataModule MainModule { get; }
}
}
}
}

9
ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs

@ -31,13 +31,8 @@ namespace ILSpy.BamlDecompiler @@ -31,13 +31,8 @@ namespace ILSpy.BamlDecompiler
{
public ILSpyTreeNode CreateNode(Resource resource)
{
return null;
}
public ILSpyTreeNode CreateNode(string key, object data)
{
if (key.EndsWith(".baml", StringComparison.OrdinalIgnoreCase) && data is Stream stream)
return new BamlResourceEntryNode(key, stream);
if (resource.Name.EndsWith(".baml", StringComparison.OrdinalIgnoreCase))
return new BamlResourceEntryNode(resource.Name, resource.TryOpenStream);
else
return null;
}

11
ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj

@ -73,9 +73,18 @@ @@ -73,9 +73,18 @@
<Page Include="ReadyToRunOptionPage.xaml" />
</ItemGroup>
<Import Project="../packages.props"/>
<ItemGroup>
<PackageReference Include="Iced" Version="1.8.0" />
<PackageReference Include="ILCompiler.Reflection.ReadyToRun" Version="1.0.11-alpha" />
<!-- ILCompiler.Reflection.ReadyToRun has dependencies on System.Reflection.Metadata and
System.Runtime.CompilerServices.Unsafe. Because the AddIn compiles into ILSpy's output
directory, we're at risk of overwriting our dependencies with different versions.
So ensure NuGet uses consistent versions (from our packages.props) for these.
-->
<PackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataVersion)" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemCompilerServicesUnsafeVersion)" />
</ItemGroup>
<Target Name="RemoveTransitiveProjectReferences" AfterTargets="IncludeTransitiveProjectReferences">
@ -84,4 +93,4 @@ @@ -84,4 +93,4 @@
</ItemGroup>
</Target>
</Project>
</Project>

27
ILSpy/AssemblyList.cs

@ -163,28 +163,7 @@ namespace ICSharpCode.ILSpy @@ -163,28 +163,7 @@ namespace ICSharpCode.ILSpy
public LoadedAssembly Open(string assemblyUri, bool isAutoLoaded = false)
{
if (assemblyUri.StartsWith("nupkg://", StringComparison.OrdinalIgnoreCase))
{
string fileName = assemblyUri.Substring("nupkg://".Length);
int separator = fileName.LastIndexOf(';');
string componentName = null;
if (separator > -1)
{
componentName = fileName.Substring(separator + 1);
fileName = fileName.Substring(0, separator);
LoadedNugetPackage package = new LoadedNugetPackage(fileName);
var entry = package.Entries.FirstOrDefault(e => e.Name == componentName);
if (entry != null)
{
return OpenAssembly(assemblyUri, entry.Stream, true);
}
}
return null;
}
else
{
return OpenAssembly(assemblyUri, isAutoLoaded);
}
return OpenAssembly(assemblyUri, isAutoLoaded);
}
/// <summary>
@ -225,7 +204,7 @@ namespace ICSharpCode.ILSpy @@ -225,7 +204,7 @@ namespace ICSharpCode.ILSpy
return asm;
}
var newAsm = new LoadedAssembly(this, file, stream);
var newAsm = new LoadedAssembly(this, file, stream: Task.FromResult(stream));
newAsm.IsAutoLoaded = isAutoLoaded;
lock (assemblies)
{
@ -248,7 +227,7 @@ namespace ICSharpCode.ILSpy @@ -248,7 +227,7 @@ namespace ICSharpCode.ILSpy
return null;
var index = this.assemblies.IndexOf(target);
var newAsm = new LoadedAssembly(this, file, stream);
var newAsm = new LoadedAssembly(this, file, stream: Task.FromResult(stream));
newAsm.IsAutoLoaded = target.IsAutoLoaded;
lock (assemblies)
{

21
ILSpy/ILSpy.csproj

@ -52,6 +52,8 @@ @@ -52,6 +52,8 @@
<ItemGroup>
<PackageReference Include="AvalonEdit" Version="$(AvalonEditVersion)" />
<PackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataVersion)" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemCompilerServicesUnsafeVersion)" />
<PackageReference Include="Dirkster.AvalonDock" Version="3.6.1" />
<PackageReference Include="Microsoft.VisualStudio.Composition" Version="16.3.7" />
<PackageReference Include="System.Composition" Version="1.3.0" />
@ -69,7 +71,7 @@ @@ -69,7 +71,7 @@
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
<PackageReference Include="Microsoft.DiaSymReader.Converter.Xml" Version="1.1.0-beta1-63314-01" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml" />
</ItemGroup>
@ -213,6 +215,7 @@ @@ -213,6 +215,7 @@
<Compile Include="Metadata\MetadataProtocolHandler.cs" />
<Compile Include="Metadata\MetadataTableTreeNode.cs" />
<Compile Include="Metadata\DebugMetadataTreeNode.cs" />
<Compile Include="TreeNodes\PackageFolderTreeNode.cs" />
<Compile Include="ViewModels\LegacyToolPaneModel.cs" />
<Compile Include="ViewModels\ManageAssemblyListsViewModel.cs" />
<Compile Include="ViewModels\ViewModelBase.cs" />
@ -382,7 +385,7 @@ @@ -382,7 +385,7 @@
<Compile Include="Search\LiteralSearchStrategy.cs" />
<Compile Include="LoadedAssembly.cs" />
<Compile Include="LoadedAssemblyExtensions.cs" />
<Compile Include="LoadedNugetPackage.cs" />
<Compile Include="LoadedPackage.cs" />
<Compile Include="Search\MemberSearchStrategy.cs" />
<Compile Include="Search\MetadataTokenSearchStrategy.cs" />
<Compile Include="NativeMethods.cs" />
@ -390,9 +393,6 @@ @@ -390,9 +393,6 @@
<Compile Include="NavigationState.cs" />
<Compile Include="Commands\OpenCommand.cs" />
<Compile Include="Commands\OpenFromGacCommand.cs" />
<Compile Include="Views\NugetPackageBrowserDialog.xaml.cs">
<DependentUpon>NugetPackageBrowserDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Views\OpenFromGacDialog.xaml.cs">
<DependentUpon>OpenFromGacDialog.xaml</DependentUpon>
</Compile>
@ -581,7 +581,6 @@ @@ -581,7 +581,6 @@
<Page Include="Metadata\MetadataTableViews.xaml" />
<Page Include="Views\OpenFromGacDialog.xaml" />
<Page Include="Views\ManageAssemblyListsDialog.xaml" />
<Page Include="Views\NugetPackageBrowserDialog.xaml" />
<Page Include="Options\DecompilerSettingsPanel.xaml" />
<Page Include="Options\DisplaySettingsPanel.xaml" />
<Page Include="Options\MiscSettingsPanel.xaml" />
@ -840,8 +839,8 @@ @@ -840,8 +839,8 @@
<VCToolsVersionPropsFileNameDefault>Microsoft.VCToolsVersion.default.props</VCToolsVersionPropsFileNameDefault>
<!-- This is necessary as long as AppVeyor does not offer Visual Studio 2019 RC or later -->
<VCToolsVersionPropsFileNameVS2019Preview>Microsoft.VCToolsVersion.v142.default.props</VCToolsVersionPropsFileNameVS2019Preview>
<VCToolsVersionPropsFile>$(VCBasePath)Auxiliary\Build\$(VCToolsVersionPropsFileNameDefault)</VCToolsVersionPropsFile>
<VCToolsVersionPropsFile Condition="!Exists('$(VCToolsVersionPropsFile)')">$(VCBasePath)Auxiliary\Build\$(VCToolsVersionPropsFileNameVS2019Preview)</VCToolsVersionPropsFile>
<VCToolsVersionPropsFile>$(VCBasePath)Auxiliary\Build\$(VCToolsVersionPropsFileNameDefault)</VCToolsVersionPropsFile>
<VCToolsVersionPropsFile Condition="!Exists('$(VCToolsVersionPropsFile)')">$(VCBasePath)Auxiliary\Build\$(VCToolsVersionPropsFileNameVS2019Preview)</VCToolsVersionPropsFile>
</PropertyGroup>
<Import Project="$(VCToolsVersionPropsFile)" Condition="Exists('$(VCToolsVersionPropsFile)')" />
@ -853,6 +852,10 @@ @@ -853,6 +852,10 @@
<SortResXStamp Include="obj\sort-resx.stamp" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\NuGet.png" />
</ItemGroup>
<Target Name="SortResX" BeforeTargets="BeforeBuild" Inputs="@(SortResXInput)" Outputs="@(SortResXStamp)">
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT' ">
<SortResX>powershell -NoProfile -ExecutionPolicy Bypass -File BuildTools/sort-resx.ps1</SortResX>
@ -865,4 +868,4 @@ @@ -865,4 +868,4 @@
<Exec Condition="'$(VCToolsVersion)'!=''" Command="&quot;$(VCBasePath)Tools\MSVC\$(VCToolsVersion)\bin\Hostx64\x64\editbin.exe&quot; /stack:16777216 &quot;$(TargetPath)&quot;&#xD;&#xA;EXIT 0" />
<Exec Command="&quot;$(TargetFrameworkSDKToolsDirectory)sn.exe&quot; -R &quot;$(TargetPath)&quot; &quot;$(SolutionDir)\ICSharpCode.Decompiler\ICSharpCode.Decompiler.snk&quot;" />
</Target>
</Project>
</Project>

4
ILSpy/Images/Images.cs

@ -20,11 +20,8 @@ using System; @@ -20,11 +20,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace ICSharpCode.ILSpy
{
@ -50,6 +47,7 @@ namespace ICSharpCode.ILSpy @@ -50,6 +47,7 @@ namespace ICSharpCode.ILSpy
public static readonly ImageSource Namespace = Load("Namespace");
public static readonly ImageSource ReferenceFolder = Load("ReferenceFolder");
public static readonly ImageSource NuGet = Load(null, "Images/NuGet.png");
public static readonly ImageSource SubTypes = Load("SubTypes");
public static readonly ImageSource SuperTypes = Load("SuperTypes");

BIN
ILSpy/Images/NuGet.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

6
ILSpy/Languages/CSharpLanguage.cs

@ -386,6 +386,10 @@ namespace ICSharpCode.ILSpy @@ -386,6 +386,10 @@ namespace ICSharpCode.ILSpy
public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
{
var module = assembly.GetPEFileOrNull();
if (module == null)
{
return null;
}
if (options.FullDecompilation && options.SaveAsProjectDirectory != null)
{
var decompiler = new ILSpyWholeProjectDecompiler(assembly, options);
@ -485,7 +489,7 @@ namespace ICSharpCode.ILSpy @@ -485,7 +489,7 @@ namespace ICSharpCode.ILSpy
readonly DecompilationOptions options;
public ILSpyWholeProjectDecompiler(LoadedAssembly assembly, DecompilationOptions options)
: base(options.DecompilerSettings, assembly.GetAssemblyResolver(), assembly.GetDebugInfoOrNull())
: base(options.DecompilerSettings, assembly.GetAssemblyResolver(), assembly.GetAssemblyReferenceClassifier(), assembly.GetDebugInfoOrNull())
{
this.assembly = assembly;
this.options = options;

215
ILSpy/LoadedAssembly.cs

@ -24,7 +24,6 @@ using System.Linq; @@ -24,7 +24,6 @@ using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
@ -40,31 +39,74 @@ using ICSharpCode.ILSpy.Options; @@ -40,31 +39,74 @@ using ICSharpCode.ILSpy.Options;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// Represents an assembly loaded into ILSpy.
/// Represents a file loaded into ILSpy.
///
/// Note: this class is misnamed.
/// The file is not necessarily an assembly, nor is it necessarily loaded.
///
/// A LoadedAssembly can refer to:
/// * a .NET module (single-file) loaded into ILSpy
/// * a non-existant file
/// * a file of unknown format that could not be loaded
/// * a .nupkg file or .NET core bundle
/// * a file that is still being loaded in the background
/// </summary>
[DebuggerDisplay("[LoadedAssembly {shortName}]")]
public sealed class LoadedAssembly
{
/// <summary>
/// Maps from PEFile (successfully loaded .NET module) back to the LoadedAssembly instance
/// that was used to load the module.
/// </summary>
internal static readonly ConditionalWeakTable<PEFile, LoadedAssembly> loadedAssemblies = new ConditionalWeakTable<PEFile, LoadedAssembly>();
readonly Task<PEFile> assemblyTask;
public sealed class LoadResult
{
public PEFile PEFile { get; }
public PEFileNotSupportedException PEFileLoadException { get; }
public LoadedPackage Package { get; }
public LoadResult(PEFile peFile)
{
this.PEFile = peFile ?? throw new ArgumentNullException(nameof(peFile));
}
public LoadResult(PEFileNotSupportedException peFileLoadException, LoadedPackage package)
{
this.PEFileLoadException = peFileLoadException ?? throw new ArgumentNullException(nameof(peFileLoadException));
this.Package = package ?? throw new ArgumentNullException(nameof(package));
}
}
readonly Task<LoadResult> loadingTask;
readonly AssemblyList assemblyList;
readonly string fileName;
readonly string shortName;
readonly IAssemblyResolver providedAssemblyResolver;
public LoadedAssembly ParentBundle { get; }
public LoadedAssembly(AssemblyList assemblyList, string fileName, Stream stream = null)
public LoadedAssembly(AssemblyList assemblyList, string fileName, Task<Stream> stream = null, IAssemblyResolver assemblyResolver = null)
{
this.assemblyList = assemblyList ?? throw new ArgumentNullException(nameof(assemblyList));
this.fileName = fileName ?? throw new ArgumentNullException(nameof(fileName));
this.providedAssemblyResolver = assemblyResolver;
this.assemblyTask = Task.Factory.StartNew(LoadAssembly, stream); // requires that this.fileName is set
this.loadingTask = Task.Run(() => LoadAsync(stream)); // requires that this.fileName is set
this.shortName = Path.GetFileNameWithoutExtension(fileName);
this.resolver = new MyAssemblyResolver(this);
}
public LoadedAssembly(LoadedAssembly bundle, string fileName, Task<Stream> stream, IAssemblyResolver assemblyResolver = null)
: this(bundle.assemblyList, fileName, stream, assemblyResolver)
{
this.ParentBundle = bundle;
}
/// <summary>
/// Returns a target framework identifier in the form '&lt;framework&gt;Version=v&lt;version&gt;'.
/// Returns an empty string if no TargetFrameworkAttribute was found or the file doesn't contain an assembly header, i.e., is only a module.
///
/// Throws an exception if the file does not contain any .NET metadata (e.g. file of unknown format).
/// </summary>
public async Task<string> GetTargetFrameworkIdAsync()
{
@ -76,12 +118,24 @@ namespace ICSharpCode.ILSpy @@ -76,12 +118,24 @@ namespace ICSharpCode.ILSpy
IDebugInfoProvider debugInfoProvider;
/// <summary>
/// Gets the <see cref="LoadResult"/>.
/// </summary>
public Task<LoadResult> GetLoadResultAsync()
{
return loadingTask;
}
/// <summary>
/// Gets the <see cref="PEFile"/>.
/// </summary>
public Task<PEFile> GetPEFileAsync()
public async Task<PEFile> GetPEFileAsync()
{
return assemblyTask;
var loadResult = await loadingTask.ConfigureAwait(false);
if (loadResult.PEFile != null)
return loadResult.PEFile;
else
throw loadResult.PEFileLoadException;
}
/// <summary>
@ -104,7 +158,7 @@ namespace ICSharpCode.ILSpy @@ -104,7 +158,7 @@ namespace ICSharpCode.ILSpy
ICompilation typeSystem;
/// <summary>
/// Gets a type system containing all types from this assembly + primitve types from mscorlib.
/// Gets a type system containing all types from this assembly + primitive types from mscorlib.
/// Returns null in case of load errors.
/// </summary>
/// <remarks>
@ -112,30 +166,34 @@ namespace ICSharpCode.ILSpy @@ -112,30 +166,34 @@ namespace ICSharpCode.ILSpy
/// </remarks>
public ICompilation GetTypeSystemOrNull()
{
if (typeSystem != null)
return typeSystem;
var module = GetPEFileOrNull();
if (module == null)
return null;
return typeSystem = new SimpleCompilation(
module.WithOptions(TypeSystemOptions.Default | TypeSystemOptions.Uncached | TypeSystemOptions.KeepModifiers),
MinimalCorlib.Instance);
return LazyInitializer.EnsureInitialized(ref this.typeSystem, () => {
var module = GetPEFileOrNull();
if (module == null)
return null;
return new SimpleCompilation(
module.WithOptions(TypeSystemOptions.Default | TypeSystemOptions.Uncached | TypeSystemOptions.KeepModifiers),
MinimalCorlib.Instance);
});
}
readonly object typeSystemWithOptionsLockObj = new object();
ICompilation typeSystemWithOptions;
TypeSystemOptions currentTypeSystemOptions;
public ICompilation GetTypeSystemOrNull(TypeSystemOptions options)
{
if (typeSystemWithOptions != null && options == currentTypeSystemOptions)
return typeSystemWithOptions;
var module = GetPEFileOrNull();
if (module == null)
return null;
currentTypeSystemOptions = options;
return typeSystemWithOptions = new SimpleCompilation(
module.WithOptions(options | TypeSystemOptions.Uncached | TypeSystemOptions.KeepModifiers),
MinimalCorlib.Instance);
lock (typeSystemWithOptionsLockObj)
{
if (typeSystemWithOptions != null && options == currentTypeSystemOptions)
return typeSystemWithOptions;
var module = GetPEFileOrNull();
if (module == null)
return null;
currentTypeSystemOptions = options;
return typeSystemWithOptions = new SimpleCompilation(
module.WithOptions(options | TypeSystemOptions.Uncached | TypeSystemOptions.KeepModifiers),
MinimalCorlib.Instance);
}
}
public AssemblyList AssemblyList => assemblyList;
@ -176,42 +234,79 @@ namespace ICSharpCode.ILSpy @@ -176,42 +234,79 @@ namespace ICSharpCode.ILSpy
}
}
public bool IsLoaded => assemblyTask.IsCompleted;
public bool IsLoaded => loadingTask.IsCompleted;
public bool HasLoadError => assemblyTask.IsFaulted;
public bool HasLoadError => loadingTask.IsFaulted;
public bool IsAutoLoaded { get; set; }
public string PdbFileOverride { get; set; }
PEFile LoadAssembly(object state)
async Task<LoadResult> LoadAsync(Task<Stream> streamTask)
{
MetadataReaderOptions options;
if (DecompilerSettingsPanel.CurrentDecompilerSettings.ApplyWindowsRuntimeProjections)
// runs on background thread
if (streamTask != null)
{
options = MetadataReaderOptions.ApplyWindowsRuntimeProjections;
var stream = await streamTask;
// Read the module from a precrafted stream
if (!stream.CanSeek)
{
var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
stream.Close();
memoryStream.Position = 0;
stream = memoryStream;
}
var streamOptions = stream is MemoryStream ? PEStreamOptions.PrefetchEntireImage : PEStreamOptions.Default;
return LoadAssembly(stream, streamOptions);
}
else
// Read the module from disk
PEFileNotSupportedException loadAssemblyException;
try
{
options = MetadataReaderOptions.None;
using (var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
return LoadAssembly(fileStream, PEStreamOptions.PrefetchEntireImage);
}
}
catch (PEFileNotSupportedException ex)
{
loadAssemblyException = ex;
}
// If it's not a .NET module, maybe it's a single-file bundle
var bundle = LoadedPackage.FromBundle(fileName);
if (bundle != null)
{
bundle.LoadedAssembly = this;
return new LoadResult(loadAssemblyException, bundle);
}
// If it's not a .NET module, maybe it's a zip archive (e.g. .nupkg)
try
{
var zip = LoadedPackage.FromZipFile(fileName);
zip.LoadedAssembly = this;
return new LoadResult(loadAssemblyException, zip);
}
catch (InvalidDataException)
{
throw loadAssemblyException;
}
}
PEFile module;
// runs on background thread
if (state is Stream stream)
LoadResult LoadAssembly(Stream stream, PEStreamOptions streamOptions)
{
MetadataReaderOptions options;
if (DecompilerSettingsPanel.CurrentDecompilerSettings.ApplyWindowsRuntimeProjections)
{
// Read the module from a precrafted stream
var streamOptions = stream is MemoryStream ? PEStreamOptions.PrefetchEntireImage : PEStreamOptions.Default;
module = new PEFile(fileName, stream, streamOptions, metadataOptions: options);
options = MetadataReaderOptions.ApplyWindowsRuntimeProjections;
}
else
{
// Read the module from disk (by default)
stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
module = new PEFile(fileName, stream, PEStreamOptions.PrefetchEntireImage,
metadataOptions: options);
options = MetadataReaderOptions.None;
}
PEFile module = new PEFile(fileName, stream, streamOptions, metadataOptions: options);
if (DecompilerSettingsPanel.CurrentDecompilerSettings.UseDebugSymbols)
{
try
@ -234,7 +329,7 @@ namespace ICSharpCode.ILSpy @@ -234,7 +329,7 @@ namespace ICSharpCode.ILSpy
{
loadedAssemblies.Add(module, this);
}
return module;
return new LoadResult(module);
}
[ThreadStatic]
@ -283,24 +378,19 @@ namespace ICSharpCode.ILSpy @@ -283,24 +378,19 @@ namespace ICSharpCode.ILSpy
this.parent = parent;
}
public bool IsGacAssembly(IAssemblyReference reference)
{
return parent.universalResolver?.IsGacAssembly(reference) == true;
}
public bool IsSharedAssembly(IAssemblyReference reference, out string runtimePack)
{
runtimePack = null;
return parent.universalResolver?.IsSharedAssembly(reference, out runtimePack) == true;
}
public PEFile Resolve(Decompiler.Metadata.IAssemblyReference reference)
public PEFile Resolve(IAssemblyReference reference)
{
var module = parent.providedAssemblyResolver?.Resolve(reference);
if (module != null)
return module;
return parent.LookupReferencedAssembly(reference)?.GetPEFileOrNull();
}
public PEFile ResolveModule(PEFile mainModule, string moduleName)
{
var module = parent.providedAssemblyResolver?.ResolveModule(mainModule, moduleName);
if (module != null)
return module;
return parent.LookupReferencedModule(mainModule, moduleName)?.GetPEFileOrNull();
}
}
@ -312,6 +402,11 @@ namespace ICSharpCode.ILSpy @@ -312,6 +402,11 @@ namespace ICSharpCode.ILSpy
return resolver;
}
public AssemblyReferenceClassifier GetAssemblyReferenceClassifier()
{
return universalResolver;
}
/// <summary>
/// Returns the debug info for this assembly. Returns null in case of load errors or no debug info is available.
/// </summary>
@ -358,6 +453,8 @@ namespace ICSharpCode.ILSpy @@ -358,6 +453,8 @@ namespace ICSharpCode.ILSpy
MyUniversalResolver universalResolver;
/// <summary>
/// 0) if we're inside a package, look for filename.dll in parent directories
/// (this step already happens in MyAssemblyResolver; not in LookupReferencedAssembly)
/// 1) try to find exact match by tfm + full asm name in loaded assemblies
/// 2) try to find match in search paths
/// 3) if a.deps.json is found: search %USERPROFILE%/.nuget/packages/* as well
@ -532,9 +629,10 @@ namespace ICSharpCode.ILSpy @@ -532,9 +629,10 @@ namespace ICSharpCode.ILSpy
return asm;
}
[Obsolete("Use GetPEFileAsync() or GetLoadResultAsync() instead")]
public Task ContinueWhenLoaded(Action<Task<PEFile>> onAssemblyLoaded, TaskScheduler taskScheduler)
{
return this.assemblyTask.ContinueWith(onAssemblyLoaded, default(CancellationToken), TaskContinuationOptions.RunContinuationsAsynchronously, taskScheduler);
return this.GetPEFileAsync().ContinueWith(onAssemblyLoaded, default(CancellationToken), TaskContinuationOptions.RunContinuationsAsynchronously, taskScheduler);
}
/// <summary>
@ -543,8 +641,7 @@ namespace ICSharpCode.ILSpy @@ -543,8 +641,7 @@ namespace ICSharpCode.ILSpy
/// </summary>
public void WaitUntilLoaded()
{
assemblyTask.Wait();
loadingTask.Wait();
}
}
}

121
ILSpy/LoadedNugetPackage.cs

@ -1,121 +0,0 @@ @@ -1,121 +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.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace ICSharpCode.ILSpy
{
public class LoadedNugetPackage : INotifyPropertyChanged
{
public List<Entry> Entries { get; } = new List<Entry>();
public List<Entry> SelectedEntries { get; } = new List<Entry>();
public LoadedNugetPackage(string file)
{
using (var archive = ZipFile.OpenRead(file))
{
foreach (var entry in archive.Entries)
{
switch (Path.GetExtension(entry.FullName))
{
case ".dll":
case ".exe":
var memory = new MemoryStream();
entry.Open().CopyTo(memory);
memory.Position = 0;
var e = new Entry(Uri.UnescapeDataString(entry.FullName), memory);
e.PropertyChanged += EntryPropertyChanged;
Entries.Add(e);
break;
}
}
}
}
void EntryPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Entry.IsSelected))
{
var entry = (Entry)sender;
if (entry.IsSelected)
SelectedEntries.Add(entry);
else
SelectedEntries.Remove(entry);
OnPropertyChanged(nameof(SelectedEntries));
}
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class Entry : INotifyPropertyChanged
{
public string Name { get; }
public bool IsSelected {
get { return isSelected; }
set {
if (isSelected != value)
{
isSelected = value;
OnPropertyChanged();
}
}
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public Stream Stream { get; }
bool isSelected;
public event PropertyChangedEventHandler PropertyChanged;
public Entry(string name, Stream stream)
{
this.Name = name;
this.Stream = stream;
}
}
}

281
ILSpy/LoadedPackage.cs

@ -0,0 +1,281 @@ @@ -0,0 +1,281 @@
// 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.IO.Compression;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// NuGet package or .NET bundle:
/// </summary>
public class LoadedPackage
{
public enum PackageKind
{
Zip,
Bundle,
}
/// <summary>
/// Gets the LoadedAssembly instance representing this bundle.
/// </summary>
internal LoadedAssembly LoadedAssembly { get; set; }
public PackageKind Kind { get; }
/// <summary>
/// List of all entries, including those in sub-directories within the package.
/// </summary>
public IReadOnlyList<PackageEntry> Entries { get; }
internal PackageFolder RootFolder { get; }
public LoadedPackage(PackageKind kind, IEnumerable<PackageEntry> entries)
{
this.Kind = kind;
this.Entries = entries.ToArray();
var topLevelEntries = new List<PackageEntry>();
var folders = new Dictionary<string, PackageFolder>();
var rootFolder = new PackageFolder(this, null, "");
folders.Add("", rootFolder);
foreach (var entry in this.Entries)
{
var (dirname, filename) = SplitName(entry.Name);
GetFolder(dirname).Entries.Add(new FolderEntry(filename, entry));
}
this.RootFolder = rootFolder;
static (string, string) SplitName(string filename)
{
int pos = filename.LastIndexOfAny(new char[] { '/', '\\' });
if (pos == -1)
return ("", filename); // file in root
else
return (filename.Substring(0, pos), filename.Substring(pos + 1));
}
PackageFolder GetFolder(string name)
{
if (folders.TryGetValue(name, out var result))
return result;
var (dirname, basename) = SplitName(name);
PackageFolder parent = GetFolder(dirname);
result = new PackageFolder(this, parent, basename);
parent.Folders.Add(result);
folders.Add(name, result);
return result;
}
}
public static LoadedPackage FromZipFile(string file)
{
Debug.WriteLine($"LoadedPackage.FromZipFile({file})");
using var archive = ZipFile.OpenRead(file);
return new LoadedPackage(PackageKind.Zip,
archive.Entries.Select(entry => new ZipFileEntry(file, entry)));
}
/// <summary>
/// Load a .NET single-file bundle.
/// </summary>
public static LoadedPackage FromBundle(string fileName)
{
using var memoryMappedFile = MemoryMappedFile.CreateFromFile(fileName, FileMode.Open, null, 0, MemoryMappedFileAccess.Read);
var view = memoryMappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read);
try
{
if (!SingleFileBundle.IsBundle(view, out long bundleHeaderOffset))
return null;
var manifest = SingleFileBundle.ReadManifest(view, bundleHeaderOffset);
var entries = manifest.Entries.Select(e => new BundleEntry(fileName, view, e)).ToList();
var result = new LoadedPackage(PackageKind.Bundle, entries);
view = null; // don't dispose the view, we're still using it in the bundle entries
return result;
}
finally
{
view?.Dispose();
}
}
/// <summary>
/// Entry inside a package folder. Effectively renames the entry.
/// </summary>
sealed class FolderEntry : PackageEntry
{
readonly PackageEntry originalEntry;
public override string Name { get; }
public FolderEntry(string name, PackageEntry originalEntry)
{
this.Name = name;
this.originalEntry = originalEntry;
}
public override ManifestResourceAttributes Attributes => originalEntry.Attributes;
public override string FullName => originalEntry.FullName;
public override ResourceType ResourceType => originalEntry.ResourceType;
public override Stream TryOpenStream() => originalEntry.TryOpenStream();
}
sealed class ZipFileEntry : PackageEntry
{
readonly string zipFile;
public override string Name { get; }
public override string FullName => $"zip://{zipFile};{Name}";
public ZipFileEntry(string zipFile, ZipArchiveEntry entry)
{
this.zipFile = zipFile;
this.Name = entry.FullName;
}
public override Stream TryOpenStream()
{
Debug.WriteLine("Decompress " + Name);
using var archive = ZipFile.OpenRead(zipFile);
var entry = archive.GetEntry(Name);
if (entry == null)
return null;
var memoryStream = new MemoryStream();
using (var s = entry.Open())
{
s.CopyTo(memoryStream);
}
memoryStream.Position = 0;
return memoryStream;
}
}
sealed class BundleEntry : PackageEntry
{
readonly string bundleFile;
readonly MemoryMappedViewAccessor view;
readonly SingleFileBundle.Entry entry;
public BundleEntry(string bundleFile, MemoryMappedViewAccessor view, SingleFileBundle.Entry entry)
{
this.bundleFile = bundleFile;
this.view = view;
this.entry = entry;
}
public override string Name => entry.RelativePath;
public override string FullName => $"bundle://{bundleFile};{Name}";
public override Stream TryOpenStream()
{
Debug.WriteLine("Open bundle member " + Name);
return new UnmanagedMemoryStream(view.SafeMemoryMappedViewHandle, entry.Offset, entry.Size);
}
}
}
public abstract class PackageEntry : Resource
{
/// <summary>
/// Gets the file name of the entry (may include path components, relative to the package root).
/// </summary>
public abstract override string Name { get; }
/// <summary>
/// Gets the full file name for the entry.
/// </summary>
public abstract string FullName { get; }
}
class PackageFolder : IAssemblyResolver
{
/// <summary>
/// Gets the short name of the folder.
/// </summary>
public string Name { get; }
readonly LoadedPackage package;
readonly PackageFolder parent;
internal PackageFolder(LoadedPackage package, PackageFolder parent, string name)
{
this.package = package;
this.parent = parent;
this.Name = name;
}
public List<PackageFolder> Folders { get; } = new List<PackageFolder>();
public List<PackageEntry> Entries { get; } = new List<PackageEntry>();
public PEFile Resolve(IAssemblyReference reference)
{
var asm = ResolveFileName(reference.Name + ".dll");
if (asm != null)
{
return asm.GetPEFileOrNull();
}
return parent?.Resolve(reference);
}
public PEFile ResolveModule(PEFile mainModule, string moduleName)
{
var asm = ResolveFileName(moduleName + ".dll");
if (asm != null)
{
return asm.GetPEFileOrNull();
}
return parent?.ResolveModule(mainModule, moduleName);
}
readonly Dictionary<string, LoadedAssembly> assemblies = new Dictionary<string, LoadedAssembly>(StringComparer.OrdinalIgnoreCase);
internal LoadedAssembly ResolveFileName(string name)
{
if (package.LoadedAssembly == null)
return null;
lock (assemblies)
{
if (assemblies.TryGetValue(name, out var asm))
return asm;
var entry = Entries.FirstOrDefault(e => string.Equals(name, e.Name, StringComparison.OrdinalIgnoreCase));
if (entry != null)
{
asm = new LoadedAssembly(
package.LoadedAssembly, entry.Name,
assemblyResolver: this,
stream: Task.Run(entry.TryOpenStream)
);
}
else
{
asm = null;
}
assemblies.Add(name, asm);
return asm;
}
}
}
}

65
ILSpy/MainWindow.xaml.cs

@ -69,7 +69,6 @@ namespace ICSharpCode.ILSpy @@ -69,7 +69,6 @@ namespace ICSharpCode.ILSpy
partial class MainWindow : Window
{
bool refreshInProgress;
bool handlingNugetPackageSelection;
readonly NavigationHistory<NavigationState> history = new NavigationHistory<NavigationState>();
ILSpySettings spySettingsForMainWindow_Loaded;
internal SessionSettings sessionSettings;
@ -335,8 +334,6 @@ namespace ICSharpCode.ILSpy @@ -335,8 +334,6 @@ namespace ICSharpCode.ILSpy
string data = new string((char*)copyData->Buffer, 0, copyData->Size / sizeof(char));
if (data.StartsWith("ILSpy:\r\n", StringComparison.Ordinal))
{
if (handlingNugetPackageSelection)
return (IntPtr)1;
data = data.Substring(8);
List<string> lines = new List<string>();
using (StringReader r = new StringReader(data))
@ -1110,58 +1107,22 @@ namespace ICSharpCode.ILSpy @@ -1110,58 +1107,22 @@ namespace ICSharpCode.ILSpy
SharpTreeNode lastNode = null;
foreach (string file in fileNames)
{
switch (Path.GetExtension(file))
var asm = assemblyList.OpenAssembly(file);
if (asm != null)
{
case ".nupkg":
this.handlingNugetPackageSelection = true;
try
{
LoadedNugetPackage package = new LoadedNugetPackage(file);
var selectionDialog = new NugetPackageBrowserDialog(package);
selectionDialog.Owner = this;
if (selectionDialog.ShowDialog() != true)
break;
foreach (var entry in selectionDialog.SelectedItems)
{
var nugetAsm = assemblyList.OpenAssembly("nupkg://" + file + ";" + entry.Name, entry.Stream, true);
if (nugetAsm != null)
{
if (loadedAssemblies != null)
loadedAssemblies.Add(nugetAsm);
else
{
var node = assemblyListTreeNode.FindAssemblyNode(nugetAsm);
if (node != null && focusNode)
{
AssemblyTreeView.SelectedItems.Add(node);
lastNode = node;
}
}
}
}
}
finally
{
this.handlingNugetPackageSelection = false;
}
break;
default:
var asm = assemblyList.OpenAssembly(file);
if (asm != null)
if (loadedAssemblies != null)
{
loadedAssemblies.Add(asm);
}
else
{
var node = assemblyListTreeNode.FindAssemblyNode(asm);
if (node != null && focusNode)
{
if (loadedAssemblies != null)
loadedAssemblies.Add(asm);
else
{
var node = assemblyListTreeNode.FindAssemblyNode(asm);
if (node != null && focusNode)
{
AssemblyTreeView.SelectedItems.Add(node);
lastNode = node;
}
}
AssemblyTreeView.SelectedItems.Add(node);
lastNode = node;
}
break;
}
}
if (lastNode != null && focusNode)

4
ILSpy/Properties/AssemblyInfo.template.cs

@ -36,8 +36,8 @@ using System.Runtime.InteropServices; @@ -36,8 +36,8 @@ using System.Runtime.InteropServices;
internal static class RevisionClass
{
public const string Major = "6";
public const string Minor = "3";
public const string Major = "7";
public const string Minor = "0";
public const string Build = "0";
public const string Revision = "$INSERTREVISION$";
public const string VersionName = "preview1";

4
ILSpy/Properties/app.config.template

@ -32,6 +32,10 @@ @@ -32,6 +32,10 @@
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

58
ILSpy/TreeNodes/AssemblyListTreeNode.cs

@ -17,17 +17,15 @@ @@ -17,17 +17,15 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Windows;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.TreeView;
namespace ICSharpCode.ILSpy.TreeNodes
@ -105,7 +103,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -105,7 +103,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
{
var assemblies = files
.Where(file => file != null)
.SelectMany(file => OpenAssembly(assemblyList, file))
.Select(file => assemblyList.OpenAssembly(file))
.Where(asm => asm != null)
.Distinct()
.ToArray();
@ -127,28 +125,6 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -127,28 +125,6 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
private IEnumerable<LoadedAssembly> OpenAssembly(AssemblyList assemblyList, string file)
{
if (file.EndsWith(".nupkg"))
{
LoadedNugetPackage package = new LoadedNugetPackage(file);
var selectionDialog = new NugetPackageBrowserDialog(package);
selectionDialog.Owner = Application.Current.MainWindow;
if (selectionDialog.ShowDialog() != true)
yield break;
foreach (var entry in selectionDialog.SelectedItems)
{
var nugetAsm = assemblyList.OpenAssembly("nupkg://" + file + ";" + entry.Name, entry.Stream, true);
if (nugetAsm != null)
{
yield return nugetAsm;
}
}
yield break;
}
yield return assemblyList.OpenAssembly(file);
}
public Action<SharpTreeNode> Select = delegate { };
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
@ -166,7 +142,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -166,7 +142,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
#region Find*Node
public ILSpyTreeNode FindResourceNode(Resource resource)
{
if (resource == null || resource.IsNil)
if (resource == null)
return null;
foreach (AssemblyTreeNode node in this.Children)
{
@ -207,13 +183,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -207,13 +183,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
{
if (module == null)
return null;
App.Current.Dispatcher.VerifyAccess();
foreach (AssemblyTreeNode node in this.Children)
{
if (node.LoadedAssembly.IsLoaded && node.LoadedAssembly.GetPEFileOrNull()?.FileName == module.FileName)
return node;
}
return null;
return FindAssemblyNode(module.GetLoadedAssembly());
}
public AssemblyTreeNode FindAssemblyNode(LoadedAssembly asm)
@ -221,10 +191,24 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -221,10 +191,24 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (asm == null)
return null;
App.Current.Dispatcher.VerifyAccess();
foreach (AssemblyTreeNode node in this.Children)
if (asm.ParentBundle != null)
{
if (node.LoadedAssembly == asm)
return node;
var bundle = FindAssemblyNode(asm.ParentBundle);
if (bundle == null)
return null;
foreach (var node in TreeTraversal.PreOrder(bundle.Children, r => (r as PackageFolderTreeNode)?.Children).OfType<AssemblyTreeNode>())
{
if (node.LoadedAssembly == asm)
return node;
}
}
else
{
foreach (AssemblyTreeNode node in this.Children)
{
if (node.LoadedAssembly == asm)
return node;
}
}
return null;
}

23
ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs

@ -56,29 +56,22 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -56,29 +56,22 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override void ActivateItem(System.Windows.RoutedEventArgs e)
{
var assemblyListNode = parentAssembly.Parent as AssemblyListTreeNode;
if (assemblyListNode != null)
if (parentAssembly.Parent is AssemblyListTreeNode assemblyListNode)
{
assemblyListNode.Select(assemblyListNode.FindAssemblyNode(parentAssembly.LoadedAssembly.LookupReferencedAssembly(r)));
var resolver = parentAssembly.LoadedAssembly.GetAssemblyResolver();
assemblyListNode.Select(assemblyListNode.FindAssemblyNode(resolver.Resolve(r)));
e.Handled = true;
}
}
protected override void LoadChildren()
{
var assemblyListNode = parentAssembly.Parent as AssemblyListTreeNode;
if (assemblyListNode != null)
var resolver = parentAssembly.LoadedAssembly.GetAssemblyResolver();
var module = resolver.Resolve(r);
if (module != null)
{
var refNode = assemblyListNode.FindAssemblyNode(parentAssembly.LoadedAssembly.LookupReferencedAssembly(r));
if (refNode != null)
{
var module = refNode.LoadedAssembly.GetPEFileOrNull();
if (module != null)
{
foreach (var childRef in module.AssemblyReferences)
this.Children.Add(new AssemblyReferenceTreeNode(childRef, refNode));
}
}
foreach (var childRef in module.AssemblyReferences)
this.Children.Add(new AssemblyReferenceTreeNode(childRef, parentAssembly));
}
}

105
ILSpy/TreeNodes/AssemblyTreeNode.cs

@ -20,7 +20,6 @@ using System; @@ -20,7 +20,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
@ -48,13 +47,18 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -48,13 +47,18 @@ namespace ICSharpCode.ILSpy.TreeNodes
readonly Dictionary<string, NamespaceTreeNode> namespaces = new Dictionary<string, NamespaceTreeNode>();
readonly Dictionary<TypeDefinitionHandle, TypeTreeNode> typeDict = new Dictionary<TypeDefinitionHandle, TypeTreeNode>();
ICompilation typeSystem;
string textOverride;
public AssemblyTreeNode(LoadedAssembly assembly)
public AssemblyTreeNode(LoadedAssembly assembly) : this(assembly, null)
{
this.LoadedAssembly = assembly ?? throw new ArgumentNullException(nameof(assembly));
assembly.ContinueWhenLoaded(OnAssemblyLoaded, TaskScheduler.FromCurrentSynchronizationContext());
}
private AssemblyTreeNode(LoadedAssembly assembly, string textOverride)
{
this.LoadedAssembly = assembly ?? throw new ArgumentNullException(nameof(assembly));
this.LazyLoading = true;
this.textOverride = textOverride;
Init();
}
public AssemblyList AssemblyList {
@ -69,13 +73,24 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -69,13 +73,24 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
public override object Text => LoadedAssembly.Text;
public override object Text => textOverride ?? LoadedAssembly.Text;
public override object Icon {
get {
if (LoadedAssembly.IsLoaded)
{
return LoadedAssembly.HasLoadError ? Images.AssemblyWarning : Images.Assembly;
if (LoadedAssembly.HasLoadError)
return Images.AssemblyWarning;
var loadResult = LoadedAssembly.GetLoadResultAsync().Result;
if (loadResult.Package != null)
{
return loadResult.Package.Kind switch
{
LoadedPackage.PackageKind.Zip => Images.NuGet,
_ => Images.Library,
};
}
return Images.Assembly;
}
else
{
@ -105,19 +120,22 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -105,19 +120,22 @@ namespace ICSharpCode.ILSpy.TreeNodes
tooltip.Inlines.Add(new Bold(new Run("Location: ")));
tooltip.Inlines.Add(new Run(LoadedAssembly.FileName));
tooltip.Inlines.Add(new LineBreak());
tooltip.Inlines.Add(new Bold(new Run("Architecture: ")));
tooltip.Inlines.Add(new Run(Language.GetPlatformDisplayName(module)));
string runtimeName = Language.GetRuntimeDisplayName(module);
if (runtimeName != null)
if (module != null)
{
tooltip.Inlines.Add(new Bold(new Run("Architecture: ")));
tooltip.Inlines.Add(new Run(Language.GetPlatformDisplayName(module)));
string runtimeName = Language.GetRuntimeDisplayName(module);
if (runtimeName != null)
{
tooltip.Inlines.Add(new LineBreak());
tooltip.Inlines.Add(new Bold(new Run("Runtime: ")));
tooltip.Inlines.Add(new Run(runtimeName));
}
var debugInfo = LoadedAssembly.GetDebugInfoOrNull();
tooltip.Inlines.Add(new LineBreak());
tooltip.Inlines.Add(new Bold(new Run("Runtime: ")));
tooltip.Inlines.Add(new Run(runtimeName));
tooltip.Inlines.Add(new Bold(new Run("Debug info: ")));
tooltip.Inlines.Add(new Run(debugInfo?.Description ?? "none"));
}
var debugInfo = LoadedAssembly.GetDebugInfoOrNull();
tooltip.Inlines.Add(new LineBreak());
tooltip.Inlines.Add(new Bold(new Run("Debug info: ")));
tooltip.Inlines.Add(new Run(debugInfo?.Description ?? "none"));
}
return tooltip;
@ -128,30 +146,48 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -128,30 +146,48 @@ namespace ICSharpCode.ILSpy.TreeNodes
get { return !LoadedAssembly.HasLoadError; }
}
void OnAssemblyLoaded(Task<PEFile> moduleTask)
async void Init()
{
// change from "Loading" icon to final icon
RaisePropertyChanged(nameof(Icon));
RaisePropertyChanged(nameof(ExpandedIcon));
RaisePropertyChanged(nameof(ToolTip));
if (moduleTask.IsFaulted)
try
{
RaisePropertyChanged(nameof(ShowExpander)); // cannot expand assemblies with load error
await this.LoadedAssembly.GetLoadResultAsync();
RaisePropertyChanged(nameof(Text)); // shortname might have changed
}
else
catch
{
RaisePropertyChanged(nameof(Text)); // shortname might have changed
RaisePropertyChanged(nameof(ShowExpander)); // cannot expand assemblies with load error
}
// change from "Loading" icon to final icon
RaisePropertyChanged(nameof(Icon));
RaisePropertyChanged(nameof(ExpandedIcon));
RaisePropertyChanged(nameof(ToolTip));
}
protected override void LoadChildren()
{
var module = LoadedAssembly.GetPEFileOrNull();
if (module == null)
LoadedAssembly.LoadResult loadResult;
try
{
loadResult = LoadedAssembly.GetLoadResultAsync().Result;
}
catch
{
// if we crashed on loading, then we don't have any children
return;
}
if (loadResult.PEFile != null)
{
LoadChildrenForPEFile(loadResult.PEFile);
}
else if (loadResult.Package != null)
{
var package = loadResult.Package;
this.Children.AddRange(PackageFolderTreeNode.LoadChildrenForFolder(package.RootFolder));
}
}
void LoadChildrenForPEFile(PEFile module)
{
typeSystem = LoadedAssembly.GetTypeSystemOrNull(DecompilerTypeSystem.GetOptions(new DecompilationOptions().DecompilerSettings));
var assembly = (MetadataModule)typeSystem.MainModule;
this.Children.Add(new Metadata.MetadataTreeNode(module, this));
@ -186,8 +222,6 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -186,8 +222,6 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
public override bool CanExpandRecursively => true;
/// <summary>
/// Finds the node for a top-level type.
/// </summary>
@ -220,7 +254,8 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -220,7 +254,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override bool CanDrag(SharpTreeNode[] nodes)
{
return nodes.All(n => n is AssemblyTreeNode);
// prohibit dragging assemblies nested in nuget packages
return nodes.All(n => n is AssemblyTreeNode { textOverride: null });
}
public override void StartDrag(DependencyObject dragSource, SharpTreeNode[] nodes)
@ -230,7 +265,8 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -230,7 +265,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override bool CanDelete()
{
return true;
// prohibit deleting assemblies nested in nuget packages
return textOverride == null;
}
public override void Delete()
@ -424,13 +460,14 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -424,13 +460,14 @@ namespace ICSharpCode.ILSpy.TreeNodes
foreach (var node in context.SelectedTreeNodes)
{
var la = ((AssemblyTreeNode)node).LoadedAssembly;
var resolver = la.GetAssemblyResolver();
var module = la.GetPEFileOrNull();
if (module != null)
{
var metadata = module.Metadata;
foreach (var assyRef in metadata.AssemblyReferences)
{
la.LookupReferencedAssembly(new AssemblyReference(module, assyRef));
resolver.Resolve(new AssemblyReference(module, assyRef));
}
}
}
@ -452,7 +489,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -452,7 +489,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
{
if (context.SelectedTreeNodes == null)
return false;
return context.SelectedTreeNodes.Where(n => n is AssemblyTreeNode).Any(n => !((AssemblyTreeNode)n).LoadedAssembly.FileName.StartsWith("nupkg://"));
return context.SelectedTreeNodes.Any(n => n is AssemblyTreeNode);
}
public void Execute(TextViewContext context)
@ -462,7 +499,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -462,7 +499,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
foreach (var node in context.SelectedTreeNodes)
{
var loadedAssm = ((AssemblyTreeNode)node).LoadedAssembly;
if (!loadedAssm.HasLoadError && !loadedAssm.FileName.StartsWith("nupkg://"))
if (!loadedAssm.HasLoadError)
{
loadedAssm.IsAutoLoaded = false;
node.RaisePropertyChanged(nameof(node.Foreground));

92
ILSpy/TreeNodes/PackageFolderTreeNode.cs

@ -0,0 +1,92 @@ @@ -0,0 +1,92 @@
// 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.Linq;
using ICSharpCode.Decompiler;
using ICSharpCode.TreeView;
namespace ICSharpCode.ILSpy.TreeNodes
{
/// <summary>
/// Lists the embedded resources in an assembly.
/// </summary>
sealed class PackageFolderTreeNode : ILSpyTreeNode
{
readonly PackageFolder folder;
public PackageFolderTreeNode(PackageFolder folder, string text = null)
{
this.folder = folder;
this.Text = text ?? folder.Name;
this.LazyLoading = true;
}
public override object Text { get; }
public override object Icon => Images.FolderClosed;
public override object ExpandedIcon => Images.FolderOpen;
protected override void LoadChildren()
{
this.Children.AddRange(LoadChildrenForFolder(folder));
}
internal static IEnumerable<SharpTreeNode> LoadChildrenForFolder(PackageFolder root)
{
foreach (var folder in root.Folders.OrderBy(f => f.Name))
{
string newName = folder.Name;
var subfolder = folder;
while (subfolder.Folders.Count == 1 && subfolder.Entries.Count == 0)
{
// special case: a folder that only contains a single sub-folder
subfolder = subfolder.Folders[0];
newName = $"{newName}/{subfolder.Name}";
}
yield return new PackageFolderTreeNode(subfolder, newName);
}
foreach (var entry in root.Entries.OrderBy(e => e.Name))
{
if (entry.Name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
{
var asm = root.ResolveFileName(entry.Name);
if (asm != null)
{
yield return new AssemblyTreeNode(asm);
}
else
{
yield return ResourceTreeNode.Create(entry);
}
}
else
{
yield return ResourceTreeNode.Create(entry);
}
}
}
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
}
}
}

41
ILSpy/TreeNodes/ResourceNodes/CursorResourceEntryNode.cs

@ -36,20 +36,11 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -36,20 +36,11 @@ namespace ICSharpCode.ILSpy.TreeNodes
public ILSpyTreeNode CreateNode(Resource resource)
{
Stream stream = resource.TryOpenStream();
if (stream == null)
return null;
return CreateNode(resource.Name, stream);
}
public ILSpyTreeNode CreateNode(string key, object data)
{
if (!(data is Stream))
return null;
string key = resource.Name;
foreach (string fileExt in imageFileExtensions)
{
if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase))
return new CursorResourceEntryNode(key, (Stream)data);
return new CursorResourceEntryNode(key, resource.TryOpenStream);
}
return null;
}
@ -57,8 +48,8 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -57,8 +48,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
sealed class CursorResourceEntryNode : ResourceEntryNode
{
public CursorResourceEntryNode(string key, Stream data)
: base(key, data)
public CursorResourceEntryNode(string key, Func<Stream> openStream)
: base(key, openStream)
{
}
@ -69,19 +60,23 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -69,19 +60,23 @@ namespace ICSharpCode.ILSpy.TreeNodes
try
{
AvalonEditTextOutput output = new AvalonEditTextOutput();
Data.Position = 0;
BitmapImage image = new BitmapImage();
//HACK: windows imaging does not understand that .cur files have the same layout as .ico
// so load to data, and modify the ResourceType in the header to make look like an icon...
MemoryStream s = Data as MemoryStream;
if (null == s)
byte[] curData;
using (var data = OpenStream())
{
// data was stored in another stream type (e.g. PinnedBufferedMemoryStream)
s = new MemoryStream();
Data.CopyTo(s);
if (data == null)
return false;
//HACK: windows imaging does not understand that .cur files have the same layout as .ico
// so load to data, and modify the ResourceType in the header to make look like an icon...
MemoryStream s = data as MemoryStream;
if (s == null)
{
// data was stored in another stream type (e.g. PinnedBufferedMemoryStream)
s = new MemoryStream();
data.CopyTo(s);
}
curData = s.ToArray();
}
byte[] curData = s.ToArray();
curData[2] = 1;
using (Stream stream = new MemoryStream(curData))
{

1
ILSpy/TreeNodes/ResourceNodes/IResourceNodeFactory.cs

@ -26,6 +26,5 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -26,6 +26,5 @@ namespace ICSharpCode.ILSpy.TreeNodes
public interface IResourceNodeFactory
{
ILSpyTreeNode CreateNode(Resource resource);
ILSpyTreeNode CreateNode(string key, object data);
}
}

26
ILSpy/TreeNodes/ResourceNodes/IconResourceEntryNode.cs

@ -34,29 +34,17 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -34,29 +34,17 @@ namespace ICSharpCode.ILSpy.TreeNodes
{
public ILSpyTreeNode CreateNode(Resource resource)
{
Stream stream = resource.TryOpenStream();
if (stream == null)
return null;
return CreateNode(resource.Name, stream);
}
public ILSpyTreeNode CreateNode(string key, object data)
{
if (data is System.Drawing.Icon)
if (resource.Name.EndsWith(".ico", StringComparison.OrdinalIgnoreCase))
{
MemoryStream s = new MemoryStream();
((System.Drawing.Icon)data).Save(s);
return new IconResourceEntryNode(key, s);
return new IconResourceEntryNode(resource.Name, resource.TryOpenStream);
}
if (data is Stream && key.EndsWith(".ico", StringComparison.OrdinalIgnoreCase))
return new IconResourceEntryNode(key, (Stream)data);
return null;
}
}
sealed class IconResourceEntryNode : ResourceEntryNode
{
public IconResourceEntryNode(string key, Stream data)
public IconResourceEntryNode(string key, Func<Stream> data)
: base(key, data)
{
}
@ -68,8 +56,10 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -68,8 +56,10 @@ namespace ICSharpCode.ILSpy.TreeNodes
try
{
AvalonEditTextOutput output = new AvalonEditTextOutput();
Data.Position = 0;
IconBitmapDecoder decoder = new IconBitmapDecoder(Data, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
using var data = OpenStream();
if (data == null)
return false;
IconBitmapDecoder decoder = new IconBitmapDecoder(data, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
foreach (var frame in decoder.Frames)
{
output.Write(String.Format("{0}x{1}, {2} bit: ", frame.PixelHeight, frame.PixelWidth, frame.Thumbnail.Format.BitsPerPixel));
@ -94,4 +84,4 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -94,4 +84,4 @@ namespace ICSharpCode.ILSpy.TreeNodes
output.AddUIElement(() => new Image { Source = frame });
}
}
}
}

5
ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
using System.ComponentModel.Composition;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using ICSharpCode.Decompiler;
@ -65,7 +66,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -65,7 +66,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
int i = 0;
foreach (Image image in this.data.Images)
{
var node = ResourceEntryNode.Create("Image" + i.ToString(), image);
using var s = new MemoryStream();
image.Save(s, System.Drawing.Imaging.ImageFormat.Bmp);
var node = ResourceEntryNode.Create("Image" + i.ToString(), s.ToArray());
if (node != null)
Children.Add(node);
++i;

26
ILSpy/TreeNodes/ResourceNodes/ImageResourceEntryNode.cs

@ -36,26 +36,11 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -36,26 +36,11 @@ namespace ICSharpCode.ILSpy.TreeNodes
public ILSpyTreeNode CreateNode(Resource resource)
{
Stream stream = resource.TryOpenStream();
if (stream == null)
return null;
return CreateNode(resource.Name, stream);
}
public ILSpyTreeNode CreateNode(string key, object data)
{
if (data is System.Drawing.Image)
{
MemoryStream s = new MemoryStream();
((System.Drawing.Image)data).Save(s, System.Drawing.Imaging.ImageFormat.Bmp);
return new ImageResourceEntryNode(key, s);
}
if (!(data is Stream))
return null;
string key = resource.Name;
foreach (string fileExt in imageFileExtensions)
{
if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase))
return new ImageResourceEntryNode(key, (Stream)data);
return new ImageResourceEntryNode(key, resource.TryOpenStream);
}
return null;
}
@ -63,8 +48,8 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -63,8 +48,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
sealed class ImageResourceEntryNode : ResourceEntryNode
{
public ImageResourceEntryNode(string key, Stream data)
: base(key, data)
public ImageResourceEntryNode(string key, Func<Stream> openStream)
: base(key, openStream)
{
}
@ -75,10 +60,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -75,10 +60,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
try
{
AvalonEditTextOutput output = new AvalonEditTextOutput();
Data.Position = 0;
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = Data;
image.StreamSource = OpenStream();
image.EndInit();
output.AddUIElement(() => new Image { Source = image });
output.WriteLine();

99
ILSpy/TreeNodes/ResourceNodes/JsonResourceNode.cs

@ -1,99 +0,0 @@ @@ -1,99 +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.ComponentModel.Composition;
using System.IO;
using System.Threading.Tasks;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.ViewModels;
namespace ICSharpCode.ILSpy.TreeNodes
{
[Export(typeof(IResourceNodeFactory))]
sealed class JsonResourceNodeFactory : IResourceNodeFactory
{
private readonly static string[] jsonFileExtensions = { ".json" };
public ILSpyTreeNode CreateNode(Resource resource)
{
Stream stream = resource.TryOpenStream();
if (stream == null)
return null;
return CreateNode(resource.Name, stream);
}
public ILSpyTreeNode CreateNode(string key, object data)
{
if (!(data is Stream))
return null;
foreach (string fileExt in jsonFileExtensions)
{
if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase))
return new JsonResourceEntryNode(key, (Stream)data);
}
return null;
}
}
sealed class JsonResourceEntryNode : ResourceEntryNode
{
string json;
public JsonResourceEntryNode(string key, Stream data)
: base(key, data)
{
}
// TODO : add Json Icon
public override object Icon => Images.Resource;
public override bool View(TabPageModel tabPage)
{
AvalonEditTextOutput output = new AvalonEditTextOutput();
IHighlightingDefinition highlighting = null;
tabPage.ShowTextView(textView => textView.RunWithCancellation(
token => Task.Factory.StartNew(
() => {
try {
// cache read XAML because stream will be closed after first read
if (json == null) {
using (var reader = new StreamReader(Data)) {
json = reader.ReadToEnd();
}
}
output.Write(json);
highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".json");
}
catch (Exception ex) {
output.Write(ex.ToString());
}
return output;
}, token)
).Then(t => textView.ShowNode(t, this, highlighting))
.HandleExceptions());
tabPage.SupportsLanguageSwitching = false;
return true;
}
}
}

40
ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs

@ -20,6 +20,7 @@ using System; @@ -20,6 +20,7 @@ using System;
using System.IO;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.ILSpy.TextView;
using Microsoft.Win32;
@ -32,42 +33,47 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -32,42 +33,47 @@ namespace ICSharpCode.ILSpy.TreeNodes
public class ResourceEntryNode : ILSpyTreeNode
{
private readonly string key;
private readonly Stream data;
private readonly Func<Stream> openStream;
public override object Text => this.key;
public override object Icon => Images.Resource;
protected Stream Data => data;
protected Stream OpenStream()
{
return openStream();
}
public ResourceEntryNode(string key, Stream data)
public ResourceEntryNode(string key, Func<Stream> openStream)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
if (data == null)
throw new ArgumentNullException(nameof(data));
if (openStream == null)
throw new ArgumentNullException(nameof(openStream));
this.key = key;
this.data = data;
this.openStream = openStream;
}
public static ILSpyTreeNode Create(string key, object data)
public static ILSpyTreeNode Create(Resource resource)
{
ILSpyTreeNode result = null;
foreach (var factory in App.ExportProvider.GetExportedValues<IResourceNodeFactory>())
{
result = factory.CreateNode(key, data);
result = factory.CreateNode(resource);
if (result != null)
return result;
break;
}
var streamData = data as Stream;
if (streamData != null)
result = new ResourceEntryNode(key, data as Stream);
return result ?? new ResourceTreeNode(resource);
}
return result;
public static ILSpyTreeNode Create(string name, byte[] data)
{
return Create(new ByteArrayResource(name, data));
}
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
using var data = OpenStream();
language.WriteCommentLine(output, string.Format("{0} = {1}", key, data));
}
@ -77,11 +83,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -77,11 +83,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
dlg.FileName = Path.GetFileName(DecompilerTextView.CleanUpName(key));
if (dlg.ShowDialog() == true)
{
data.Position = 0;
using (var fs = dlg.OpenFile())
{
data.CopyTo(fs);
}
using var data = OpenStream();
using var fs = dlg.OpenFile();
data.CopyTo(fs);
}
return true;
}

12
ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs

@ -41,7 +41,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -41,7 +41,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
{
public ResourceTreeNode(Resource r)
{
if (r.IsNil)
if (r == null)
throw new ArgumentNullException(nameof(r));
this.Resource = r;
}
@ -85,6 +85,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -85,6 +85,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
{
s.Position = 0;
AvalonEditTextOutput output = new AvalonEditTextOutput();
output.Title = Resource.Name;
output.Write(FileReader.OpenStream(s, Encoding.UTF8).ReadToEnd());
string ext;
if (type == FileType.Xml)
@ -119,14 +120,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -119,14 +120,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
public static ILSpyTreeNode Create(Resource resource)
{
ILSpyTreeNode result = null;
foreach (var factory in App.ExportProvider.GetExportedValues<IResourceNodeFactory>())
{
result = factory.CreateNode(resource);
if (result != null)
break;
}
return result ?? new ResourceTreeNode(resource);
return ResourceEntryNode.Create(resource);
}
}
}

9
ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs

@ -99,14 +99,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -99,14 +99,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (entry.Value is byte[])
{
Children.Add(ResourceEntryNode.Create(entry.Key, new MemoryStream((byte[])entry.Value)));
return;
}
var node = ResourceEntryNode.Create(entry.Key, entry.Value);
if (node != null)
{
Children.Add(node);
Children.Add(ResourceEntryNode.Create(entry.Key, (byte[])entry.Value));
return;
}

98
ILSpy/TreeNodes/ResourceNodes/TextResourceNode.cs

@ -1,98 +0,0 @@ @@ -1,98 +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.ComponentModel.Composition;
using System.IO;
using System.Threading.Tasks;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.ViewModels;
namespace ICSharpCode.ILSpy.TreeNodes
{
[Export(typeof(IResourceNodeFactory))]
sealed class TextResourceNodeFactory : IResourceNodeFactory
{
private readonly static string[] txtFileExtensions = { ".txt", ".md" };
public ILSpyTreeNode CreateNode(Resource resource)
{
Stream stream = resource.TryOpenStream();
if (stream == null)
return null;
return CreateNode(resource.Name, stream);
}
public ILSpyTreeNode CreateNode(string key, object data)
{
if (!(data is Stream))
return null;
foreach (string fileExt in txtFileExtensions)
{
if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase))
return new TextResourceEntryNode(key, (Stream)data);
}
return null;
}
}
sealed class TextResourceEntryNode : ResourceEntryNode
{
string text;
public TextResourceEntryNode(string key, Stream data)
: base(key, data)
{
}
public override object Icon => Images.Resource;
public override bool View(TabPageModel tabPage)
{
AvalonEditTextOutput output = new AvalonEditTextOutput();
IHighlightingDefinition highlighting = null;
tabPage.ShowTextView(textView => textView.RunWithCancellation(
token => Task.Factory.StartNew(
() => {
try {
// cache read text because stream will be closed after first read
if (text == null) {
using (var reader = new StreamReader(Data)) {
text = reader.ReadToEnd();
}
}
output.Write(text);
highlighting = null;
}
catch (Exception ex) {
output.Write(ex.ToString());
}
return output;
}, token)
).Then(t => textView.ShowNode(t, this, highlighting))
.HandleExceptions());
tabPage.SupportsLanguageSwitching = false;
return true;
}
}
}

20
ILSpy/TreeNodes/ResourceNodes/XamlResourceNode.cs

@ -34,13 +34,8 @@ namespace ICSharpCode.ILSpy.Xaml @@ -34,13 +34,8 @@ namespace ICSharpCode.ILSpy.Xaml
{
public ILSpyTreeNode CreateNode(Resource resource)
{
return null;
}
public ILSpyTreeNode CreateNode(string key, object data)
{
if (key.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase) && data is Stream)
return new XamlResourceEntryNode(key, (Stream)data);
if (resource.Name.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
return new XamlResourceEntryNode(resource.Name, resource.TryOpenStream);
else
return null;
}
@ -50,7 +45,7 @@ namespace ICSharpCode.ILSpy.Xaml @@ -50,7 +45,7 @@ namespace ICSharpCode.ILSpy.Xaml
{
string xaml;
public XamlResourceEntryNode(string key, Stream data) : base(key, data)
public XamlResourceEntryNode(string key, Func<Stream> openStream) : base(key, openStream)
{
}
@ -67,7 +62,14 @@ namespace ICSharpCode.ILSpy.Xaml @@ -67,7 +62,14 @@ namespace ICSharpCode.ILSpy.Xaml
// cache read XAML because stream will be closed after first read
if (xaml == null)
{
using (var reader = new StreamReader(Data))
using var data = OpenStream();
if (data == null)
{
output.Write("ILSpy: Failed opening resource stream.");
output.WriteLine();
return output;
}
using (var reader = new StreamReader(data))
{
xaml = reader.ReadToEnd();
}

24
ILSpy/TreeNodes/ResourceNodes/XmlResourceNode.cs

@ -36,20 +36,11 @@ namespace ICSharpCode.ILSpy.Xaml @@ -36,20 +36,11 @@ namespace ICSharpCode.ILSpy.Xaml
public ILSpyTreeNode CreateNode(Resource resource)
{
Stream stream = resource.TryOpenStream();
if (stream == null)
return null;
return CreateNode(resource.Name, stream);
}
public ILSpyTreeNode CreateNode(string key, object data)
{
if (!(data is Stream))
return null;
string key = resource.Name;
foreach (string fileExt in xmlFileExtensions)
{
if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase))
return new XmlResourceEntryNode(key, (Stream)data);
return new XmlResourceEntryNode(key, resource.TryOpenStream);
}
return null;
}
@ -59,7 +50,7 @@ namespace ICSharpCode.ILSpy.Xaml @@ -59,7 +50,7 @@ namespace ICSharpCode.ILSpy.Xaml
{
string xml;
public XmlResourceEntryNode(string key, Stream data)
public XmlResourceEntryNode(string key, Func<Stream> data)
: base(key, data)
{
}
@ -91,7 +82,14 @@ namespace ICSharpCode.ILSpy.Xaml @@ -91,7 +82,14 @@ namespace ICSharpCode.ILSpy.Xaml
// cache read XAML because stream will be closed after first read
if (xml == null)
{
using (var reader = new StreamReader(Data))
using var data = OpenStream();
if (data == null)
{
output.Write("ILSpy: Failed opening resource stream.");
output.WriteLine();
return output;
}
using (var reader = new StreamReader(data))
{
xml = reader.ReadToEnd();
}

40
ILSpy/Views/NugetPackageBrowserDialog.xaml

@ -1,40 +0,0 @@ @@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Window
x:Class="ICSharpCode.ILSpy.NugetPackageBrowserDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:ICSharpCode.ILSpy.Controls"
xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
xmlns:treeview="http://icsharpcode.net/sharpdevelop/treeview"
Title="{x:Static properties:Resources.NugetPackageBrowser}"
Style="{DynamicResource DialogWindow}"
WindowStartupLocation="CenterOwner"
ResizeMode="CanResizeWithGrip"
MinWidth="200"
MinHeight="150"
Height="350"
Width="750">
<Grid
Margin="12,8">
<Grid.RowDefinitions>
<RowDefinition
Height="Auto" />
<RowDefinition
Height="1*" />
<RowDefinition
Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{x:Static properties:Resources.SelectAssembliesOpen}" Margin="5" />
<ListBox ItemsSource="{Binding Package.Entries}" Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsSelected}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right">
<Button IsDefault="True" Margin="2,0" IsEnabled="{Binding HasSelection}" Name="okButton" Click="OKButton_Click" Content="{x:Static properties:Resources.Open}"/>
<Button IsCancel="True" Margin="2,0" Content="{x:Static properties:Resources.Cancel}"/>
</StackPanel>
</Grid>
</Window>

86
ILSpy/Views/NugetPackageBrowserDialog.xaml.cs

@ -1,86 +0,0 @@ @@ -1,86 +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.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using ICSharpCode.ILSpy.Controls;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// Interaction logic for NugetPackageBrowserDialog.xaml
/// </summary>
public partial class NugetPackageBrowserDialog : Window, INotifyPropertyChanged
{
public LoadedNugetPackage Package { get; }
public NugetPackageBrowserDialog()
{
InitializeComponent();
}
public NugetPackageBrowserDialog(LoadedNugetPackage package)
{
InitializeComponent();
this.Package = package;
this.Package.PropertyChanged += Package_PropertyChanged;
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
void Package_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Package.SelectedEntries))
{
OnPropertyChanged(new PropertyChangedEventArgs("HasSelection"));
}
}
void OKButton_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
Close();
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
public Entry[] SelectedItems {
get {
return Package.SelectedEntries.ToArray();
}
}
public bool HasSelection {
get { return SelectedItems.Length > 0; }
}
}
}

5
packages.props

@ -1,10 +1,13 @@ @@ -1,10 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<!-- Centrally define the versions of the NuGet packages we use -->
<!-- Centrally define the versions of the NuGet packages we use.
Note: when updating these, ensure to also adjust the binding redirects in app.config.template appropriately.
-->
<PropertyGroup>
<HumanizerVersion>2.2.0</HumanizerVersion>
<SystemCollectionsImmutableVersion>5.0.0-preview.8.20407.11</SystemCollectionsImmutableVersion>
<SystemReflectionMetadataVersion>5.0.0-preview.8.20407.11</SystemReflectionMetadataVersion>
<SystemCompilerServicesUnsafeVersion>4.7.1</SystemCompilerServicesUnsafeVersion>
<ILAsmVersion>5.0.0-rc.2.20475.5</ILAsmVersion> <!-- Microsoft.NETCore.ILAsm -->
<RoslynVersion>3.8.0-4.final</RoslynVersion> <!-- Microsoft.CodeAnalysis.* -->
<MonoCecilVersion>0.10.3</MonoCecilVersion>

Loading…
Cancel
Save