// 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.IO;
using System.Threading.Tasks;
//using System.Windows.Threading;
//using ICSharpCode.ILSpy.Options;
using Mono.Cecil;
namespace ICSharpCode.ILSpy
{
///
/// Represents an assembly loaded into ILSpy.
///
public sealed class LoadedAssembly
{
readonly Task assemblyTask;
readonly AssemblyList assemblyList;
readonly string fileName;
readonly string shortName;
public Guid ProjectGuid{ get; set;}
public string ProjectFileName{ get; set; }
public LoadedAssembly(AssemblyList assemblyList, string fileName, Stream stream = null)
{
if (assemblyList == null)
throw new ArgumentNullException("assemblyList");
if (fileName == null)
throw new ArgumentNullException("fileName");
this.assemblyList = assemblyList;
this.fileName = fileName;
this.assemblyTask = Task.Factory.StartNew(LoadAssembly, stream); // requires that this.fileName is set
this.shortName = Path.GetFileNameWithoutExtension(fileName);
}
///
/// Gets the Cecil ModuleDefinition.
/// Can be null when there was a load error.
///
public ModuleDefinition ModuleDefinition {
get {
try {
return assemblyTask.Result;
} catch (AggregateException) {
return null;
}
}
}
///
/// Gets the Cecil AssemblyDefinition.
/// Is null when there was a load error; or when opening a netmodule.
///
public AssemblyDefinition AssemblyDefinition {
get {
var module = this.ModuleDefinition;
return module != null ? module.Assembly : null;
}
}
public AssemblyList AssemblyList {
get { return assemblyList; }
}
public string FileName {
get { return fileName; }
}
public string ShortName {
get { return shortName; }
}
public string Text {
get {
if (AssemblyDefinition != null) {
return String.Format("{0} ({1})", ShortName, AssemblyDefinition.Name.Version);
} else {
return ShortName;
}
}
}
public bool IsLoaded {
get { return assemblyTask.IsCompleted; }
}
public bool HasLoadError {
get { return assemblyTask.IsFaulted; }
}
public bool IsAutoLoaded { get; set; }
ModuleDefinition LoadAssembly(object state)
{
var stream = state as Stream;
ModuleDefinition module;
// runs on background thread
ReaderParameters p = new ReaderParameters();
p.AssemblyResolver = new MyAssemblyResolver(this);
if (stream != null)
{
// Read the module from a precrafted stream
module = ModuleDefinition.ReadModule(stream, p);
}
else
{
// Read the module from disk (by default)
module = ModuleDefinition.ReadModule(fileName, p);
}
//if (DecompilerSettingsPanel.CurrentDecompilerSettings.UseDebugSymbols) {
// try {
// LoadSymbols(module);
// } catch (IOException) {
// } catch (UnauthorizedAccessException) {
// } catch (InvalidOperationException) {
// // ignore any errors during symbol loading
// }
//}
return module;
}
// private void LoadSymbols(ModuleDefinition module)
// {
// // search for pdb in same directory as dll
// string pdbName = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName) + ".pdb");
// if (File.Exists(pdbName)) {
// using (Stream s = File.OpenRead(pdbName)) {
// module.ReadSymbols(new Mono.Cecil.Pdb.PdbReaderProvider().GetSymbolReader(module, s));
// }
// return;
// }
//
// // TODO: use symbol cache, get symbols from microsoft
// }
[ThreadStatic]
static int assemblyLoadDisableCount;
public static IDisposable DisableAssemblyLoad()
{
assemblyLoadDisableCount++;
return new DecrementAssemblyLoadDisableCount();
}
sealed class DecrementAssemblyLoadDisableCount : IDisposable
{
bool disposed;
public void Dispose()
{
if (!disposed) {
disposed = true;
assemblyLoadDisableCount--;
// clear the lookup cache since we might have stored the lookups failed due to DisableAssemblyLoad()
//MainWindow.Instance.CurrentAssemblyList.ClearCache();
}
}
}
sealed class MyAssemblyResolver : IAssemblyResolver
{
readonly LoadedAssembly parent;
public MyAssemblyResolver(LoadedAssembly parent)
{
this.parent = parent;
}
public AssemblyDefinition Resolve(AssemblyNameReference name)
{
var node = parent.LookupReferencedAssembly(name);
return node != null ? node.AssemblyDefinition : null;
}
public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
{
var node = parent.LookupReferencedAssembly(name);
return node != null ? node.AssemblyDefinition : null;
}
public AssemblyDefinition Resolve(string fullName)
{
var node = parent.LookupReferencedAssembly(fullName);
return node != null ? node.AssemblyDefinition : null;
}
public AssemblyDefinition Resolve(string fullName, ReaderParameters parameters)
{
var node = parent.LookupReferencedAssembly(fullName);
return node != null ? node.AssemblyDefinition : null;
}
}
public IAssemblyResolver GetAssemblyResolver()
{
return new MyAssemblyResolver(this);
}
public LoadedAssembly LookupReferencedAssembly(AssemblyNameReference name)
{
if (name == null)
throw new ArgumentNullException("name");
if (name.IsWindowsRuntime) {
return assemblyList.winRTMetadataLookupCache.GetOrAdd(name.Name, LookupWinRTMetadata);
} else {
return assemblyList.assemblyLookupCache.GetOrAdd(name.FullName, LookupReferencedAssemblyInternal);
}
}
public LoadedAssembly LookupReferencedAssembly(string fullName)
{
return assemblyList.assemblyLookupCache.GetOrAdd(fullName, LookupReferencedAssemblyInternal);
}
LoadedAssembly LookupReferencedAssemblyInternal(string fullName)
{
foreach (LoadedAssembly asm in assemblyList.GetAssemblies()) {
if (asm.AssemblyDefinition != null && fullName.Equals(asm.AssemblyDefinition.FullName, StringComparison.OrdinalIgnoreCase))
return asm;
}
if (assemblyLoadDisableCount > 0)
return null;
// if (!App.Current.Dispatcher.CheckAccess()) {
// // Call this method on the GUI thread.
// return (LoadedAssembly)App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Func(LookupReferencedAssembly), fullName);
// }
var name = AssemblyNameReference.Parse(fullName);
string file = null; // GacInterop.FindAssemblyInNetGac(name);
if (file == null) {
string dir = Path.GetDirectoryName(this.fileName);
if (File.Exists(Path.Combine(dir, name.Name + ".dll")))
file = Path.Combine(dir, name.Name + ".dll");
else if (File.Exists(Path.Combine(dir, name.Name + ".exe")))
file = Path.Combine(dir, name.Name + ".exe");
}
if (file != null) {
var loaded = assemblyList.OpenAssembly(file, true);
return loaded;
} else {
return null;
}
}
LoadedAssembly LookupWinRTMetadata(string name)
{
foreach (LoadedAssembly asm in assemblyList.GetAssemblies()) {
if (asm.AssemblyDefinition != null && name.Equals(asm.AssemblyDefinition.Name.Name, StringComparison.OrdinalIgnoreCase))
return asm;
}
if (assemblyLoadDisableCount > 0)
return null;
// if (!App.Current.Dispatcher.CheckAccess()) {
// // Call this method on the GUI thread.
// return (LoadedAssembly)App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Func(LookupWinRTMetadata), name);
// }
string file = Path.Combine(Environment.SystemDirectory, "WinMetadata", name + ".winmd");
if (File.Exists(file)) {
return assemblyList.OpenAssembly(file, true);
} else {
return null;
}
}
public Task ContinueWhenLoaded(Action> onAssemblyLoaded, TaskScheduler taskScheduler)
{
return this.assemblyTask.ContinueWith(onAssemblyLoaded, taskScheduler);
}
///
/// Wait until the assembly is loaded.
/// Throws an AggregateException when loading the assembly fails.
///
public void WaitUntilLoaded()
{
assemblyTask.Wait();
}
}
}