Browse Source

Basic implementation of DotNetCorePathFinder

pull/848/head
Siegfried Pammer 8 years ago
parent
commit
7df1b2908d
  1. 116
      ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinder.cs
  2. 33
      ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinderExtensions.cs
  3. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  4. 43
      ILSpy/LoadedAssembly.cs

116
ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinder.cs

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using Mono.Cecil;
using Newtonsoft.Json.Linq;
namespace ICSharpCode.Decompiler
{
public class DotNetCorePathFinder
{
class DotNetCorePackageInfo
{
public readonly string Name;
public readonly string Version;
public readonly string Type;
public readonly string Path;
public readonly string[] RuntimeComponents;
public DotNetCorePackageInfo(string fullName, string type, string path, string[] runtimeComponents)
{
var parts = fullName.Split('/');
this.Name = parts[0];
this.Version = parts[1];
this.Type = type;
this.Path = path;
this.RuntimeComponents = runtimeComponents ?? new string[0];
}
}
static readonly string[] LookupPaths = new string[] {
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget\\packages")
};
readonly Dictionary<string, DotNetCorePackageInfo> packages;
ISet<string> packageBasePaths = new HashSet<string>(StringComparer.Ordinal);
readonly string assemblyName;
readonly string basePath;
readonly string targetFrameworkId;
readonly string version;
public DotNetCorePathFinder(string parentAssemblyFileName, string targetFrameworkId, string version)
{
this.assemblyName = Path.GetFileNameWithoutExtension(parentAssemblyFileName);
this.basePath = Path.GetDirectoryName(parentAssemblyFileName);
this.targetFrameworkId = targetFrameworkId;
this.version = version;
DecompilerEventSource.Log.Info($"Looking for {assemblyName}.deps.json in {basePath}");
var depsJsonFileName = Path.Combine(basePath, $"{assemblyName}.deps.json");
if (!File.Exists(depsJsonFileName)) {
DecompilerEventSource.Log.Info($"{assemblyName}.deps.json not found.");
return;
}
packages = LoadPackageInfos(depsJsonFileName, targetFrameworkId).ToDictionary(i => i.Name);
foreach (var path in LookupPaths) {
foreach (var pk in packages) {
foreach (var item in pk.Value.RuntimeComponents) {
var itemPath = Path.GetDirectoryName(item);
var fullPath = Path.Combine(path, pk.Value.Name, pk.Value.Version, itemPath);
if (Directory.Exists(fullPath))
packageBasePaths.Add(fullPath);
}
}
}
}
public string TryResolveDotNetCore(AssemblyNameReference name)
{
DecompilerEventSource.Log.Info($"TryResolveDotNetCore: {assemblyName}, version: {version}, reference: {name}");
foreach (var basePath in packageBasePaths) {
DecompilerEventSource.Log.Info($"TryResolveDotNetCore: scanning {basePath}...");
if (File.Exists(Path.Combine(basePath, name.Name + ".dll"))) {
return Path.Combine(basePath, name.Name + ".dll");
} else if (File.Exists(Path.Combine(basePath, name.Name + ".exe"))) {
return Path.Combine(basePath, name.Name + ".exe");
}
}
return FallbackToDotNetSharedDirectory(name, version);
}
static IEnumerable<DotNetCorePackageInfo> LoadPackageInfos(string depsJsonFileName, string targetFramework)
{
var dependencies = JObject.Parse(File.ReadAllText(depsJsonFileName));
var runtimeInfos = dependencies["targets"][targetFramework].Children().OfType<JProperty>().ToArray();
var libraries = dependencies["libraries"].Children().OfType<JProperty>().ToArray();
foreach (var library in libraries) {
var type = library.First()["type"].ToString();
var path = library.First()["path"]?.ToString();
var runtimeInfo = runtimeInfos.FirstOrDefault(r => r.Name == library.Name)?.First()["runtime"]?.Children().OfType<JProperty>().Select(i => i.Name).ToArray();
yield return new DotNetCorePackageInfo(library.Name, type, path, runtimeInfo);
}
}
static string FallbackToDotNetSharedDirectory(AssemblyNameReference name, string version)
{
var basePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet\\shared\\Microsoft.NETCore.App", version);
DecompilerEventSource.Log.Info($"Fallback: use {basePath}");
if (File.Exists(Path.Combine(basePath, name.Name + ".dll"))) {
return Path.Combine(basePath, name.Name + ".dll");
} else if (File.Exists(Path.Combine(basePath, name.Name + ".exe"))) {
return Path.Combine(basePath, name.Name + ".exe");
}
return null;
}
}
}

