mirror of https://github.com/icsharpcode/ILSpy.git
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							306 lines
						
					
					
						
							8.4 KiB
						
					
					
				
			
		
		
	
	
							306 lines
						
					
					
						
							8.4 KiB
						
					
					
				// Copyright (c) 2018 Siegfried Pammer | 
						|
//  | 
						|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this | 
						|
// software and associated documentation files (the "Software"), to deal in the Software | 
						|
// without restriction, including without limitation the rights to use, copy, modify, merge, | 
						|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons | 
						|
// to whom the Software is furnished to do so, subject to the following conditions: | 
						|
//  | 
						|
// The above copyright notice and this permission notice shall be included in all copies or | 
						|
// substantial portions of the Software. | 
						|
//  | 
						|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | 
						|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR | 
						|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE | 
						|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | 
						|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
						|
// DEALINGS IN THE SOFTWARE. | 
						|
 | 
						|
#nullable enable | 
						|
 | 
						|
using System; | 
						|
using System.Diagnostics.CodeAnalysis; | 
						|
using System.Linq; | 
						|
using System.Reflection; | 
						|
using System.Reflection.Metadata; | 
						|
using System.Security.Cryptography; | 
						|
using System.Text; | 
						|
using System.Threading.Tasks; | 
						|
 | 
						|