33
ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinderExtensions.cs

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using Mono.Cecil;
using Newtonsoft.Json.Linq;
namespace ICSharpCode.Decompiler
{
public static class DotNetCorePathFinderExtensions
{
public static string DetectTargetFrameworkId(this AssemblyDefinition assembly)
{
if (assembly == null)
throw new ArgumentNullException(nameof(assembly));
const string TargetFrameworkAttributeName = "System.Runtime.Versioning.TargetFrameworkAttribute";
foreach (var attribute in assembly.CustomAttributes) {
if (attribute.AttributeType.FullName != TargetFrameworkAttributeName)
continue;
var blobReader = new BlobReader(attribute.GetBlob(), null);
if (blobReader.ReadUInt16() == 0x0001) {
return blobReader.ReadSerString();
}
}
return string.Empty;
}
}
}

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -45,6 +45,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="System.Collections.Immutable" Version="1.3.0" /> <PackageReference Include="System.Collections.Immutable" Version="1.3.0" />
<PackageReference Include="System.Diagnostics.Tracing" Version="4.3.0" /> <PackageReference Include="System.Diagnostics.Tracing" Version="4.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.3.0" /> <PackageReference Include="System.ValueTuple" Version="4.3.0" />
@ -259,6 +260,7 @@
<Compile Include="Documentation\IdStringProvider.cs" /> <Compile Include="Documentation\IdStringProvider.cs" />
<Compile Include="Documentation\XmlDocumentationProvider.cs" /> <Compile Include="Documentation\XmlDocumentationProvider.cs" />
<Compile Include="DecompilerEventSource.cs" /> <Compile Include="DecompilerEventSource.cs" />
<Compile Include="DotNetCore\DotNetCorePathFinder.cs" />
<Compile Include="IL\ControlFlow\AsyncAwaitDecompiler.cs" /> <Compile Include="IL\ControlFlow\AsyncAwaitDecompiler.cs" />
<Compile Include="IL\ControlFlow\ControlFlowGraph.cs" /> <Compile Include="IL\ControlFlow\ControlFlowGraph.cs" />
<Compile Include="IL\ControlFlow\StateRangeAnalysis.cs" /> <Compile Include="IL\ControlFlow\StateRangeAnalysis.cs" />

43
ILSpy/LoadedAssembly.cs

@ -20,6 +20,7 @@ using System;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Threading; using System.Windows.Threading;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpy.Options;
using Mono.Cecil; using Mono.Cecil;
@ -34,7 +35,8 @@ namespace ICSharpCode.ILSpy
readonly AssemblyList assemblyList; readonly AssemblyList assemblyList;
readonly string fileName; readonly string fileName;
readonly string shortName; readonly string shortName;
readonly Lazy<string> targetFrameworkId;
public LoadedAssembly(AssemblyList assemblyList, string fileName, Stream stream = null) public LoadedAssembly(AssemblyList assemblyList, string fileName, Stream stream = null)
{ {
if (assemblyList == null) if (assemblyList == null)
@ -46,8 +48,11 @@ namespace ICSharpCode.ILSpy
this.assemblyTask = Task.Factory.StartNew<ModuleDefinition>(LoadAssembly, stream); // requires that this.fileName is set this.assemblyTask = Task.Factory.StartNew<ModuleDefinition>(LoadAssembly, stream); // requires that this.fileName is set
this.shortName = Path.GetFileNameWithoutExtension(fileName); this.shortName = Path.GetFileNameWithoutExtension(fileName);
this.targetFrameworkId = new Lazy<string>(AssemblyDefinition.DetectTargetFrameworkId, false);
} }
public string TargetFrameworkId => targetFrameworkId.Value;
/// <summary> /// <summary>
/// Gets the Cecil ModuleDefinition. /// Gets the Cecil ModuleDefinition.
/// Can be null when there was a load error. /// Can be null when there was a load error.
@ -234,23 +239,47 @@ namespace ICSharpCode.ILSpy
{ {
return assemblyList.assemblyLookupCache.GetOrAdd(fullName, LookupReferencedAssemblyInternal); return assemblyList.assemblyLookupCache.GetOrAdd(fullName, LookupReferencedAssemblyInternal);
} }
DotNetCorePathFinder dotNetCorePathFinder;
LoadedAssembly LookupReferencedAssemblyInternal(string fullName) LoadedAssembly LookupReferencedAssemblyInternal(string fullName)
{ {
DecompilerEventSource.Log.Info($"Looking for {fullName}...");
foreach (LoadedAssembly asm in assemblyList.GetAssemblies()) { foreach (LoadedAssembly asm in assemblyList.GetAssemblies()) {
if (asm.AssemblyDefinition != null && fullName.Equals(asm.AssemblyDefinition.FullName, StringComparison.OrdinalIgnoreCase)) if (asm.AssemblyDefinition != null && fullName.Equals(asm.AssemblyDefinition.FullName, StringComparison.OrdinalIgnoreCase)) {
DecompilerEventSource.Log.Info($"Found in list of loaded assemblies.");
return asm; return asm;
}
} }
if (assemblyLoadDisableCount > 0) if (assemblyLoadDisableCount > 0)
return null; return null;
if (!App.Current.Dispatcher.CheckAccess()) { if (!App.Current.Dispatcher.CheckAccess()) {
DecompilerEventSource.Log.Info($"Retry on UI thread...");
// Call this method on the GUI thread. // Call this method on the GUI thread.
return (LoadedAssembly)App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Func<string, LoadedAssembly>(LookupReferencedAssembly), fullName); return (LoadedAssembly)App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Func<string, LoadedAssembly>(LookupReferencedAssembly), fullName);
} }
DecompilerEventSource.Log.Info($"Detected target framework: {TargetFrameworkId}.");
var targetFramework = TargetFrameworkId.Split(new[] { ",Version=v" }, StringSplitOptions.None);
var name = AssemblyNameReference.Parse(fullName); var name = AssemblyNameReference.Parse(fullName);
string file = GacInterop.FindAssemblyInNetGac(name); string file = null;
switch (targetFramework[0]) {
case ".NETCoreApp":
case ".NETStandard":
if (targetFramework.Length != 2) break;
if (dotNetCorePathFinder == null) {
var version = targetFramework[1].Length == 3 ? targetFramework[1] + ".0" : targetFramework[1];
dotNetCorePathFinder = new DotNetCorePathFinder(fileName, TargetFrameworkId, version);
}
file = dotNetCorePathFinder.TryResolveDotNetCore(name);
break;
default:
file = GacInterop.FindAssemblyInNetGac(name);
break;
}
if (file == null) { if (file == null) {
string dir = Path.GetDirectoryName(this.fileName); string dir = Path.GetDirectoryName(this.fileName);
if (File.Exists(Path.Combine(dir, name.Name + ".dll"))) if (File.Exists(Path.Combine(dir, name.Name + ".dll")))
@ -259,8 +288,8 @@ namespace ICSharpCode.ILSpy
file = Path.Combine(dir, name.Name + ".exe"); file = Path.Combine(dir, name.Name + ".exe");
} }
if (file != null) { if (file != null) {
var loaded = assemblyList.OpenAssembly(file, true); DecompilerEventSource.Log.Info($"Success - Loading {file}...");
return loaded; return assemblyList.OpenAssembly(file, true);
} else { } else {
return null; return null;
} }

Loading…
Cancel
Save