namespace ICSharpCode.Decompiler.Metadata | 
						|
{ | 
						|
	public sealed class ResolutionException : Exception | 
						|
	{ | 
						|
		public IAssemblyReference? Reference { get; } | 
						|
 | 
						|
		public string? ModuleName { get; } | 
						|
 | 
						|
		public string? MainModuleFullPath { get; } | 
						|
 | 
						|
		public string? ResolvedFullPath { get; } | 
						|
 | 
						|
		public ResolutionException(IAssemblyReference reference, string? resolvedPath, Exception? innerException) | 
						|
			: base($"Failed to resolve assembly: '{reference}'{Environment.NewLine}" + | 
						|
				  $"Resolve result: {resolvedPath ?? "<not found>"}", innerException) | 
						|
		{ | 
						|
			this.Reference = reference ?? throw new ArgumentNullException(nameof(reference)); | 
						|
			this.ResolvedFullPath = resolvedPath; | 
						|
		} | 
						|
 | 
						|
		public ResolutionException(string mainModule, string moduleName, string? resolvedPath, Exception? innerException) | 
						|
			: base($"Failed to resolve module: '{moduleName} of {mainModule}'{Environment.NewLine}" + | 
						|
				  $"Resolve result: {resolvedPath ?? "<not found>"}", innerException) | 
						|
		{ | 
						|
			this.MainModuleFullPath = mainModule ?? throw new ArgumentNullException(nameof(mainModule)); | 
						|
			this.ModuleName = moduleName ?? throw new ArgumentNullException(nameof(moduleName)); | 
						|
			this.ResolvedFullPath = resolvedPath; | 
						|
		} | 
						|
	} | 
						|
 | 
						|
	public interface IAssemblyResolver | 
						|
	{ | 
						|
#if !VSADDIN | 
						|
		PEFile? Resolve(IAssemblyReference reference); | 
						|
		PEFile? ResolveModule(PEFile mainModule, string moduleName); | 
						|
		Task<PEFile?> ResolveAsync(IAssemblyReference reference); | 
						|
		Task<PEFile?> ResolveModuleAsync(PEFile mainModule, string moduleName); | 
						|
#endif | 
						|
	} | 
						|
 | 
						|
	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, [NotNullWhen(true)] out string? runtimePack) | 
						|
		{ | 
						|
			runtimePack = null; | 
						|
			return false; | 
						|
		} | 
						|
	} | 
						|
 | 
						|
	public interface IAssemblyReference | 
						|
	{ | 
						|
		string Name { get; } | 
						|
		string FullName { get; } | 
						|
		Version? Version { get; } | 
						|
		string? Culture { get; } | 
						|
		byte[]? PublicKeyToken { get; } | 
						|
 | 
						|
		bool IsWindowsRuntime { get; } | 
						|
		bool IsRetargetable { get; } | 
						|
	} | 
						|
 | 
						|
	public class AssemblyNameReference : IAssemblyReference | 
						|
	{ | 
						|
		string? fullName; | 
						|
 | 
						|
		public string Name { get; private set; } = string.Empty; | 
						|
 | 
						|
		public string FullName { | 
						|
			get { | 
						|
				if (fullName != null) | 
						|
					return fullName; | 
						|
 | 
						|
				const string sep = ", "; | 
						|
 | 
						|
				var builder = new StringBuilder(); | 
						|
				builder.Append(Name); | 
						|
				builder.Append(sep); | 
						|
				builder.Append("Version="); | 
						|
				builder.Append((Version ?? UniversalAssemblyResolver.ZeroVersion).ToString(fieldCount: 4)); | 
						|
				builder.Append(sep); | 
						|
				builder.Append("Culture="); | 
						|
				builder.Append(string.IsNullOrEmpty(Culture) ? "neutral" : Culture); | 
						|
				builder.Append(sep); | 
						|
				builder.Append("PublicKeyToken="); | 
						|
 | 
						|
				var pk_token = PublicKeyToken; | 
						|
				if (pk_token != null && pk_token.Length > 0) | 
						|
				{ | 
						|
					for (int i = 0; i < pk_token.Length; i++) | 
						|
					{ | 
						|
						builder.Append(pk_token[i].ToString("x2")); | 
						|
					} | 
						|
				} | 
						|
				else | 
						|
					builder.Append("null"); | 
						|
 | 
						|
				if (IsRetargetable) | 
						|
				{ | 
						|
					builder.Append(sep); | 
						|
					builder.Append("Retargetable=Yes"); | 
						|
				} | 
						|
 | 
						|
				return fullName = builder.ToString(); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		public Version? Version { get; private set; } | 
						|
 | 
						|
		public string? Culture { get; private set; } | 
						|
 | 
						|
		public byte[]? PublicKeyToken { get; private set; } | 
						|
 | 
						|
		public bool IsWindowsRuntime { get; private set; } | 
						|
 | 
						|
		public bool IsRetargetable { get; private set; } | 
						|
 | 
						|
		public static AssemblyNameReference Parse(string fullName) | 
						|
		{ | 
						|
			if (fullName == null) | 
						|
				throw new ArgumentNullException(nameof(fullName)); | 
						|
			if (fullName.Length == 0) | 
						|
				throw new ArgumentException("Name can not be empty"); | 
						|
 | 
						|
			var name = new AssemblyNameReference(); | 
						|
			var tokens = fullName.Split(','); | 
						|
			for (int i = 0; i < tokens.Length; i++) | 
						|
			{ | 
						|
				var token = tokens[i].Trim(); | 
						|
 | 
						|
				if (i == 0) | 
						|
				{ | 
						|
					name.Name = token; | 
						|
					continue; | 
						|
				} | 
						|
 | 
						|
				var parts = token.Split('='); | 
						|
				if (parts.Length != 2) | 
						|
					throw new ArgumentException("Malformed name"); | 
						|
 | 
						|
				switch (parts[0].ToLowerInvariant()) | 
						|
				{ | 
						|
					case "version": | 
						|
						name.Version = new Version(parts[1]); | 
						|
						break; | 
						|
					case "culture": | 
						|
						name.Culture = parts[1] == "neutral" ? "" : parts[1]; | 
						|
						break; | 
						|
					case "publickeytoken": | 
						|
						var pk_token = parts[1]; | 
						|
						if (pk_token == "null") | 
						|
							break; | 
						|
 | 
						|
						name.PublicKeyToken = new byte[pk_token.Length / 2]; | 
						|
						for (int j = 0; j < name.PublicKeyToken.Length; j++) | 
						|
							name.PublicKeyToken[j] = Byte.Parse(pk_token.Substring(j * 2, 2), System.Globalization.NumberStyles.HexNumber); | 
						|
 | 
						|
						break; | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			return name; | 
						|
		} | 
						|
 | 
						|
		public override string ToString() | 
						|
		{ | 
						|
			return FullName; | 
						|
		} | 
						|
	} | 
						|
 | 
						|
#if !VSADDIN | 
						|
	public class AssemblyReference : IAssemblyReference | 
						|
	{ | 
						|
		static readonly SHA1 sha1 = SHA1.Create(); | 
						|
 | 
						|
		readonly System.Reflection.Metadata.AssemblyReference entry; | 
						|
 | 
						|
		public MetadataReader Metadata { get; } | 
						|
		public AssemblyReferenceHandle Handle { get; } | 
						|
 | 
						|
		public bool IsWindowsRuntime => (entry.Flags & AssemblyFlags.WindowsRuntime) != 0; | 
						|
		public bool IsRetargetable => (entry.Flags & AssemblyFlags.Retargetable) != 0; | 
						|
 | 
						|
		string? name; | 
						|
		string? fullName; | 
						|
 | 
						|
		public string Name { | 
						|
			get { | 
						|
				if (name == null) | 
						|
				{ | 
						|
					try | 
						|
					{ | 
						|
						name = Metadata.GetString(entry.Name); | 
						|
					} | 
						|
					catch (BadImageFormatException) | 
						|
					{ | 
						|
						name = $"AR:{Handle}"; | 
						|
					} | 
						|
				} | 
						|
				return name; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		public string FullName { | 
						|
			get { | 
						|
				if (fullName == null) | 
						|
				{ | 
						|
					try | 
						|
					{ | 
						|
						fullName = entry.GetFullAssemblyName(Metadata); | 
						|
					} | 
						|
					catch (BadImageFormatException) | 
						|
					{ | 
						|
						fullName = $"fullname(AR:{Handle})"; | 
						|
					} | 
						|
				} | 
						|
				return fullName; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		public Version? Version => entry.Version; | 
						|
		public string Culture => Metadata.GetString(entry.Culture); | 
						|
		byte[]? IAssemblyReference.PublicKeyToken => GetPublicKeyToken(); | 
						|
 | 
						|
		public byte[]? GetPublicKeyToken() | 
						|
		{ | 
						|
			if (entry.PublicKeyOrToken.IsNil) | 
						|
				return null; | 
						|
			var bytes = Metadata.GetBlobBytes(entry.PublicKeyOrToken); | 
						|
			if ((entry.Flags & AssemblyFlags.PublicKey) != 0) | 
						|
			{ | 
						|
				return sha1.ComputeHash(bytes).Skip(12).ToArray(); | 
						|
			} | 
						|
			return bytes; | 
						|
		} | 
						|
 | 
						|
		public AssemblyReference(MetadataReader metadata, AssemblyReferenceHandle handle) | 
						|
		{ | 
						|
			if (metadata == null) | 
						|
				throw new ArgumentNullException(nameof(metadata)); | 
						|
			if (handle.IsNil) | 
						|
				throw new ArgumentNullException(nameof(handle)); | 
						|
			Metadata = metadata; | 
						|
			Handle = handle; | 
						|
			entry = metadata.GetAssemblyReference(handle); | 
						|
		} | 
						|
 | 
						|
		public AssemblyReference(PEFile module, AssemblyReferenceHandle handle) | 
						|
		{ | 
						|
			if (module == null) | 
						|
				throw new ArgumentNullException(nameof(module)); | 
						|
			if (handle.IsNil) | 
						|
				throw new ArgumentNullException(nameof(handle)); | 
						|
			Metadata = module.Metadata; | 
						|
			Handle = handle; | 
						|
			entry = Metadata.GetAssemblyReference(handle); | 
						|
		} | 
						|
 | 
						|
		public override string ToString() | 
						|
		{ | 
						|
			return FullName; | 
						|
		} | 
						|
	} | 
						|
#endif | 
						|
}
 | 
						|
 |