添加dnlib源码
parent
8d917bd213
commit
b3ccb26003
Binary file not shown.
|
|
@ -0,0 +1,23 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
#if NET35
|
||||||
|
namespace System.Runtime.InteropServices {
|
||||||
|
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Method, AllowMultiple = false)]
|
||||||
|
sealed class DefaultDllImportSearchPathsAttribute : Attribute {
|
||||||
|
public DefaultDllImportSearchPathsAttribute(DllImportSearchPath paths) => _paths = paths;
|
||||||
|
public DllImportSearchPath Paths => _paths;
|
||||||
|
internal DllImportSearchPath _paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
enum DllImportSearchPath {
|
||||||
|
LegacyBehavior = 0,
|
||||||
|
AssemblyDirectory = 2,
|
||||||
|
UseDllDirectoryForDependencies = 0x100,
|
||||||
|
ApplicationDirectory = 0x200,
|
||||||
|
UserDirectories = 0x400,
|
||||||
|
System32 = 0x800,
|
||||||
|
SafeDirectories = 0x1000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Returns types without getting stuck in an infinite loop
|
||||||
|
/// </summary>
|
||||||
|
readonly struct AllTypesHelper {
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of all types and nested types
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="types">A list of types</param>
|
||||||
|
public static IEnumerable<TypeDef> Types(IEnumerable<TypeDef> types) {
|
||||||
|
var visited = new Dictionary<TypeDef, bool>();
|
||||||
|
var stack = new Stack<IEnumerator<TypeDef>>();
|
||||||
|
if (types is not null)
|
||||||
|
stack.Push(types.GetEnumerator());
|
||||||
|
while (stack.Count > 0) {
|
||||||
|
var enumerator = stack.Pop();
|
||||||
|
while (enumerator.MoveNext()) {
|
||||||
|
var type = enumerator.Current;
|
||||||
|
if (visited.ContainsKey(type))
|
||||||
|
continue;
|
||||||
|
visited[type] = true;
|
||||||
|
yield return type;
|
||||||
|
if (type.NestedTypes.Count > 0) {
|
||||||
|
stack.Push(enumerator);
|
||||||
|
enumerator = type.NestedTypes.GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Assembly flags from Assembly.Flags column.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>See CorHdr.h/CorAssemblyFlags</remarks>
|
||||||
|
[Flags]
|
||||||
|
public enum AssemblyAttributes : uint {
|
||||||
|
/// <summary>No flags set</summary>
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
/// <summary>The assembly ref holds the full (unhashed) public key.</summary>
|
||||||
|
PublicKey = 1,
|
||||||
|
|
||||||
|
/// <summary>Processor Architecture unspecified</summary>
|
||||||
|
PA_None = 0x0000,
|
||||||
|
/// <summary>Processor Architecture: neutral (PE32)</summary>
|
||||||
|
PA_MSIL = 0x0010,
|
||||||
|
/// <summary>Processor Architecture: x86 (PE32)</summary>
|
||||||
|
PA_x86 = 0x0020,
|
||||||
|
/// <summary>Processor Architecture: Itanium (PE32+)</summary>
|
||||||
|
PA_IA64 = 0x0030,
|
||||||
|
/// <summary>Processor Architecture: AMD X64 (PE32+)</summary>
|
||||||
|
PA_AMD64 = 0x0040,
|
||||||
|
/// <summary>Processor Architecture: ARM (PE32)</summary>
|
||||||
|
PA_ARM = 0x0050,
|
||||||
|
/// <summary>Processor Architecture: ARM64 (PE32+)</summary>
|
||||||
|
PA_ARM64 = 0x0060,
|
||||||
|
/// <summary>applies to any platform but cannot run on any (e.g. reference assembly), should not have "specified" set</summary>
|
||||||
|
PA_NoPlatform = 0x0070,
|
||||||
|
/// <summary>Propagate PA flags to AssemblyRef record</summary>
|
||||||
|
PA_Specified = 0x0080,
|
||||||
|
/// <summary>Bits describing the processor architecture</summary>
|
||||||
|
PA_Mask = 0x0070,
|
||||||
|
/// <summary>Bits describing the PA incl. Specified</summary>
|
||||||
|
PA_FullMask = 0x00F0,
|
||||||
|
/// <summary>NOT A FLAG, shift count in PA flags <--> index conversion</summary>
|
||||||
|
PA_Shift = 0x0004,
|
||||||
|
|
||||||
|
/// <summary>From "DebuggableAttribute".</summary>
|
||||||
|
EnableJITcompileTracking = 0x8000,
|
||||||
|
/// <summary>From "DebuggableAttribute".</summary>
|
||||||
|
DisableJITcompileOptimizer = 0x4000,
|
||||||
|
|
||||||
|
/// <summary>The assembly can be retargeted (at runtime) to an assembly from a different publisher.</summary>
|
||||||
|
Retargetable = 0x0100,
|
||||||
|
|
||||||
|
/// <summary/>
|
||||||
|
ContentType_Default = 0x0000,
|
||||||
|
/// <summary/>
|
||||||
|
ContentType_WindowsRuntime = 0x0200,
|
||||||
|
/// <summary>Bits describing ContentType</summary>
|
||||||
|
ContentType_Mask = 0x0E00,
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,111 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Hashes some data according to a <see cref="AssemblyHashAlgorithm"/>
|
||||||
|
/// </summary>
|
||||||
|
readonly struct AssemblyHash : IDisposable {
|
||||||
|
readonly HashAlgorithm hasher;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>If <paramref name="hashAlgo"/> is an unsupported hash algorithm, then
|
||||||
|
/// <see cref="AssemblyHashAlgorithm.SHA1"/> will be used as the hash algorithm.</remarks>
|
||||||
|
/// <param name="hashAlgo">The algorithm to use</param>
|
||||||
|
public AssemblyHash(AssemblyHashAlgorithm hashAlgo) =>
|
||||||
|
hasher = hashAlgo switch {
|
||||||
|
AssemblyHashAlgorithm.MD5 => MD5.Create(),
|
||||||
|
AssemblyHashAlgorithm.SHA_256 => SHA256.Create(),
|
||||||
|
AssemblyHashAlgorithm.SHA_384 => SHA384.Create(),
|
||||||
|
AssemblyHashAlgorithm.SHA_512 => SHA512.Create(),
|
||||||
|
_ => SHA1.Create(),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Dispose() {
|
||||||
|
if (hasher is not null)
|
||||||
|
((IDisposable)hasher).Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hash data
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>If <paramref name="hashAlgo"/> is an unsupported hash algorithm, then
|
||||||
|
/// <see cref="AssemblyHashAlgorithm.SHA1"/> will be used as the hash algorithm.</remarks>
|
||||||
|
/// <param name="data">The data</param>
|
||||||
|
/// <param name="hashAlgo">The algorithm to use</param>
|
||||||
|
/// <returns>Hashed data or null if <paramref name="data"/> was <c>null</c></returns>
|
||||||
|
public static byte[] Hash(byte[] data, AssemblyHashAlgorithm hashAlgo) {
|
||||||
|
if (data is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
using (var asmHash = new AssemblyHash(hashAlgo)) {
|
||||||
|
asmHash.Hash(data);
|
||||||
|
return asmHash.ComputeHash();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hash data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data</param>
|
||||||
|
public void Hash(byte[] data) => Hash(data, 0, data.Length);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hash data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data</param>
|
||||||
|
/// <param name="offset">Offset</param>
|
||||||
|
/// <param name="length">Length</param>
|
||||||
|
public void Hash(byte[] data, int offset, int length) {
|
||||||
|
if (hasher.TransformBlock(data, offset, length, data, offset) != length)
|
||||||
|
throw new IOException("Could not calculate hash");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hash stream data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">Stream</param>
|
||||||
|
/// <param name="length">Number of bytes to hash</param>
|
||||||
|
/// <param name="buffer">Temp buffer</param>
|
||||||
|
public void Hash(Stream stream, uint length, byte[] buffer) {
|
||||||
|
while (length > 0) {
|
||||||
|
int len = length > (uint)buffer.Length ? buffer.Length : (int)length;
|
||||||
|
if (stream.Read(buffer, 0, len) != len)
|
||||||
|
throw new IOException("Could not read data");
|
||||||
|
Hash(buffer, 0, len);
|
||||||
|
length -= (uint)len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the hash
|
||||||
|
/// </summary>
|
||||||
|
public byte[] ComputeHash() {
|
||||||
|
hasher.TransformFinalBlock(Array2.Empty<byte>(), 0, 0);
|
||||||
|
return hasher.Hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a public key token from the hash of some <paramref name="publicKeyData"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>A public key is hashed, and the last 8 bytes of the hash, in reverse
|
||||||
|
/// order, is used as the public key token</remarks>
|
||||||
|
/// <param name="publicKeyData">The data</param>
|
||||||
|
/// <returns>A new <see cref="PublicKeyToken"/> instance</returns>
|
||||||
|
public static PublicKeyToken CreatePublicKeyToken(byte[] publicKeyData) {
|
||||||
|
if (publicKeyData is null)
|
||||||
|
return new PublicKeyToken();
|
||||||
|
var hash = Hash(publicKeyData, AssemblyHashAlgorithm.SHA1);
|
||||||
|
var pkt = new byte[8];
|
||||||
|
for (int i = 0; i < pkt.Length && i < hash.Length; i++)
|
||||||
|
pkt[i] = hash[hash.Length - i - 1];
|
||||||
|
return new PublicKeyToken(pkt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Any ALG_CLASS_HASH type in WinCrypt.h can be used by Microsoft's CLI implementation
|
||||||
|
/// </summary>
|
||||||
|
public enum AssemblyHashAlgorithm : uint {
|
||||||
|
/// <summary/>
|
||||||
|
None = 0,
|
||||||
|
/// <summary/>
|
||||||
|
MD2 = 0x8001,
|
||||||
|
/// <summary/>
|
||||||
|
MD4 = 0x8002,
|
||||||
|
/// <summary>This is a reserved value in the CLI</summary>
|
||||||
|
MD5 = 0x8003,
|
||||||
|
/// <summary>The only algorithm supported by the CLI</summary>
|
||||||
|
SHA1 = 0x8004,
|
||||||
|
/// <summary/>
|
||||||
|
MAC = 0x8005,
|
||||||
|
/// <summary/>
|
||||||
|
SSL3_SHAMD5 = 0x8008,
|
||||||
|
/// <summary/>
|
||||||
|
HMAC = 0x8009,
|
||||||
|
/// <summary/>
|
||||||
|
TLS1PRF = 0x800A,
|
||||||
|
/// <summary/>
|
||||||
|
HASH_REPLACE_OWF = 0x800B,
|
||||||
|
/// <summary/>
|
||||||
|
SHA_256 = 0x800C,
|
||||||
|
/// <summary/>
|
||||||
|
SHA_384 = 0x800D,
|
||||||
|
/// <summary/>
|
||||||
|
SHA_512 = 0x800E,
|
||||||
|
}
|
||||||
|
|
||||||
|
public static partial class Extensions {
|
||||||
|
internal static string GetName(this AssemblyHashAlgorithm hashAlg) =>
|
||||||
|
hashAlg switch {
|
||||||
|
AssemblyHashAlgorithm.MD2 => null,
|
||||||
|
AssemblyHashAlgorithm.MD4 => null,
|
||||||
|
AssemblyHashAlgorithm.MD5 => "MD5",
|
||||||
|
AssemblyHashAlgorithm.SHA1 => "SHA1",
|
||||||
|
AssemblyHashAlgorithm.MAC => null,
|
||||||
|
AssemblyHashAlgorithm.SSL3_SHAMD5 => null,
|
||||||
|
AssemblyHashAlgorithm.HMAC => null,
|
||||||
|
AssemblyHashAlgorithm.TLS1PRF => null,
|
||||||
|
AssemblyHashAlgorithm.HASH_REPLACE_OWF => null,
|
||||||
|
AssemblyHashAlgorithm.SHA_256 => "SHA256",
|
||||||
|
AssemblyHashAlgorithm.SHA_384 => "SHA384",
|
||||||
|
AssemblyHashAlgorithm.SHA_512 => "SHA512",
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,259 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Flags used by <see cref="AssemblyNameComparer"/>
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum AssemblyNameComparerFlags {
|
||||||
|
/// <summary>
|
||||||
|
/// Compare assembly simple name
|
||||||
|
/// </summary>
|
||||||
|
Name = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compare assembly version
|
||||||
|
/// </summary>
|
||||||
|
Version = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compare assembly public key token
|
||||||
|
/// </summary>
|
||||||
|
PublicKeyToken = 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compare assembly culture
|
||||||
|
/// </summary>
|
||||||
|
Culture = 8,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compare content type
|
||||||
|
/// </summary>
|
||||||
|
ContentType = 0x10,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compare assembly simple name, version, public key token, locale and content type
|
||||||
|
/// </summary>
|
||||||
|
All = Name | Version | PublicKeyToken | Culture | ContentType,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compares two assembly names
|
||||||
|
/// </summary>
|
||||||
|
public readonly struct AssemblyNameComparer : IEqualityComparer<IAssembly> {
|
||||||
|
/// <summary>
|
||||||
|
/// Compares the name, version, public key token, culture and content type
|
||||||
|
/// </summary>
|
||||||
|
public static readonly AssemblyNameComparer CompareAll = new AssemblyNameComparer(AssemblyNameComparerFlags.All);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compares only the name and the public key token
|
||||||
|
/// </summary>
|
||||||
|
public static readonly AssemblyNameComparer NameAndPublicKeyTokenOnly = new AssemblyNameComparer(AssemblyNameComparerFlags.Name | AssemblyNameComparerFlags.PublicKeyToken);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compares only the name
|
||||||
|
/// </summary>
|
||||||
|
public static readonly AssemblyNameComparer NameOnly = new AssemblyNameComparer(AssemblyNameComparerFlags.Name);
|
||||||
|
|
||||||
|
readonly AssemblyNameComparerFlags flags;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="AssemblyNameComparerFlags.Name"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool CompareName => (flags & AssemblyNameComparerFlags.Name) != 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="AssemblyNameComparerFlags.Version"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool CompareVersion => (flags & AssemblyNameComparerFlags.Version) != 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="AssemblyNameComparerFlags.PublicKeyToken"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool ComparePublicKeyToken => (flags & AssemblyNameComparerFlags.PublicKeyToken) != 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="AssemblyNameComparerFlags.Culture"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool CompareCulture => (flags & AssemblyNameComparerFlags.Culture) != 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="AssemblyNameComparerFlags.ContentType"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool CompareContentType => (flags & AssemblyNameComparerFlags.ContentType) != 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="flags">Comparison flags</param>
|
||||||
|
public AssemblyNameComparer(AssemblyNameComparerFlags flags) => this.flags = flags;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compares two assembly names
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="a">First</param>
|
||||||
|
/// <param name="b">Second</param>
|
||||||
|
/// <returns>< 0 if a < b, 0 if a == b, > 0 if a > b</returns>
|
||||||
|
public int CompareTo(IAssembly a, IAssembly b) {
|
||||||
|
if (a == b)
|
||||||
|
return 0;
|
||||||
|
if (a is null)
|
||||||
|
return -1;
|
||||||
|
if (b is null)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
int v;
|
||||||
|
|
||||||
|
if (CompareName && (v = UTF8String.CaseInsensitiveCompareTo(a.Name, b.Name)) != 0)
|
||||||
|
return v;
|
||||||
|
if (CompareVersion && (v = Utils.CompareTo(a.Version, b.Version)) != 0)
|
||||||
|
return v;
|
||||||
|
if (ComparePublicKeyToken && (v = PublicKeyBase.TokenCompareTo(a.PublicKeyOrToken, b.PublicKeyOrToken)) != 0)
|
||||||
|
return v;
|
||||||
|
if (CompareCulture && (v = Utils.LocaleCompareTo(a.Culture, b.Culture)) != 0)
|
||||||
|
return v;
|
||||||
|
if (CompareContentType && (v = a.ContentType.CompareTo(b.ContentType)) != 0)
|
||||||
|
return v;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compares two assembly names
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="a">First</param>
|
||||||
|
/// <param name="b">Second</param>
|
||||||
|
/// <returns><c>true</c> if equal, <c>false</c> otherwise</returns>
|
||||||
|
public bool Equals(IAssembly a, IAssembly b) => CompareTo(a, b) == 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Figures out which of two assembly names is closer to another assembly name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="requested">Requested assembly name</param>
|
||||||
|
/// <param name="a">First</param>
|
||||||
|
/// <param name="b">Second</param>
|
||||||
|
/// <returns>-1 if both are equally close, 0 if <paramref name="a"/> is closest, 1 if
|
||||||
|
/// <paramref name="b"/> is closest</returns>
|
||||||
|
public int CompareClosest(IAssembly requested, IAssembly a, IAssembly b) {
|
||||||
|
if (a == b)
|
||||||
|
return 0;
|
||||||
|
if (a is null)
|
||||||
|
return !CompareName ? 1 : UTF8String.CaseInsensitiveEquals(requested.Name, b.Name) ? 1 : 0;
|
||||||
|
if (b is null)
|
||||||
|
return !CompareName ? 0 : UTF8String.CaseInsensitiveEquals(requested.Name, a.Name) ? 0 : 1;
|
||||||
|
|
||||||
|
// Compare the most important parts first:
|
||||||
|
// 1. Assembly simple name
|
||||||
|
// 2. Public key token
|
||||||
|
// 3. Version
|
||||||
|
// 4. Locale
|
||||||
|
// 5. Content type
|
||||||
|
|
||||||
|
if (CompareName) {
|
||||||
|
// If the name only matches one of a or b, return that one.
|
||||||
|
bool na = UTF8String.CaseInsensitiveEquals(requested.Name, a.Name);
|
||||||
|
bool nb = UTF8String.CaseInsensitiveEquals(requested.Name, b.Name);
|
||||||
|
if (na && !nb)
|
||||||
|
return 0;
|
||||||
|
if (!na && nb)
|
||||||
|
return 1;
|
||||||
|
if (!na && !nb)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ComparePublicKeyToken) {
|
||||||
|
bool pa, pb;
|
||||||
|
if (PublicKeyBase.IsNullOrEmpty2(requested.PublicKeyOrToken)) {
|
||||||
|
// If one of them has a pkt but the other one hasn't, return the one with
|
||||||
|
// no pkt.
|
||||||
|
pa = PublicKeyBase.IsNullOrEmpty2(a.PublicKeyOrToken);
|
||||||
|
pb = PublicKeyBase.IsNullOrEmpty2(b.PublicKeyOrToken);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If one of them has the correct pkt, but the other one has an incorrect
|
||||||
|
// pkt, return the one with the correct pkt.
|
||||||
|
pa = PublicKeyBase.TokenEquals(requested.PublicKeyOrToken, a.PublicKeyOrToken);
|
||||||
|
pb = PublicKeyBase.TokenEquals(requested.PublicKeyOrToken, b.PublicKeyOrToken);
|
||||||
|
}
|
||||||
|
if (pa && !pb)
|
||||||
|
return 0;
|
||||||
|
if (!pa && pb)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CompareVersion && !Utils.Equals(a.Version, b.Version)) {
|
||||||
|
var rv = Utils.CreateVersionWithNoUndefinedValues(requested.Version);
|
||||||
|
if (rv == new Version(0, 0, 0, 0))
|
||||||
|
rv = new Version(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue);
|
||||||
|
int va = Utils.CompareTo(a.Version, rv);
|
||||||
|
int vb = Utils.CompareTo(b.Version, rv);
|
||||||
|
if (va == 0)
|
||||||
|
return 0; // vb != 0 so return 0
|
||||||
|
if (vb == 0)
|
||||||
|
return 1; // va != 0 so return 1
|
||||||
|
if (va > 0 && vb < 0)
|
||||||
|
return 0;
|
||||||
|
if (va < 0 && vb > 0)
|
||||||
|
return 1;
|
||||||
|
// Now either both a and b's version > req version or both are < req version
|
||||||
|
if (va > 0) {
|
||||||
|
// a.Version and b.Version > req.Version. Pick the one that is closest.
|
||||||
|
return Utils.CompareTo(a.Version, b.Version) < 0 ? 0 : 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// a.Version and b.Version < req.Version. Pick the one that is closest.
|
||||||
|
return Utils.CompareTo(a.Version, b.Version) > 0 ? 0 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CompareCulture) {
|
||||||
|
bool la = Utils.LocaleEquals(requested.Culture, a.Culture);
|
||||||
|
bool lb = Utils.LocaleEquals(requested.Culture, b.Culture);
|
||||||
|
if (la && !lb)
|
||||||
|
return 0;
|
||||||
|
if (!la && lb)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CompareContentType) {
|
||||||
|
bool ca = requested.ContentType == a.ContentType;
|
||||||
|
bool cb = requested.ContentType == b.ContentType;
|
||||||
|
if (ca && !cb)
|
||||||
|
return 0;
|
||||||
|
if (!ca && cb)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the hash code of an assembly name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="a">Assembly name</param>
|
||||||
|
/// <returns>The hash code</returns>
|
||||||
|
public int GetHashCode(IAssembly a) {
|
||||||
|
if (a is null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int hash = 0;
|
||||||
|
|
||||||
|
if (CompareName)
|
||||||
|
hash += UTF8String.GetHashCode(a.Name);
|
||||||
|
if (CompareVersion)
|
||||||
|
hash += Utils.CreateVersionWithNoUndefinedValues(a.Version).GetHashCode();
|
||||||
|
if (ComparePublicKeyToken)
|
||||||
|
hash += PublicKeyBase.GetHashCodeToken(a.PublicKeyOrToken);
|
||||||
|
if (CompareCulture)
|
||||||
|
hash += Utils.GetHashCodeLocale(a.Culture);
|
||||||
|
if (CompareContentType)
|
||||||
|
hash += (int)a.ContentType;
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,255 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Stores assembly name information
|
||||||
|
/// </summary>
|
||||||
|
public sealed class AssemblyNameInfo : IAssembly {
|
||||||
|
AssemblyHashAlgorithm hashAlgId;
|
||||||
|
Version version;
|
||||||
|
AssemblyAttributes flags;
|
||||||
|
PublicKeyBase publicKeyOrToken;
|
||||||
|
UTF8String name;
|
||||||
|
UTF8String culture;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="AssemblyHashAlgorithm"/>
|
||||||
|
/// </summary>
|
||||||
|
public AssemblyHashAlgorithm HashAlgId {
|
||||||
|
get => hashAlgId;
|
||||||
|
set => hashAlgId = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="Version"/> or <c>null</c> if none specified
|
||||||
|
/// </summary>
|
||||||
|
public Version Version {
|
||||||
|
get => version;
|
||||||
|
set => version = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="AssemblyAttributes"/>
|
||||||
|
/// </summary>
|
||||||
|
public AssemblyAttributes Attributes {
|
||||||
|
get => flags;
|
||||||
|
set => flags = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the public key or token
|
||||||
|
/// </summary>
|
||||||
|
public PublicKeyBase PublicKeyOrToken {
|
||||||
|
get => publicKeyOrToken;
|
||||||
|
set => publicKeyOrToken = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the name
|
||||||
|
/// </summary>
|
||||||
|
public UTF8String Name {
|
||||||
|
get => name;
|
||||||
|
set => name = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the culture or <c>null</c> if none specified
|
||||||
|
/// </summary>
|
||||||
|
public UTF8String Culture {
|
||||||
|
get => culture;
|
||||||
|
set => culture = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the full name of the assembly
|
||||||
|
/// </summary>
|
||||||
|
public string FullName => FullNameToken;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the full name of the assembly but use a public key token
|
||||||
|
/// </summary>
|
||||||
|
public string FullNameToken => FullNameFactory.AssemblyFullName(this, true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Modify <see cref="Attributes"/> property: <see cref="Attributes"/> =
|
||||||
|
/// (<see cref="Attributes"/> & <paramref name="andMask"/>) | <paramref name="orMask"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="andMask">Value to <c>AND</c></param>
|
||||||
|
/// <param name="orMask">Value to OR</param>
|
||||||
|
void ModifyAttributes(AssemblyAttributes andMask, AssemblyAttributes orMask) => Attributes = (Attributes & andMask) | orMask;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set or clear flags in <see cref="Attributes"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="set"><c>true</c> if flags should be set, <c>false</c> if flags should
|
||||||
|
/// be cleared</param>
|
||||||
|
/// <param name="flags">Flags to set or clear</param>
|
||||||
|
void ModifyAttributes(bool set, AssemblyAttributes flags) {
|
||||||
|
if (set)
|
||||||
|
Attributes |= flags;
|
||||||
|
else
|
||||||
|
Attributes &= ~flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="AssemblyAttributes.PublicKey"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool HasPublicKey {
|
||||||
|
get => (Attributes & AssemblyAttributes.PublicKey) != 0;
|
||||||
|
set => ModifyAttributes(value, AssemblyAttributes.PublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the processor architecture
|
||||||
|
/// </summary>
|
||||||
|
public AssemblyAttributes ProcessorArchitecture {
|
||||||
|
get => Attributes & AssemblyAttributes.PA_Mask;
|
||||||
|
set => ModifyAttributes(~AssemblyAttributes.PA_Mask, value & AssemblyAttributes.PA_Mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the processor architecture
|
||||||
|
/// </summary>
|
||||||
|
public AssemblyAttributes ProcessorArchitectureFull {
|
||||||
|
get => Attributes & AssemblyAttributes.PA_FullMask;
|
||||||
|
set => ModifyAttributes(~AssemblyAttributes.PA_FullMask, value & AssemblyAttributes.PA_FullMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if unspecified processor architecture
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcessorArchitectureNone => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_None;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if neutral (PE32) architecture
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcessorArchitectureMSIL => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_MSIL;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if x86 (PE32) architecture
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcessorArchitectureX86 => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_x86;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if IA-64 (PE32+) architecture
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcessorArchitectureIA64 => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_IA64;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if x64 (PE32+) architecture
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcessorArchitectureX64 => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_AMD64;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if ARM (PE32) architecture
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcessorArchitectureARM => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_ARM;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if eg. reference assembly (not runnable)
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcessorArchitectureNoPlatform => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_NoPlatform;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="AssemblyAttributes.PA_Specified"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcessorArchitectureSpecified {
|
||||||
|
get => (Attributes & AssemblyAttributes.PA_Specified) != 0;
|
||||||
|
set => ModifyAttributes(value, AssemblyAttributes.PA_Specified);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="AssemblyAttributes.EnableJITcompileTracking"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool EnableJITcompileTracking {
|
||||||
|
get => (Attributes & AssemblyAttributes.EnableJITcompileTracking) != 0;
|
||||||
|
set => ModifyAttributes(value, AssemblyAttributes.EnableJITcompileTracking);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="AssemblyAttributes.DisableJITcompileOptimizer"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool DisableJITcompileOptimizer {
|
||||||
|
get => (Attributes & AssemblyAttributes.DisableJITcompileOptimizer) != 0;
|
||||||
|
set => ModifyAttributes(value, AssemblyAttributes.DisableJITcompileOptimizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="AssemblyAttributes.Retargetable"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRetargetable {
|
||||||
|
get => (Attributes & AssemblyAttributes.Retargetable) != 0;
|
||||||
|
set => ModifyAttributes(value, AssemblyAttributes.Retargetable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the content type
|
||||||
|
/// </summary>
|
||||||
|
public AssemblyAttributes ContentType {
|
||||||
|
get => Attributes & AssemblyAttributes.ContentType_Mask;
|
||||||
|
set => ModifyAttributes(~AssemblyAttributes.ContentType_Mask, value & AssemblyAttributes.ContentType_Mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if content type is <c>Default</c>
|
||||||
|
/// </summary>
|
||||||
|
public bool IsContentTypeDefault => (Attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_Default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if content type is <c>WindowsRuntime</c>
|
||||||
|
/// </summary>
|
||||||
|
public bool IsContentTypeWindowsRuntime => (Attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_WindowsRuntime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public AssemblyNameInfo() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="asmFullName">An assembly name</param>
|
||||||
|
public AssemblyNameInfo(string asmFullName)
|
||||||
|
: this(ReflectionTypeNameParser.ParseAssemblyRef(asmFullName)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="asm">The assembly</param>
|
||||||
|
public AssemblyNameInfo(IAssembly asm) {
|
||||||
|
if (asm is null)
|
||||||
|
return;
|
||||||
|
var asmDef = asm as AssemblyDef;
|
||||||
|
hashAlgId = asmDef is null ? 0 : asmDef.HashAlgorithm;
|
||||||
|
version = asm.Version ?? new Version(0, 0, 0, 0);
|
||||||
|
flags = asm.Attributes;
|
||||||
|
publicKeyOrToken = asm.PublicKeyOrToken;
|
||||||
|
name = UTF8String.IsNullOrEmpty(asm.Name) ? UTF8String.Empty : asm.Name;
|
||||||
|
culture = UTF8String.IsNullOrEmpty(asm.Culture) ? UTF8String.Empty : asm.Culture;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="asmName">Assembly name info</param>
|
||||||
|
public AssemblyNameInfo(AssemblyName asmName) {
|
||||||
|
if (asmName is null)
|
||||||
|
return;
|
||||||
|
hashAlgId = (AssemblyHashAlgorithm)asmName.HashAlgorithm;
|
||||||
|
version = asmName.Version ?? new Version(0, 0, 0, 0);
|
||||||
|
flags = (AssemblyAttributes)asmName.Flags;
|
||||||
|
publicKeyOrToken = (PublicKeyBase)PublicKeyBase.CreatePublicKey(asmName.GetPublicKey()) ??
|
||||||
|
PublicKeyBase.CreatePublicKeyToken(asmName.GetPublicKeyToken());
|
||||||
|
name = asmName.Name ?? string.Empty;
|
||||||
|
culture = asmName.CultureInfo is not null && asmName.CultureInfo.Name is not null ? asmName.CultureInfo.Name : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inhertidoc/>
|
||||||
|
public override string ToString() => FullName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,466 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
|
using dnlib.DotNet.MD;
|
||||||
|
using dnlib.DotNet.Pdb;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// A high-level representation of a row in the AssemblyRef table
|
||||||
|
/// </summary>
|
||||||
|
public abstract class AssemblyRef : IHasCustomAttribute, IImplementation, IResolutionScope, IHasCustomDebugInformation, IAssembly, IScope {
|
||||||
|
/// <summary>
|
||||||
|
/// An assembly ref that can be used to indicate that it references the current assembly
|
||||||
|
/// when the current assembly is not known (eg. a type string without any assembly info
|
||||||
|
/// when it references a type in the current assembly).
|
||||||
|
/// </summary>
|
||||||
|
public static readonly AssemblyRef CurrentAssembly = new AssemblyRefUser("<<<CURRENT_ASSEMBLY>>>");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The row id in its table
|
||||||
|
/// </summary>
|
||||||
|
protected uint rid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public MDToken MDToken => new MDToken(Table.AssemblyRef, rid);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint Rid {
|
||||||
|
get => rid;
|
||||||
|
set => rid = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomAttributeTag => 15;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int ImplementationTag => 1;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int ResolutionScopeTag => 2;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ScopeType ScopeType => ScopeType.AssemblyRef;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public string ScopeName => FullName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From columns AssemblyRef.MajorVersion, AssemblyRef.MinorVersion,
|
||||||
|
/// AssemblyRef.BuildNumber, AssemblyRef.RevisionNumber
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="ArgumentNullException">If <paramref name="value"/> is <c>null</c></exception>
|
||||||
|
public Version Version {
|
||||||
|
get => version;
|
||||||
|
set => version = value ?? throw new ArgumentNullException(nameof(value));
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected Version version;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column AssemblyRef.Flags
|
||||||
|
/// </summary>
|
||||||
|
public AssemblyAttributes Attributes {
|
||||||
|
get => (AssemblyAttributes)attributes;
|
||||||
|
set => attributes = (int)value;
|
||||||
|
}
|
||||||
|
/// <summary>Attributes</summary>
|
||||||
|
protected int attributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column AssemblyRef.PublicKeyOrToken
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="ArgumentNullException">If <paramref name="value"/> is <c>null</c></exception>
|
||||||
|
public PublicKeyBase PublicKeyOrToken {
|
||||||
|
get => publicKeyOrToken;
|
||||||
|
set => publicKeyOrToken = value ?? throw new ArgumentNullException(nameof(value));
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected PublicKeyBase publicKeyOrToken;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column AssemblyRef.Name
|
||||||
|
/// </summary>
|
||||||
|
public UTF8String Name {
|
||||||
|
get => name;
|
||||||
|
set => name = value;
|
||||||
|
}
|
||||||
|
/// <summary>Name</summary>
|
||||||
|
protected UTF8String name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column AssemblyRef.Locale
|
||||||
|
/// </summary>
|
||||||
|
public UTF8String Culture {
|
||||||
|
get => culture;
|
||||||
|
set => culture = value;
|
||||||
|
}
|
||||||
|
/// <summary>Culture</summary>
|
||||||
|
protected UTF8String culture;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column AssemblyRef.HashValue
|
||||||
|
/// </summary>
|
||||||
|
public byte[] Hash {
|
||||||
|
get => hashValue;
|
||||||
|
set => hashValue = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected byte[] hashValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom attributes
|
||||||
|
/// </summary>
|
||||||
|
public CustomAttributeCollection CustomAttributes {
|
||||||
|
get {
|
||||||
|
if (customAttributes is null)
|
||||||
|
InitializeCustomAttributes();
|
||||||
|
return customAttributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected CustomAttributeCollection customAttributes;
|
||||||
|
/// <summary>Initializes <see cref="customAttributes"/></summary>
|
||||||
|
protected virtual void InitializeCustomAttributes() =>
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomAttributes => CustomAttributes.Count > 0;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomDebugInformationTag => 15;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom debug infos
|
||||||
|
/// </summary>
|
||||||
|
public IList<PdbCustomDebugInfo> CustomDebugInfos {
|
||||||
|
get {
|
||||||
|
if (customDebugInfos is null)
|
||||||
|
InitializeCustomDebugInfos();
|
||||||
|
return customDebugInfos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected IList<PdbCustomDebugInfo> customDebugInfos;
|
||||||
|
/// <summary>Initializes <see cref="customDebugInfos"/></summary>
|
||||||
|
protected virtual void InitializeCustomDebugInfos() =>
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, new List<PdbCustomDebugInfo>(), null);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public string FullName => FullNameToken;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Same as <see cref="FullName"/>, except that it uses the <c>PublicKey</c> if available.
|
||||||
|
/// </summary>
|
||||||
|
public string RealFullName => FullNameFactory.AssemblyFullName(this, false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the full name of the assembly but use a public key token
|
||||||
|
/// </summary>
|
||||||
|
public string FullNameToken => FullNameFactory.AssemblyFullName(this, true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Modify <see cref="attributes"/> property: <see cref="attributes"/> =
|
||||||
|
/// (<see cref="attributes"/> & <paramref name="andMask"/>) | <paramref name="orMask"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="andMask">Value to <c>AND</c></param>
|
||||||
|
/// <param name="orMask">Value to OR</param>
|
||||||
|
void ModifyAttributes(AssemblyAttributes andMask, AssemblyAttributes orMask) =>
|
||||||
|
attributes = (attributes & (int)andMask) | (int)orMask;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set or clear flags in <see cref="attributes"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="set"><c>true</c> if flags should be set, <c>false</c> if flags should
|
||||||
|
/// be cleared</param>
|
||||||
|
/// <param name="flags">Flags to set or clear</param>
|
||||||
|
void ModifyAttributes(bool set, AssemblyAttributes flags) {
|
||||||
|
if (set)
|
||||||
|
attributes |= (int)flags;
|
||||||
|
else
|
||||||
|
attributes &= ~(int)flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="AssemblyAttributes.PublicKey"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool HasPublicKey {
|
||||||
|
get => ((AssemblyAttributes)attributes & AssemblyAttributes.PublicKey) != 0;
|
||||||
|
set => ModifyAttributes(value, AssemblyAttributes.PublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the processor architecture
|
||||||
|
/// </summary>
|
||||||
|
public AssemblyAttributes ProcessorArchitecture {
|
||||||
|
get => (AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask;
|
||||||
|
set => ModifyAttributes(~AssemblyAttributes.PA_Mask, value & AssemblyAttributes.PA_Mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the processor architecture
|
||||||
|
/// </summary>
|
||||||
|
public AssemblyAttributes ProcessorArchitectureFull {
|
||||||
|
get => (AssemblyAttributes)attributes & AssemblyAttributes.PA_FullMask;
|
||||||
|
set => ModifyAttributes(~AssemblyAttributes.PA_FullMask, value & AssemblyAttributes.PA_FullMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if unspecified processor architecture
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcessorArchitectureNone => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_None;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if neutral (PE32) architecture
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcessorArchitectureMSIL => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_MSIL;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if x86 (PE32) architecture
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcessorArchitectureX86 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_x86;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if IA-64 (PE32+) architecture
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcessorArchitectureIA64 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_IA64;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if x64 (PE32+) architecture
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcessorArchitectureX64 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_AMD64;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if ARM (PE32) architecture
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcessorArchitectureARM => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_ARM;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if eg. reference assembly (not runnable)
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcessorArchitectureNoPlatform => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_NoPlatform;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="AssemblyAttributes.PA_Specified"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcessorArchitectureSpecified {
|
||||||
|
get => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Specified) != 0;
|
||||||
|
set => ModifyAttributes(value, AssemblyAttributes.PA_Specified);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="AssemblyAttributes.EnableJITcompileTracking"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool EnableJITcompileTracking {
|
||||||
|
get => ((AssemblyAttributes)attributes & AssemblyAttributes.EnableJITcompileTracking) != 0;
|
||||||
|
set => ModifyAttributes(value, AssemblyAttributes.EnableJITcompileTracking);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="AssemblyAttributes.DisableJITcompileOptimizer"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool DisableJITcompileOptimizer {
|
||||||
|
get => ((AssemblyAttributes)attributes & AssemblyAttributes.DisableJITcompileOptimizer) != 0;
|
||||||
|
set => ModifyAttributes(value, AssemblyAttributes.DisableJITcompileOptimizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="AssemblyAttributes.Retargetable"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRetargetable {
|
||||||
|
get => ((AssemblyAttributes)attributes & AssemblyAttributes.Retargetable) != 0;
|
||||||
|
set => ModifyAttributes(value, AssemblyAttributes.Retargetable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the content type
|
||||||
|
/// </summary>
|
||||||
|
public AssemblyAttributes ContentType {
|
||||||
|
get => (AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask;
|
||||||
|
set => ModifyAttributes(~AssemblyAttributes.ContentType_Mask, value & AssemblyAttributes.ContentType_Mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if content type is <c>Default</c>
|
||||||
|
/// </summary>
|
||||||
|
public bool IsContentTypeDefault => ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_Default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if content type is <c>WindowsRuntime</c>
|
||||||
|
/// </summary>
|
||||||
|
public bool IsContentTypeWindowsRuntime => ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_WindowsRuntime;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString() => FullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An AssemblyRef row created by the user and not present in the original .NET file
|
||||||
|
/// </summary>
|
||||||
|
public class AssemblyRefUser : AssemblyRef {
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a reference to CLR 1.0's mscorlib
|
||||||
|
/// </summary>
|
||||||
|
public static AssemblyRefUser CreateMscorlibReferenceCLR10() => new AssemblyRefUser("mscorlib", new Version(1, 0, 3300, 0), new PublicKeyToken("b77a5c561934e089"));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a reference to CLR 1.1's mscorlib
|
||||||
|
/// </summary>
|
||||||
|
public static AssemblyRefUser CreateMscorlibReferenceCLR11() => new AssemblyRefUser("mscorlib", new Version(1, 0, 5000, 0), new PublicKeyToken("b77a5c561934e089"));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a reference to CLR 2.0's mscorlib
|
||||||
|
/// </summary>
|
||||||
|
public static AssemblyRefUser CreateMscorlibReferenceCLR20() => new AssemblyRefUser("mscorlib", new Version(2, 0, 0, 0), new PublicKeyToken("b77a5c561934e089"));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a reference to CLR 4.0's mscorlib
|
||||||
|
/// </summary>
|
||||||
|
public static AssemblyRefUser CreateMscorlibReferenceCLR40() => new AssemblyRefUser("mscorlib", new Version(4, 0, 0, 0), new PublicKeyToken("b77a5c561934e089"));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public AssemblyRefUser()
|
||||||
|
: this(UTF8String.Empty) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Simple name</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If any of the args is invalid</exception>
|
||||||
|
public AssemblyRefUser(UTF8String name)
|
||||||
|
: this(name, new Version(0, 0, 0, 0)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Simple name</param>
|
||||||
|
/// <param name="version">Version</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If any of the args is invalid</exception>
|
||||||
|
public AssemblyRefUser(UTF8String name, Version version)
|
||||||
|
: this(name, version, new PublicKey()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Simple name</param>
|
||||||
|
/// <param name="version">Version</param>
|
||||||
|
/// <param name="publicKey">Public key or public key token</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If any of the args is invalid</exception>
|
||||||
|
public AssemblyRefUser(UTF8String name, Version version, PublicKeyBase publicKey)
|
||||||
|
: this(name, version, publicKey, UTF8String.Empty) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Simple name</param>
|
||||||
|
/// <param name="version">Version</param>
|
||||||
|
/// <param name="publicKey">Public key or public key token</param>
|
||||||
|
/// <param name="locale">Locale</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If any of the args is invalid</exception>
|
||||||
|
public AssemblyRefUser(UTF8String name, Version version, PublicKeyBase publicKey, UTF8String locale) {
|
||||||
|
if (name is null)
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
if (locale is null)
|
||||||
|
throw new ArgumentNullException(nameof(locale));
|
||||||
|
this.name = name;
|
||||||
|
this.version = version ?? throw new ArgumentNullException(nameof(version));
|
||||||
|
publicKeyOrToken = publicKey;
|
||||||
|
culture = locale;
|
||||||
|
attributes = (int)(publicKey is PublicKey ? AssemblyAttributes.PublicKey : AssemblyAttributes.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="asmName">Assembly name info</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If <paramref name="asmName"/> is <c>null</c></exception>
|
||||||
|
public AssemblyRefUser(AssemblyName asmName)
|
||||||
|
: this(new AssemblyNameInfo(asmName)) => attributes = (int)asmName.Flags;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assembly">Assembly</param>
|
||||||
|
public AssemblyRefUser(IAssembly assembly) {
|
||||||
|
if (assembly is null)
|
||||||
|
throw new ArgumentNullException("asmName");
|
||||||
|
|
||||||
|
version = assembly.Version ?? new Version(0, 0, 0, 0);
|
||||||
|
publicKeyOrToken = assembly.PublicKeyOrToken;
|
||||||
|
name = UTF8String.IsNullOrEmpty(assembly.Name) ? UTF8String.Empty : assembly.Name;
|
||||||
|
culture = assembly.Culture;
|
||||||
|
attributes = (int)((publicKeyOrToken is PublicKey ? AssemblyAttributes.PublicKey : AssemblyAttributes.None) | assembly.ContentType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Created from a row in the AssemblyRef table
|
||||||
|
/// </summary>
|
||||||
|
sealed class AssemblyRefMD : AssemblyRef, IMDTokenProviderMD {
|
||||||
|
/// <summary>The module where this instance is located</summary>
|
||||||
|
readonly ModuleDefMD readerModule;
|
||||||
|
|
||||||
|
readonly uint origRid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint OrigRid => origRid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomAttributes() {
|
||||||
|
var list = readerModule.Metadata.GetCustomAttributeRidList(Table.AssemblyRef, origRid);
|
||||||
|
var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index]));
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, tmp, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomDebugInfos() {
|
||||||
|
var list = new List<PdbCustomDebugInfo>();
|
||||||
|
readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list);
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, list, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readerModule">The module which contains this <c>AssemblyRef</c> row</param>
|
||||||
|
/// <param name="rid">Row ID</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If <paramref name="readerModule"/> is <c>null</c></exception>
|
||||||
|
/// <exception cref="ArgumentException">If <paramref name="rid"/> is invalid</exception>
|
||||||
|
public AssemblyRefMD(ModuleDefMD readerModule, uint rid) {
|
||||||
|
#if DEBUG
|
||||||
|
if (readerModule is null)
|
||||||
|
throw new ArgumentNullException("readerModule");
|
||||||
|
if (readerModule.TablesStream.AssemblyRefTable.IsInvalidRID(rid))
|
||||||
|
throw new BadImageFormatException($"AssemblyRef rid {rid} does not exist");
|
||||||
|
#endif
|
||||||
|
origRid = rid;
|
||||||
|
this.rid = rid;
|
||||||
|
this.readerModule = readerModule;
|
||||||
|
bool b = readerModule.TablesStream.TryReadAssemblyRefRow(origRid, out var row);
|
||||||
|
Debug.Assert(b);
|
||||||
|
version = new Version(row.MajorVersion, row.MinorVersion, row.BuildNumber, row.RevisionNumber);
|
||||||
|
attributes = (int)row.Flags;
|
||||||
|
var pkData = readerModule.BlobStream.Read(row.PublicKeyOrToken);
|
||||||
|
if ((attributes & (uint)AssemblyAttributes.PublicKey) != 0)
|
||||||
|
publicKeyOrToken = new PublicKey(pkData);
|
||||||
|
else
|
||||||
|
publicKeyOrToken = new PublicKeyToken(pkData);
|
||||||
|
name = readerModule.StringsStream.ReadNoNull(row.Name);
|
||||||
|
culture = readerModule.StringsStream.ReadNoNull(row.Locale);
|
||||||
|
hashValue = readerModule.BlobStream.Read(row.HashValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,832 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Xml;
|
||||||
|
using dnlib.Threading;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves assemblies
|
||||||
|
/// </summary>
|
||||||
|
public class AssemblyResolver : IAssemblyResolver {
|
||||||
|
static readonly ModuleDef nullModule = new ModuleDefUser();
|
||||||
|
|
||||||
|
// DLL files are searched before EXE files
|
||||||
|
static readonly string[] assemblyExtensions = new string[] { ".dll", ".exe" };
|
||||||
|
static readonly string[] winMDAssemblyExtensions = new string[] { ".winmd" };
|
||||||
|
|
||||||
|
static readonly List<GacInfo> gacInfos;
|
||||||
|
static readonly string[] extraMonoPaths;
|
||||||
|
static readonly string[] monoVerDirs = new string[] {
|
||||||
|
// The "-api" dirs are reference assembly dirs.
|
||||||
|
"4.5", @"4.5\Facades", "4.5-api", @"4.5-api\Facades", "4.0", "4.0-api",
|
||||||
|
"3.5", "3.5-api", "3.0", "3.0-api", "2.0", "2.0-api",
|
||||||
|
"1.1", "1.0",
|
||||||
|
};
|
||||||
|
|
||||||
|
ModuleContext defaultModuleContext;
|
||||||
|
readonly Dictionary<ModuleDef, List<string>> moduleSearchPaths = new Dictionary<ModuleDef, List<string>>();
|
||||||
|
readonly Dictionary<string, AssemblyDef> cachedAssemblies = new Dictionary<string, AssemblyDef>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
readonly List<string> preSearchPaths = new List<string>();
|
||||||
|
readonly List<string> postSearchPaths = new List<string>();
|
||||||
|
bool findExactMatch;
|
||||||
|
bool enableFrameworkRedirect;
|
||||||
|
bool enableTypeDefCache = true;
|
||||||
|
bool useGac = true;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
readonly Lock theLock = Lock.Create();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sealed class GacInfo {
|
||||||
|
public readonly int Version;
|
||||||
|
public readonly string Path;
|
||||||
|
public readonly string Prefix;
|
||||||
|
public readonly string[] SubDirs;
|
||||||
|
|
||||||
|
public GacInfo(int version, string prefix, string path, string[] subDirs) {
|
||||||
|
Version = version;
|
||||||
|
Prefix = prefix;
|
||||||
|
Path = path;
|
||||||
|
SubDirs = subDirs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static AssemblyResolver() {
|
||||||
|
gacInfos = new List<GacInfo>();
|
||||||
|
|
||||||
|
if (Type.GetType("Mono.Runtime") is not null) {
|
||||||
|
var dirs = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
var extraMonoPathsList = new List<string>();
|
||||||
|
foreach (var prefix in FindMonoPrefixes()) {
|
||||||
|
var dir = Path.Combine(Path.Combine(Path.Combine(prefix, "lib"), "mono"), "gac");
|
||||||
|
if (dirs.ContainsKey(dir))
|
||||||
|
continue;
|
||||||
|
dirs[dir] = true;
|
||||||
|
|
||||||
|
if (Directory.Exists(dir)) {
|
||||||
|
gacInfos.Add(new GacInfo(-1, "", Path.GetDirectoryName(dir), new string[] {
|
||||||
|
Path.GetFileName(dir)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
dir = Path.GetDirectoryName(dir);
|
||||||
|
foreach (var verDir in monoVerDirs) {
|
||||||
|
var dir2 = dir;
|
||||||
|
foreach (var d in verDir.Split(new char[] { '\\' }))
|
||||||
|
dir2 = Path.Combine(dir2, d);
|
||||||
|
if (Directory.Exists(dir2))
|
||||||
|
extraMonoPathsList.Add(dir2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var paths = Environment.GetEnvironmentVariable("MONO_PATH");
|
||||||
|
if (paths is not null) {
|
||||||
|
foreach (var tmp in paths.Split(Path.PathSeparator)) {
|
||||||
|
var path = tmp.Trim();
|
||||||
|
if (path != string.Empty && Directory.Exists(path))
|
||||||
|
extraMonoPathsList.Add(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extraMonoPaths = extraMonoPathsList.ToArray();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var windir = Environment.GetEnvironmentVariable("WINDIR");
|
||||||
|
if (!string.IsNullOrEmpty(windir)) {
|
||||||
|
string path;
|
||||||
|
|
||||||
|
// .NET Framework 1.x and 2.x
|
||||||
|
path = Path.Combine(windir, "assembly");
|
||||||
|
if (Directory.Exists(path)) {
|
||||||
|
gacInfos.Add(new GacInfo(2, "", path, new string[] {
|
||||||
|
"GAC_32", "GAC_64", "GAC_MSIL", "GAC"
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// .NET Framework 4.x
|
||||||
|
path = Path.Combine(Path.Combine(windir, "Microsoft.NET"), "assembly");
|
||||||
|
if (Directory.Exists(path)) {
|
||||||
|
gacInfos.Add(new GacInfo(4, "v4.0_", path, new string[] {
|
||||||
|
"GAC_32", "GAC_64", "GAC_MSIL"
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static string GetCurrentMonoPrefix() {
|
||||||
|
var path = typeof(object).Module.FullyQualifiedName;
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
path = Path.GetDirectoryName(path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IEnumerable<string> FindMonoPrefixes() {
|
||||||
|
yield return GetCurrentMonoPrefix();
|
||||||
|
|
||||||
|
var prefixes = Environment.GetEnvironmentVariable("MONO_GAC_PREFIX");
|
||||||
|
if (!string.IsNullOrEmpty(prefixes)) {
|
||||||
|
foreach (var tmp in prefixes.Split(Path.PathSeparator)) {
|
||||||
|
var prefix = tmp.Trim();
|
||||||
|
if (prefix != string.Empty)
|
||||||
|
yield return prefix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the default <see cref="ModuleContext"/>
|
||||||
|
/// </summary>
|
||||||
|
public ModuleContext DefaultModuleContext {
|
||||||
|
get => defaultModuleContext;
|
||||||
|
set => defaultModuleContext = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="Resolve"/> should find an assembly that matches exactly.
|
||||||
|
/// <c>false</c> if it first tries to match exactly, and if that fails, it picks an
|
||||||
|
/// assembly that is closest to the requested assembly.
|
||||||
|
/// </summary>
|
||||||
|
public bool FindExactMatch {
|
||||||
|
get => findExactMatch;
|
||||||
|
set => findExactMatch = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if resolved .NET framework assemblies can be redirected to the source
|
||||||
|
/// module's framework assembly version. Eg. if a resolved .NET Framework 3.5 assembly can be
|
||||||
|
/// redirected to a .NET Framework 4.0 assembly if the source module is a .NET Framework 4.0 assembly. This is
|
||||||
|
/// ignored if <see cref="FindExactMatch"/> is <c>true</c>.
|
||||||
|
/// </summary>
|
||||||
|
public bool EnableFrameworkRedirect {
|
||||||
|
get => enableFrameworkRedirect;
|
||||||
|
set => enableFrameworkRedirect = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If <c>true</c>, all modules in newly resolved assemblies will have their
|
||||||
|
/// <see cref="ModuleDef.EnableTypeDefFindCache"/> property set to <c>true</c>. This is
|
||||||
|
/// enabled by default since these modules shouldn't be modified by the user.
|
||||||
|
/// </summary>
|
||||||
|
public bool EnableTypeDefCache {
|
||||||
|
get => enableTypeDefCache;
|
||||||
|
set => enableTypeDefCache = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// true to search the Global Assembly Cache. Default value is true.
|
||||||
|
/// </summary>
|
||||||
|
public bool UseGAC {
|
||||||
|
get => useGac;
|
||||||
|
set => useGac = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets paths searched before trying the standard locations
|
||||||
|
/// </summary>
|
||||||
|
public IList<string> PreSearchPaths => preSearchPaths;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets paths searched after trying the standard locations
|
||||||
|
/// </summary>
|
||||||
|
public IList<string> PostSearchPaths => postSearchPaths;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public AssemblyResolver()
|
||||||
|
: this(null) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="defaultModuleContext">Module context for all resolved assemblies</param>
|
||||||
|
public AssemblyResolver(ModuleContext defaultModuleContext) {
|
||||||
|
this.defaultModuleContext = defaultModuleContext;
|
||||||
|
enableFrameworkRedirect = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public AssemblyDef Resolve(IAssembly assembly, ModuleDef sourceModule) {
|
||||||
|
if (assembly is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (EnableFrameworkRedirect && !FindExactMatch)
|
||||||
|
FrameworkRedirect.ApplyFrameworkRedirect(ref assembly, sourceModule);
|
||||||
|
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
var resolvedAssembly = Resolve2(assembly, sourceModule);
|
||||||
|
if (resolvedAssembly is null) {
|
||||||
|
string asmName = UTF8String.ToSystemStringOrEmpty(assembly.Name);
|
||||||
|
string asmNameTrimmed = asmName.Trim();
|
||||||
|
if (asmName != asmNameTrimmed) {
|
||||||
|
assembly = new AssemblyNameInfo {
|
||||||
|
Name = asmNameTrimmed,
|
||||||
|
Version = assembly.Version,
|
||||||
|
PublicKeyOrToken = assembly.PublicKeyOrToken,
|
||||||
|
Culture = assembly.Culture,
|
||||||
|
};
|
||||||
|
resolvedAssembly = Resolve2(assembly, sourceModule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolvedAssembly is null) {
|
||||||
|
// Make sure we don't search for this assembly again. This speeds up callers who
|
||||||
|
// keep asking for this assembly when trying to resolve many different TypeRefs
|
||||||
|
cachedAssemblies[GetAssemblyNameKey(assembly)] = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var key1 = GetAssemblyNameKey(resolvedAssembly);
|
||||||
|
var key2 = GetAssemblyNameKey(assembly);
|
||||||
|
cachedAssemblies.TryGetValue(key1, out var asm1);
|
||||||
|
cachedAssemblies.TryGetValue(key2, out var asm2);
|
||||||
|
|
||||||
|
if (asm1 != resolvedAssembly && asm2 != resolvedAssembly) {
|
||||||
|
// This assembly was just resolved
|
||||||
|
if (enableTypeDefCache) {
|
||||||
|
var modules = resolvedAssembly.Modules;
|
||||||
|
int count = modules.Count;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
var module = modules[i];
|
||||||
|
if (module is not null)
|
||||||
|
module.EnableTypeDefFindCache = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inserted = false;
|
||||||
|
if (!cachedAssemblies.ContainsKey(key1)) {
|
||||||
|
cachedAssemblies.Add(key1, resolvedAssembly);
|
||||||
|
inserted = true;
|
||||||
|
}
|
||||||
|
if (!cachedAssemblies.ContainsKey(key2)) {
|
||||||
|
cachedAssemblies.Add(key2, resolvedAssembly);
|
||||||
|
inserted = true;
|
||||||
|
}
|
||||||
|
if (inserted || asm1 == resolvedAssembly || asm2 == resolvedAssembly)
|
||||||
|
return resolvedAssembly;
|
||||||
|
|
||||||
|
// Dupe assembly. Don't insert it.
|
||||||
|
var dupeModule = resolvedAssembly.ManifestModule;
|
||||||
|
if (dupeModule is not null)
|
||||||
|
dupeModule.Dispose();
|
||||||
|
return asm1 ?? asm2;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a module's assembly to the assembly cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">The module whose assembly should be cached</param>
|
||||||
|
/// <returns><c>true</c> if <paramref name="module"/>'s assembly is cached, <c>false</c>
|
||||||
|
/// if it's not cached because some other assembly with the exact same full name has
|
||||||
|
/// already been cached or if <paramref name="module"/> or its assembly is <c>null</c>.</returns>
|
||||||
|
public bool AddToCache(ModuleDef module) => module is not null && AddToCache(module.Assembly);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add an assembly to the assembly cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="asm">The assembly</param>
|
||||||
|
/// <returns><c>true</c> if <paramref name="asm"/> is cached, <c>false</c> if it's not
|
||||||
|
/// cached because some other assembly with the exact same full name has already been
|
||||||
|
/// cached or if <paramref name="asm"/> is <c>null</c>.</returns>
|
||||||
|
public bool AddToCache(AssemblyDef asm) {
|
||||||
|
if (asm is null)
|
||||||
|
return false;
|
||||||
|
var asmKey = GetAssemblyNameKey(asm);
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
if (cachedAssemblies.TryGetValue(asmKey, out var cachedAsm) && cachedAsm is not null)
|
||||||
|
return asm == cachedAsm;
|
||||||
|
|
||||||
|
if (enableTypeDefCache)
|
||||||
|
{
|
||||||
|
var modules = asm.Modules;
|
||||||
|
int count = modules.Count;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var module = modules[i];
|
||||||
|
if (module is not null)
|
||||||
|
module.EnableTypeDefFindCache = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cachedAssemblies[asmKey] = asm;
|
||||||
|
return true;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a module's assembly from the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">The module</param>
|
||||||
|
/// <returns><c>true</c> if its assembly was removed, <c>false</c> if it wasn't removed
|
||||||
|
/// since it wasn't in the cache, it has no assembly, or <paramref name="module"/> was
|
||||||
|
/// <c>null</c></returns>
|
||||||
|
public bool Remove(ModuleDef module) => module is not null && Remove(module.Assembly);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the assembly from the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="asm">The assembly</param>
|
||||||
|
/// <returns><c>true</c> if it was removed, <c>false</c> if it wasn't removed since it
|
||||||
|
/// wasn't in the cache or if <paramref name="asm"/> was <c>null</c></returns>
|
||||||
|
public bool Remove(AssemblyDef asm) {
|
||||||
|
if (asm is null)
|
||||||
|
return false;
|
||||||
|
var asmKey = GetAssemblyNameKey(asm);
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
return cachedAssemblies.Remove(asmKey);
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears the cache and calls <see cref="IDisposable.Dispose()"/> on each cached module.
|
||||||
|
/// Use <see cref="Remove(AssemblyDef)"/> to remove any assemblies you added yourself
|
||||||
|
/// using <see cref="AddToCache(AssemblyDef)"/> before calling this method if you don't want
|
||||||
|
/// them disposed.
|
||||||
|
/// </summary>
|
||||||
|
public void Clear() {
|
||||||
|
List<AssemblyDef> asms;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
asms = new List<AssemblyDef>(cachedAssemblies.Values);
|
||||||
|
cachedAssemblies.Clear();
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
foreach (var asm in asms) {
|
||||||
|
if (asm is null)
|
||||||
|
continue;
|
||||||
|
foreach (var mod in asm.Modules)
|
||||||
|
mod.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the cached assemblies in this resolver.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The cached assemblies.</returns>
|
||||||
|
public IEnumerable<AssemblyDef> GetCachedAssemblies() {
|
||||||
|
AssemblyDef[] assemblies;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterReadLock(); try {
|
||||||
|
#endif
|
||||||
|
assemblies = cachedAssemblies.Values.ToArray();
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitReadLock(); }
|
||||||
|
#endif
|
||||||
|
return assemblies;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string GetAssemblyNameKey(IAssembly asmName) {
|
||||||
|
// Make sure the name contains PublicKeyToken= and not PublicKey=
|
||||||
|
return asmName.FullNameToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssemblyDef Resolve2(IAssembly assembly, ModuleDef sourceModule) {
|
||||||
|
if (cachedAssemblies.TryGetValue(GetAssemblyNameKey(assembly), out var resolvedAssembly))
|
||||||
|
return resolvedAssembly;
|
||||||
|
|
||||||
|
var moduleContext = defaultModuleContext;
|
||||||
|
if (moduleContext is null && sourceModule is not null)
|
||||||
|
moduleContext = sourceModule.Context;
|
||||||
|
|
||||||
|
resolvedAssembly = FindExactAssembly(assembly, PreFindAssemblies(assembly, sourceModule, true), moduleContext) ??
|
||||||
|
FindExactAssembly(assembly, FindAssemblies(assembly, sourceModule, true), moduleContext) ??
|
||||||
|
FindExactAssembly(assembly, PostFindAssemblies(assembly, sourceModule, true), moduleContext);
|
||||||
|
if (resolvedAssembly is not null)
|
||||||
|
return resolvedAssembly;
|
||||||
|
|
||||||
|
if (!findExactMatch) {
|
||||||
|
resolvedAssembly = FindClosestAssembly(assembly);
|
||||||
|
resolvedAssembly = FindClosestAssembly(assembly, resolvedAssembly, PreFindAssemblies(assembly, sourceModule, false), moduleContext);
|
||||||
|
resolvedAssembly = FindClosestAssembly(assembly, resolvedAssembly, FindAssemblies(assembly, sourceModule, false), moduleContext);
|
||||||
|
resolvedAssembly = FindClosestAssembly(assembly, resolvedAssembly, PostFindAssemblies(assembly, sourceModule, false), moduleContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolvedAssembly;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds an assembly that exactly matches the requested assembly
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assembly">Assembly to find</param>
|
||||||
|
/// <param name="paths">Search paths or <c>null</c> if none</param>
|
||||||
|
/// <param name="moduleContext">Module context</param>
|
||||||
|
/// <returns>An <see cref="AssemblyDef"/> instance or <c>null</c> if an exact match
|
||||||
|
/// couldn't be found.</returns>
|
||||||
|
AssemblyDef FindExactAssembly(IAssembly assembly, IEnumerable<string> paths, ModuleContext moduleContext) {
|
||||||
|
if (paths is null)
|
||||||
|
return null;
|
||||||
|
var asmComparer = AssemblyNameComparer.CompareAll;
|
||||||
|
foreach (var path in paths) {
|
||||||
|
ModuleDefMD mod = null;
|
||||||
|
try {
|
||||||
|
mod = ModuleDefMD.Load(path, moduleContext);
|
||||||
|
var asm = mod.Assembly;
|
||||||
|
if (asm is not null && asmComparer.Equals(assembly, asm)) {
|
||||||
|
mod = null;
|
||||||
|
return asm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (mod is not null)
|
||||||
|
mod.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds the closest assembly from the already cached assemblies
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assembly">Assembly to find</param>
|
||||||
|
/// <returns>The closest <see cref="AssemblyDef"/> or <c>null</c> if none found</returns>
|
||||||
|
AssemblyDef FindClosestAssembly(IAssembly assembly) {
|
||||||
|
AssemblyDef closest = null;
|
||||||
|
var asmComparer = AssemblyNameComparer.CompareAll;
|
||||||
|
foreach (var kv in cachedAssemblies) {
|
||||||
|
var asm = kv.Value;
|
||||||
|
if (asm is null)
|
||||||
|
continue;
|
||||||
|
if (asmComparer.CompareClosest(assembly, closest, asm) == 1)
|
||||||
|
closest = asm;
|
||||||
|
}
|
||||||
|
return closest;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssemblyDef FindClosestAssembly(IAssembly assembly, AssemblyDef closest, IEnumerable<string> paths, ModuleContext moduleContext) {
|
||||||
|
if (paths is null)
|
||||||
|
return closest;
|
||||||
|
var asmComparer = AssemblyNameComparer.CompareAll;
|
||||||
|
foreach (var path in paths) {
|
||||||
|
ModuleDefMD mod = null;
|
||||||
|
try {
|
||||||
|
mod = ModuleDefMD.Load(path, moduleContext);
|
||||||
|
var asm = mod.Assembly;
|
||||||
|
if (asm is not null && asmComparer.CompareClosest(assembly, closest, asm) == 1) {
|
||||||
|
if (!IsCached(closest) && closest is not null) {
|
||||||
|
var closeMod = closest.ManifestModule;
|
||||||
|
if (closeMod is not null)
|
||||||
|
closeMod.Dispose();
|
||||||
|
}
|
||||||
|
closest = asm;
|
||||||
|
mod = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (mod is not null)
|
||||||
|
mod.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return closest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns <c>true</c> if <paramref name="asm"/> is inserted in <see cref="cachedAssemblies"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="asm">Assembly to check</param>
|
||||||
|
bool IsCached(AssemblyDef asm) {
|
||||||
|
if (asm is null)
|
||||||
|
return false;
|
||||||
|
return cachedAssemblies.TryGetValue(GetAssemblyNameKey(asm), out var cachedAsm) &&
|
||||||
|
cachedAsm == asm;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<string> FindAssemblies2(IAssembly assembly, IEnumerable<string> paths) {
|
||||||
|
if (paths is not null) {
|
||||||
|
var asmSimpleName = UTF8String.ToSystemStringOrEmpty(assembly.Name);
|
||||||
|
var exts = assembly.IsContentTypeWindowsRuntime ? winMDAssemblyExtensions : assemblyExtensions;
|
||||||
|
foreach (var ext in exts) {
|
||||||
|
foreach (var path in paths) {
|
||||||
|
string fullPath;
|
||||||
|
try {
|
||||||
|
fullPath = Path.Combine(path, asmSimpleName + ext);
|
||||||
|
}
|
||||||
|
catch (ArgumentException) {
|
||||||
|
// Invalid path chars
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
if (File.Exists(fullPath))
|
||||||
|
yield return fullPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called before <see cref="FindAssemblies"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assembly">Assembly to find</param>
|
||||||
|
/// <param name="sourceModule">The module that needs to resolve an assembly or <c>null</c></param>
|
||||||
|
/// <param name="matchExactly">We're trying to find an exact match</param>
|
||||||
|
/// <returns><c>null</c> or an enumerable of full paths to try</returns>
|
||||||
|
protected virtual IEnumerable<string> PreFindAssemblies(IAssembly assembly, ModuleDef sourceModule, bool matchExactly) {
|
||||||
|
foreach (var path in FindAssemblies2(assembly, preSearchPaths))
|
||||||
|
yield return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called after <see cref="FindAssemblies"/> (if it fails)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assembly">Assembly to find</param>
|
||||||
|
/// <param name="sourceModule">The module that needs to resolve an assembly or <c>null</c></param>
|
||||||
|
/// <param name="matchExactly">We're trying to find an exact match</param>
|
||||||
|
/// <returns><c>null</c> or an enumerable of full paths to try</returns>
|
||||||
|
protected virtual IEnumerable<string> PostFindAssemblies(IAssembly assembly, ModuleDef sourceModule, bool matchExactly) {
|
||||||
|
foreach (var path in FindAssemblies2(assembly, postSearchPaths))
|
||||||
|
yield return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called after <see cref="PreFindAssemblies"/> (if it fails)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assembly">Assembly to find</param>
|
||||||
|
/// <param name="sourceModule">The module that needs to resolve an assembly or <c>null</c></param>
|
||||||
|
/// <param name="matchExactly">We're trying to find an exact match</param>
|
||||||
|
/// <returns><c>null</c> or an enumerable of full paths to try</returns>
|
||||||
|
protected virtual IEnumerable<string> FindAssemblies(IAssembly assembly, ModuleDef sourceModule, bool matchExactly) {
|
||||||
|
if (assembly.IsContentTypeWindowsRuntime) {
|
||||||
|
string path;
|
||||||
|
try {
|
||||||
|
path = Path.Combine(Path.Combine(Environment.SystemDirectory, "WinMetadata"), assembly.Name + ".winmd");
|
||||||
|
}
|
||||||
|
catch (ArgumentException) {
|
||||||
|
// Invalid path chars
|
||||||
|
path = null;
|
||||||
|
}
|
||||||
|
if (File.Exists(path))
|
||||||
|
yield return path;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (UseGAC) {
|
||||||
|
foreach (var path in FindAssembliesGac(assembly, sourceModule, matchExactly))
|
||||||
|
yield return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (var path in FindAssembliesModuleSearchPaths(assembly, sourceModule, matchExactly))
|
||||||
|
yield return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<string> FindAssembliesGac(IAssembly assembly, ModuleDef sourceModule, bool matchExactly) {
|
||||||
|
if (matchExactly)
|
||||||
|
return FindAssembliesGacExactly(assembly, sourceModule);
|
||||||
|
return FindAssembliesGacAny(assembly, sourceModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<GacInfo> GetGacInfos(ModuleDef sourceModule) {
|
||||||
|
int version = sourceModule is null ? int.MinValue : sourceModule.IsClr40 ? 4 : 2;
|
||||||
|
// Try the correct GAC first (eg. GAC4 if it's a .NET Framework 4 assembly)
|
||||||
|
foreach (var gacInfo in gacInfos) {
|
||||||
|
if (gacInfo.Version == version)
|
||||||
|
yield return gacInfo;
|
||||||
|
}
|
||||||
|
foreach (var gacInfo in gacInfos) {
|
||||||
|
if (gacInfo.Version != version)
|
||||||
|
yield return gacInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<string> FindAssembliesGacExactly(IAssembly assembly, ModuleDef sourceModule) {
|
||||||
|
foreach (var gacInfo in GetGacInfos(sourceModule)) {
|
||||||
|
foreach (var path in FindAssembliesGacExactly(gacInfo, assembly, sourceModule))
|
||||||
|
yield return path;
|
||||||
|
}
|
||||||
|
if (extraMonoPaths is not null) {
|
||||||
|
foreach (var path in GetExtraMonoPaths(assembly, sourceModule))
|
||||||
|
yield return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static IEnumerable<string> GetExtraMonoPaths(IAssembly assembly, ModuleDef sourceModule) {
|
||||||
|
if (extraMonoPaths is not null) {
|
||||||
|
foreach (var dir in extraMonoPaths) {
|
||||||
|
string file;
|
||||||
|
try {
|
||||||
|
file = Path.Combine(dir, assembly.Name + ".dll");
|
||||||
|
}
|
||||||
|
catch (ArgumentException) {
|
||||||
|
// Invalid path chars
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (File.Exists(file))
|
||||||
|
yield return file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<string> FindAssembliesGacExactly(GacInfo gacInfo, IAssembly assembly, ModuleDef sourceModule) {
|
||||||
|
var pkt = PublicKeyBase.ToPublicKeyToken(assembly.PublicKeyOrToken);
|
||||||
|
if (gacInfo is not null && pkt is not null) {
|
||||||
|
string pktString = pkt.ToString();
|
||||||
|
string verString = Utils.CreateVersionWithNoUndefinedValues(assembly.Version).ToString();
|
||||||
|
var cultureString = UTF8String.ToSystemStringOrEmpty(assembly.Culture);
|
||||||
|
if (cultureString.Equals("neutral", StringComparison.OrdinalIgnoreCase))
|
||||||
|
cultureString = string.Empty;
|
||||||
|
var asmSimpleName = UTF8String.ToSystemStringOrEmpty(assembly.Name);
|
||||||
|
foreach (var subDir in gacInfo.SubDirs) {
|
||||||
|
var baseDir = Path.Combine(gacInfo.Path, subDir);
|
||||||
|
try {
|
||||||
|
baseDir = Path.Combine(baseDir, asmSimpleName);
|
||||||
|
}
|
||||||
|
catch (ArgumentException) {
|
||||||
|
// Invalid path chars
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
baseDir = Path.Combine(baseDir, $"{gacInfo.Prefix}{verString}_{cultureString}_{pktString}");
|
||||||
|
var pathName = Path.Combine(baseDir, asmSimpleName + ".dll");
|
||||||
|
if (File.Exists(pathName))
|
||||||
|
yield return pathName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<string> FindAssembliesGacAny(IAssembly assembly, ModuleDef sourceModule) {
|
||||||
|
foreach (var gacInfo in GetGacInfos(sourceModule)) {
|
||||||
|
foreach (var path in FindAssembliesGacAny(gacInfo, assembly, sourceModule))
|
||||||
|
yield return path;
|
||||||
|
}
|
||||||
|
if (extraMonoPaths is not null) {
|
||||||
|
foreach (var path in GetExtraMonoPaths(assembly, sourceModule))
|
||||||
|
yield return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<string> FindAssembliesGacAny(GacInfo gacInfo, IAssembly assembly, ModuleDef sourceModule) {
|
||||||
|
if (gacInfo is not null) {
|
||||||
|
var asmSimpleName = UTF8String.ToSystemStringOrEmpty(assembly.Name);
|
||||||
|
foreach (var subDir in gacInfo.SubDirs) {
|
||||||
|
var baseDir = Path.Combine(gacInfo.Path, subDir);
|
||||||
|
try {
|
||||||
|
baseDir = Path.Combine(baseDir, asmSimpleName);
|
||||||
|
}
|
||||||
|
catch (ArgumentException) {
|
||||||
|
// Invalid path chars
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
foreach (var dir in GetDirs(baseDir)) {
|
||||||
|
var pathName = Path.Combine(dir, asmSimpleName + ".dll");
|
||||||
|
if (File.Exists(pathName))
|
||||||
|
yield return pathName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<string> GetDirs(string baseDir) {
|
||||||
|
if (!Directory.Exists(baseDir))
|
||||||
|
return Array2.Empty<string>();
|
||||||
|
var dirs = new List<string>();
|
||||||
|
try {
|
||||||
|
foreach (var di in new DirectoryInfo(baseDir).GetDirectories())
|
||||||
|
dirs.Add(di.FullName);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
}
|
||||||
|
return dirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<string> FindAssembliesModuleSearchPaths(IAssembly assembly, ModuleDef sourceModule, bool matchExactly) {
|
||||||
|
string asmSimpleName = UTF8String.ToSystemStringOrEmpty(assembly.Name);
|
||||||
|
var searchPaths = GetSearchPaths(sourceModule);
|
||||||
|
var exts = assembly.IsContentTypeWindowsRuntime ? winMDAssemblyExtensions : assemblyExtensions;
|
||||||
|
foreach (var ext in exts) {
|
||||||
|
foreach (var path in searchPaths) {
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
string path2;
|
||||||
|
try {
|
||||||
|
if (i == 0)
|
||||||
|
path2 = Path.Combine(path, asmSimpleName + ext);
|
||||||
|
else
|
||||||
|
path2 = Path.Combine(Path.Combine(path, asmSimpleName), asmSimpleName + ext);
|
||||||
|
}
|
||||||
|
catch (ArgumentException) {
|
||||||
|
// Invalid path chars
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
if (File.Exists(path2))
|
||||||
|
yield return path2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all search paths to use for this module
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">The module or <c>null</c> if unknown</param>
|
||||||
|
/// <returns>A list of all search paths to use for this module</returns>
|
||||||
|
IEnumerable<string> GetSearchPaths(ModuleDef module) {
|
||||||
|
var keyModule = module;
|
||||||
|
if (keyModule is null)
|
||||||
|
keyModule = nullModule;
|
||||||
|
if (moduleSearchPaths.TryGetValue(keyModule, out var searchPaths))
|
||||||
|
return searchPaths;
|
||||||
|
moduleSearchPaths[keyModule] = searchPaths = new List<string>(GetModuleSearchPaths(module));
|
||||||
|
return searchPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all module search paths. This is usually empty unless its assembly has
|
||||||
|
/// a <c>.config</c> file specifying any additional private search paths in a
|
||||||
|
/// <probing/> element.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">The module or <c>null</c> if unknown</param>
|
||||||
|
/// <returns>A list of search paths</returns>
|
||||||
|
protected virtual IEnumerable<string> GetModuleSearchPaths(ModuleDef module) => GetModulePrivateSearchPaths(module);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all private assembly search paths as found in the module's <c>.config</c> file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">The module or <c>null</c> if unknown</param>
|
||||||
|
/// <returns>A list of search paths</returns>
|
||||||
|
protected IEnumerable<string> GetModulePrivateSearchPaths(ModuleDef module) {
|
||||||
|
if (module is null)
|
||||||
|
return Array2.Empty<string>();
|
||||||
|
var asm = module.Assembly;
|
||||||
|
if (asm is null)
|
||||||
|
return Array2.Empty<string>();
|
||||||
|
module = asm.ManifestModule;
|
||||||
|
if (module is null)
|
||||||
|
return Array2.Empty<string>(); // Should never happen
|
||||||
|
|
||||||
|
string baseDir = null;
|
||||||
|
try {
|
||||||
|
var imageName = module.Location;
|
||||||
|
if (imageName != string.Empty) {
|
||||||
|
var directoryInfo = Directory.GetParent(imageName);
|
||||||
|
if (directoryInfo is not null) {
|
||||||
|
baseDir = directoryInfo.FullName;
|
||||||
|
var configName = imageName + ".config";
|
||||||
|
if (File.Exists(configName))
|
||||||
|
return GetPrivatePaths(baseDir, configName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
}
|
||||||
|
if (baseDir is not null)
|
||||||
|
return new List<string> { baseDir };
|
||||||
|
return Array2.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<string> GetPrivatePaths(string baseDir, string configFileName) {
|
||||||
|
var searchPaths = new List<string>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
var dirName = Path.GetDirectoryName(Path.GetFullPath(configFileName));
|
||||||
|
searchPaths.Add(dirName);
|
||||||
|
|
||||||
|
using (var xmlStream = new FileStream(configFileName, FileMode.Open, FileAccess.Read, FileShare.Read)) {
|
||||||
|
var doc = new XmlDocument();
|
||||||
|
doc.Load(XmlReader.Create(xmlStream));
|
||||||
|
foreach (var tmp in doc.GetElementsByTagName("probing")) {
|
||||||
|
var probingElem = tmp as XmlElement;
|
||||||
|
if (probingElem is null)
|
||||||
|
continue;
|
||||||
|
var privatePath = probingElem.GetAttribute("privatePath");
|
||||||
|
if (string.IsNullOrEmpty(privatePath))
|
||||||
|
continue;
|
||||||
|
foreach (var tmp2 in privatePath.Split(';')) {
|
||||||
|
var path = tmp2.Trim();
|
||||||
|
if (path == "")
|
||||||
|
continue;
|
||||||
|
var newPath = Path.GetFullPath(Path.Combine(dirName, path.Replace('\\', Path.DirectorySeparatorChar)));
|
||||||
|
if (Directory.Exists(newPath) && newPath.StartsWith(baseDir + Path.DirectorySeparatorChar))
|
||||||
|
searchPaths.Add(newPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ArgumentException) {
|
||||||
|
}
|
||||||
|
catch (IOException) {
|
||||||
|
}
|
||||||
|
catch (XmlException) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return searchPaths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// See CorHdr.h/CorCallingConvention
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum CallingConvention : byte {
|
||||||
|
/// <summary>The managed calling convention</summary>
|
||||||
|
Default = 0x0,
|
||||||
|
/// <summary/>
|
||||||
|
C = 0x1,
|
||||||
|
/// <summary/>
|
||||||
|
StdCall = 0x2,
|
||||||
|
/// <summary/>
|
||||||
|
ThisCall = 0x3,
|
||||||
|
/// <summary/>
|
||||||
|
FastCall = 0x4,
|
||||||
|
/// <summary/>
|
||||||
|
VarArg = 0x5,
|
||||||
|
/// <summary/>
|
||||||
|
Field = 0x6,
|
||||||
|
/// <summary/>
|
||||||
|
LocalSig = 0x7,
|
||||||
|
/// <summary/>
|
||||||
|
Property = 0x8,
|
||||||
|
/// <summary>Unmanaged calling convention encoded as modopts</summary>
|
||||||
|
Unmanaged = 0x9,
|
||||||
|
/// <summary>generic method instantiation</summary>
|
||||||
|
GenericInst = 0xA,
|
||||||
|
/// <summary>used ONLY for 64bit vararg PInvoke calls</summary>
|
||||||
|
NativeVarArg = 0xB,
|
||||||
|
|
||||||
|
/// <summary>Calling convention is bottom 4 bits</summary>
|
||||||
|
Mask = 0x0F,
|
||||||
|
|
||||||
|
/// <summary>Generic method</summary>
|
||||||
|
Generic = 0x10,
|
||||||
|
/// <summary>Method needs a 'this' parameter</summary>
|
||||||
|
HasThis = 0x20,
|
||||||
|
/// <summary>'this' parameter is the first arg if set (else it's hidden)</summary>
|
||||||
|
ExplicitThis = 0x40,
|
||||||
|
/// <summary>Used internally by the CLR</summary>
|
||||||
|
ReservedByCLR = 0x80,
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,99 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using dnlib.DotNet.MD;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// A high-level representation of a row in the ClassLayout table
|
||||||
|
/// </summary>
|
||||||
|
public abstract class ClassLayout : IMDTokenProvider {
|
||||||
|
/// <summary>
|
||||||
|
/// The row id in its table
|
||||||
|
/// </summary>
|
||||||
|
protected uint rid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public MDToken MDToken => new MDToken(Table.ClassLayout, rid);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint Rid {
|
||||||
|
get => rid;
|
||||||
|
set => rid = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column ClassLayout.PackingSize
|
||||||
|
/// </summary>
|
||||||
|
public ushort PackingSize {
|
||||||
|
get => packingSize;
|
||||||
|
set => packingSize = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected ushort packingSize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column ClassLayout.ClassSize
|
||||||
|
/// </summary>
|
||||||
|
public uint ClassSize {
|
||||||
|
get => classSize;
|
||||||
|
set => classSize = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected uint classSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A ClassLayout row created by the user and not present in the original .NET file
|
||||||
|
/// </summary>
|
||||||
|
public class ClassLayoutUser : ClassLayout {
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public ClassLayoutUser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="packingSize">PackingSize</param>
|
||||||
|
/// <param name="classSize">ClassSize</param>
|
||||||
|
public ClassLayoutUser(ushort packingSize, uint classSize) {
|
||||||
|
this.packingSize = packingSize;
|
||||||
|
this.classSize = classSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Created from a row in the ClassLayout table
|
||||||
|
/// </summary>
|
||||||
|
sealed class ClassLayoutMD : ClassLayout, IMDTokenProviderMD {
|
||||||
|
readonly uint origRid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint OrigRid => origRid;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readerModule">The module which contains this <c>ClassLayout</c> row</param>
|
||||||
|
/// <param name="rid">Row ID</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If <paramref name="readerModule"/> is <c>null</c></exception>
|
||||||
|
/// <exception cref="ArgumentException">If <paramref name="rid"/> is invalid</exception>
|
||||||
|
public ClassLayoutMD(ModuleDefMD readerModule, uint rid) {
|
||||||
|
#if DEBUG
|
||||||
|
if (readerModule is null)
|
||||||
|
throw new ArgumentNullException("readerModule");
|
||||||
|
if (readerModule.TablesStream.ClassLayoutTable.IsInvalidRID(rid))
|
||||||
|
throw new BadImageFormatException($"ClassLayout rid {rid} does not exist");
|
||||||
|
#endif
|
||||||
|
origRid = rid;
|
||||||
|
this.rid = rid;
|
||||||
|
bool b = readerModule.TablesStream.TryReadClassLayoutRow(origRid, out var row);
|
||||||
|
Debug.Assert(b);
|
||||||
|
classSize = row.ClassSize;
|
||||||
|
packingSize = row.PackingSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,204 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using dnlib.DotNet.MD;
|
||||||
|
using dnlib.IO;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// A high-level representation of a row in the Constant table
|
||||||
|
/// </summary>
|
||||||
|
public abstract class Constant : IMDTokenProvider {
|
||||||
|
/// <summary>
|
||||||
|
/// The row id in its table
|
||||||
|
/// </summary>
|
||||||
|
protected uint rid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public MDToken MDToken => new MDToken(Table.Constant, rid);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint Rid {
|
||||||
|
get => rid;
|
||||||
|
set => rid = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column Constant.Type
|
||||||
|
/// </summary>
|
||||||
|
public ElementType Type {
|
||||||
|
get => type;
|
||||||
|
set => type = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected ElementType type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column Constant.Value
|
||||||
|
/// </summary>
|
||||||
|
public object Value {
|
||||||
|
get => value;
|
||||||
|
set => this.value = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected object value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A Constant row created by the user and not present in the original .NET file
|
||||||
|
/// </summary>
|
||||||
|
public class ConstantUser : Constant {
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public ConstantUser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Value</param>
|
||||||
|
public ConstantUser(object value) {
|
||||||
|
type = GetElementType(value);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Value</param>
|
||||||
|
/// <param name="type">Type</param>
|
||||||
|
public ConstantUser(object value, ElementType type) {
|
||||||
|
this.type = type;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ElementType GetElementType(object value) {
|
||||||
|
if (value is null)
|
||||||
|
return ElementType.Class;
|
||||||
|
return System.Type.GetTypeCode(value.GetType()) switch {
|
||||||
|
TypeCode.Boolean => ElementType.Boolean,
|
||||||
|
TypeCode.Char => ElementType.Char,
|
||||||
|
TypeCode.SByte => ElementType.I1,
|
||||||
|
TypeCode.Byte => ElementType.U1,
|
||||||
|
TypeCode.Int16 => ElementType.I2,
|
||||||
|
TypeCode.UInt16 => ElementType.U2,
|
||||||
|
TypeCode.Int32 => ElementType.I4,
|
||||||
|
TypeCode.UInt32 => ElementType.U4,
|
||||||
|
TypeCode.Int64 => ElementType.I8,
|
||||||
|
TypeCode.UInt64 => ElementType.U8,
|
||||||
|
TypeCode.Single => ElementType.R4,
|
||||||
|
TypeCode.Double => ElementType.R8,
|
||||||
|
TypeCode.String => ElementType.String,
|
||||||
|
_ => ElementType.Void,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Created from a row in the Constant table
|
||||||
|
/// </summary>
|
||||||
|
sealed class ConstantMD : Constant, IMDTokenProviderMD {
|
||||||
|
readonly uint origRid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint OrigRid => origRid;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readerModule">The module which contains this <c>Constant</c> row</param>
|
||||||
|
/// <param name="rid">Row ID</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If <paramref name="readerModule"/> is <c>null</c></exception>
|
||||||
|
/// <exception cref="ArgumentException">If <paramref name="rid"/> is invalid</exception>
|
||||||
|
public ConstantMD(ModuleDefMD readerModule, uint rid) {
|
||||||
|
#if DEBUG
|
||||||
|
if (readerModule is null)
|
||||||
|
throw new ArgumentNullException("readerModule");
|
||||||
|
if (readerModule.TablesStream.ConstantTable.IsInvalidRID(rid))
|
||||||
|
throw new BadImageFormatException($"Constant rid {rid} does not exist");
|
||||||
|
#endif
|
||||||
|
origRid = rid;
|
||||||
|
this.rid = rid;
|
||||||
|
bool b = readerModule.TablesStream.TryReadConstantRow(origRid, out var row);
|
||||||
|
Debug.Assert(b);
|
||||||
|
type = (ElementType)row.Type;
|
||||||
|
var reader = readerModule.BlobStream.CreateReader(row.Value);
|
||||||
|
value = GetValue(type, ref reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
static object GetValue(ElementType etype, ref DataReader reader) {
|
||||||
|
switch (etype) {
|
||||||
|
case ElementType.Boolean:
|
||||||
|
if (reader.Length < 1)
|
||||||
|
return false;
|
||||||
|
return reader.ReadBoolean();
|
||||||
|
|
||||||
|
case ElementType.Char:
|
||||||
|
if (reader.Length < 2)
|
||||||
|
return (char)0;
|
||||||
|
return reader.ReadChar();
|
||||||
|
|
||||||
|
case ElementType.I1:
|
||||||
|
if (reader.Length < 1)
|
||||||
|
return (sbyte)0;
|
||||||
|
return reader.ReadSByte();
|
||||||
|
|
||||||
|
case ElementType.U1:
|
||||||
|
if (reader.Length < 1)
|
||||||
|
return (byte)0;
|
||||||
|
return reader.ReadByte();
|
||||||
|
|
||||||
|
case ElementType.I2:
|
||||||
|
if (reader.Length < 2)
|
||||||
|
return (short)0;
|
||||||
|
return reader.ReadInt16();
|
||||||
|
|
||||||
|
case ElementType.U2:
|
||||||
|
if (reader.Length < 2)
|
||||||
|
return (ushort)0;
|
||||||
|
return reader.ReadUInt16();
|
||||||
|
|
||||||
|
case ElementType.I4:
|
||||||
|
if (reader.Length < 4)
|
||||||
|
return (int)0;
|
||||||
|
return reader.ReadInt32();
|
||||||
|
|
||||||
|
case ElementType.U4:
|
||||||
|
if (reader.Length < 4)
|
||||||
|
return (uint)0;
|
||||||
|
return reader.ReadUInt32();
|
||||||
|
|
||||||
|
case ElementType.I8:
|
||||||
|
if (reader.Length < 8)
|
||||||
|
return (long)0;
|
||||||
|
return reader.ReadInt64();
|
||||||
|
|
||||||
|
case ElementType.U8:
|
||||||
|
if (reader.Length < 8)
|
||||||
|
return (ulong)0;
|
||||||
|
return reader.ReadUInt64();
|
||||||
|
|
||||||
|
case ElementType.R4:
|
||||||
|
if (reader.Length < 4)
|
||||||
|
return (float)0;
|
||||||
|
return reader.ReadSingle();
|
||||||
|
|
||||||
|
case ElementType.R8:
|
||||||
|
if (reader.Length < 8)
|
||||||
|
return (double)0;
|
||||||
|
return reader.ReadDouble();
|
||||||
|
|
||||||
|
case ElementType.String:
|
||||||
|
return reader.ReadUtf16String((int)(reader.BytesLeft / 2));
|
||||||
|
|
||||||
|
case ElementType.Class:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Default implementation of <see cref="ICorLibTypes"/>
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CorLibTypes : ICorLibTypes {
|
||||||
|
readonly ModuleDef module;
|
||||||
|
CorLibTypeSig typeVoid;
|
||||||
|
CorLibTypeSig typeBoolean;
|
||||||
|
CorLibTypeSig typeChar;
|
||||||
|
CorLibTypeSig typeSByte;
|
||||||
|
CorLibTypeSig typeByte;
|
||||||
|
CorLibTypeSig typeInt16;
|
||||||
|
CorLibTypeSig typeUInt16;
|
||||||
|
CorLibTypeSig typeInt32;
|
||||||
|
CorLibTypeSig typeUInt32;
|
||||||
|
CorLibTypeSig typeInt64;
|
||||||
|
CorLibTypeSig typeUInt64;
|
||||||
|
CorLibTypeSig typeSingle;
|
||||||
|
CorLibTypeSig typeDouble;
|
||||||
|
CorLibTypeSig typeString;
|
||||||
|
CorLibTypeSig typeTypedReference;
|
||||||
|
CorLibTypeSig typeIntPtr;
|
||||||
|
CorLibTypeSig typeUIntPtr;
|
||||||
|
CorLibTypeSig typeObject;
|
||||||
|
readonly AssemblyRef corLibAssemblyRef;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig Void => typeVoid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig Boolean => typeBoolean;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig Char => typeChar;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig SByte => typeSByte;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig Byte => typeByte;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig Int16 => typeInt16;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig UInt16 => typeUInt16;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig Int32 => typeInt32;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig UInt32 => typeUInt32;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig Int64 => typeInt64;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig UInt64 => typeUInt64;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig Single => typeSingle;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig Double => typeDouble;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig String => typeString;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig TypedReference => typeTypedReference;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig IntPtr => typeIntPtr;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig UIntPtr => typeUIntPtr;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CorLibTypeSig Object => typeObject;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public AssemblyRef AssemblyRef => corLibAssemblyRef;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">The owner module</param>
|
||||||
|
public CorLibTypes(ModuleDef module)
|
||||||
|
: this(module, null) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">The owner module</param>
|
||||||
|
/// <param name="corLibAssemblyRef">Corlib assembly reference or <c>null</c> if a default
|
||||||
|
/// assembly reference should be created</param>
|
||||||
|
public CorLibTypes(ModuleDef module, AssemblyRef corLibAssemblyRef) {
|
||||||
|
this.module = module;
|
||||||
|
this.corLibAssemblyRef = corLibAssemblyRef ?? CreateCorLibAssemblyRef();
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
AssemblyRef CreateCorLibAssemblyRef() => module.UpdateRowId(AssemblyRefUser.CreateMscorlibReferenceCLR20());
|
||||||
|
|
||||||
|
void Initialize() {
|
||||||
|
bool isCorLib = module.Assembly.IsCorLib();
|
||||||
|
typeVoid = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Void"), ElementType.Void);
|
||||||
|
typeBoolean = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Boolean"), ElementType.Boolean);
|
||||||
|
typeChar = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Char"), ElementType.Char);
|
||||||
|
typeSByte = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "SByte"), ElementType.I1);
|
||||||
|
typeByte = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Byte"), ElementType.U1);
|
||||||
|
typeInt16 = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Int16"), ElementType.I2);
|
||||||
|
typeUInt16 = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "UInt16"), ElementType.U2);
|
||||||
|
typeInt32 = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Int32"), ElementType.I4);
|
||||||
|
typeUInt32 = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "UInt32"), ElementType.U4);
|
||||||
|
typeInt64 = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Int64"), ElementType.I8);
|
||||||
|
typeUInt64 = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "UInt64"), ElementType.U8);
|
||||||
|
typeSingle = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Single"), ElementType.R4);
|
||||||
|
typeDouble = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Double"), ElementType.R8);
|
||||||
|
typeString = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "String"), ElementType.String);
|
||||||
|
typeTypedReference = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "TypedReference"), ElementType.TypedByRef);
|
||||||
|
typeIntPtr = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "IntPtr"), ElementType.I);
|
||||||
|
typeUIntPtr = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "UIntPtr"), ElementType.U);
|
||||||
|
typeObject = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Object"), ElementType.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
ITypeDefOrRef CreateCorLibTypeRef(bool isCorLib, string name) {
|
||||||
|
var tr = new TypeRefUser(module, "System", name, corLibAssemblyRef);
|
||||||
|
if (isCorLib) {
|
||||||
|
var td = module.Find(tr);
|
||||||
|
if (td is not null)
|
||||||
|
return td;
|
||||||
|
}
|
||||||
|
return module.UpdateRowId(tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public TypeRef GetTypeRef(string @namespace, string name) => module.UpdateRowId(new TypeRefUser(module, @namespace, name, corLibAssemblyRef));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,427 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using dnlib.DotNet.Writer;
|
||||||
|
using dnlib.IO;
|
||||||
|
using dnlib.PE;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
enum StubType {
|
||||||
|
Export,
|
||||||
|
EntryPoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class CpuArch {
|
||||||
|
// To support a new CPU arch, the easiest way is to check coreclr/src/ilasm/writer.cpp or
|
||||||
|
// coreclr/src/dlls/mscorpe/stubs.h, eg. ExportStubAMD64Template, ExportStubX86Template,
|
||||||
|
// ExportStubARMTemplate, ExportStubIA64Template, or use ilasm to generate a file with
|
||||||
|
// exports and check the stub
|
||||||
|
static readonly X86CpuArch x86CpuArch = new X86CpuArch();
|
||||||
|
static readonly X64CpuArch x64CpuArch = new X64CpuArch();
|
||||||
|
static readonly ItaniumCpuArch itaniumCpuArch = new ItaniumCpuArch();
|
||||||
|
static readonly ArmCpuArch armCpuArch = new ArmCpuArch();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the required alignment for the stubs, must be a power of 2
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stubType">Stub type</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public abstract uint GetStubAlignment(StubType stubType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size of a stub, it doesn't have to be a multiple of <see cref="GetStubAlignment(StubType)"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stubType">Stub type</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public abstract uint GetStubSize(StubType stubType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the offset of the code (entry point) relative to the start of the stub
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stubType">Stub type</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public abstract uint GetStubCodeOffset(StubType stubType);
|
||||||
|
|
||||||
|
public static bool TryGetCpuArch(Machine machine, out CpuArch cpuArch) {
|
||||||
|
switch (machine) {
|
||||||
|
case Machine.I386:
|
||||||
|
case Machine.I386_Native_Apple:
|
||||||
|
case Machine.I386_Native_FreeBSD:
|
||||||
|
case Machine.I386_Native_Linux:
|
||||||
|
case Machine.I386_Native_NetBSD:
|
||||||
|
case Machine.I386_Native_Sun:
|
||||||
|
cpuArch = x86CpuArch;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Machine.AMD64:
|
||||||
|
case Machine.AMD64_Native_Apple:
|
||||||
|
case Machine.AMD64_Native_FreeBSD:
|
||||||
|
case Machine.AMD64_Native_Linux:
|
||||||
|
case Machine.AMD64_Native_NetBSD:
|
||||||
|
case Machine.AMD64_Native_Sun:
|
||||||
|
cpuArch = x64CpuArch;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Machine.IA64:
|
||||||
|
cpuArch = itaniumCpuArch;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Machine.ARMNT:
|
||||||
|
case Machine.ARMNT_Native_Apple:
|
||||||
|
case Machine.ARMNT_Native_FreeBSD:
|
||||||
|
case Machine.ARMNT_Native_Linux:
|
||||||
|
case Machine.ARMNT_Native_NetBSD:
|
||||||
|
case Machine.ARMNT_Native_Sun:
|
||||||
|
cpuArch = armCpuArch;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Machine.ARM64:
|
||||||
|
case Machine.ARM64_Native_Apple:
|
||||||
|
case Machine.ARM64_Native_FreeBSD:
|
||||||
|
case Machine.ARM64_Native_Linux:
|
||||||
|
case Machine.ARM64_Native_NetBSD:
|
||||||
|
case Machine.ARM64_Native_Sun:
|
||||||
|
//TODO: Support ARM64
|
||||||
|
goto default;
|
||||||
|
|
||||||
|
default:
|
||||||
|
cpuArch = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the RVA of the func field that the stub jumps to
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">Reader, positioned at the stub func</param>
|
||||||
|
/// <param name="peImage">PE image</param>
|
||||||
|
/// <param name="funcRva">Updated with RVA of func field</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool TryGetExportedRvaFromStub(ref DataReader reader, IPEImage peImage, out uint funcRva) =>
|
||||||
|
TryGetExportedRvaFromStubCore(ref reader, peImage, out funcRva);
|
||||||
|
|
||||||
|
protected abstract bool TryGetExportedRvaFromStubCore(ref DataReader reader, IPEImage peImage, out uint funcRva);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes stub relocs, if needed
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stubType">Stub type</param>
|
||||||
|
/// <param name="relocDirectory">Reloc directory</param>
|
||||||
|
/// <param name="chunk">The chunk where this stub will be written to</param>
|
||||||
|
/// <param name="stubOffset">Offset of this stub in <paramref name="chunk"/></param>
|
||||||
|
public abstract void WriteStubRelocs(StubType stubType, RelocDirectory relocDirectory, IChunk chunk, uint stubOffset);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the stub that jumps to the managed function
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stubType">Stub type</param>
|
||||||
|
/// <param name="writer">Writer</param>
|
||||||
|
/// <param name="imageBase">Image base</param>
|
||||||
|
/// <param name="stubRva">RVA of this stub</param>
|
||||||
|
/// <param name="managedFuncRva">RVA of a pointer-sized field that contains the absolute address of the managed function</param>
|
||||||
|
public abstract void WriteStub(StubType stubType, DataWriter writer, ulong imageBase, uint stubRva, uint managedFuncRva);
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class X86CpuArch : CpuArch {
|
||||||
|
public override uint GetStubAlignment(StubType stubType) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetStubSize(StubType stubType) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
return 2/*padding*/ + 6;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetStubCodeOffset(StubType stubType) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
return 2/*padding*/;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool TryGetExportedRvaFromStubCore(ref DataReader reader, IPEImage peImage, out uint funcRva) {
|
||||||
|
funcRva = 0;
|
||||||
|
|
||||||
|
// FF25xxxxxxxx jmp DWORD PTR [xxxxxxxx]
|
||||||
|
if (reader.ReadUInt16() != 0x25FF)
|
||||||
|
return false;
|
||||||
|
funcRva = reader.ReadUInt32() - (uint)peImage.ImageNTHeaders.OptionalHeader.ImageBase;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteStubRelocs(StubType stubType, RelocDirectory relocDirectory, IChunk chunk, uint stubOffset) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
relocDirectory.Add(chunk, stubOffset + 4);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteStub(StubType stubType, DataWriter writer, ulong imageBase, uint stubRva, uint managedFuncRva) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
writer.WriteUInt16(0);// padding
|
||||||
|
writer.WriteUInt16(0x25FF);
|
||||||
|
writer.WriteUInt32((uint)imageBase + managedFuncRva);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class X64CpuArch : CpuArch {
|
||||||
|
public override uint GetStubAlignment(StubType stubType) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetStubSize(StubType stubType) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
return 2/*padding*/ + 12;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetStubCodeOffset(StubType stubType) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
return 2/*padding*/;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool TryGetExportedRvaFromStubCore(ref DataReader reader, IPEImage peImage, out uint funcRva) {
|
||||||
|
funcRva = 0;
|
||||||
|
|
||||||
|
// 48A1xxxxxxxxxxxxxxxx movabs rax,[xxxxxxxxxxxxxxxx]
|
||||||
|
// FFE0 jmp rax
|
||||||
|
if (reader.ReadUInt16() != 0xA148)
|
||||||
|
return false;
|
||||||
|
ulong absAddr = reader.ReadUInt64();
|
||||||
|
if (reader.ReadUInt16() != 0xE0FF)
|
||||||
|
return false;
|
||||||
|
ulong rva = absAddr - peImage.ImageNTHeaders.OptionalHeader.ImageBase;
|
||||||
|
if (rva > uint.MaxValue)
|
||||||
|
return false;
|
||||||
|
funcRva = (uint)rva;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteStubRelocs(StubType stubType, RelocDirectory relocDirectory, IChunk chunk, uint stubOffset) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
relocDirectory.Add(chunk, stubOffset + 4);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteStub(StubType stubType, DataWriter writer, ulong imageBase, uint stubRva, uint managedFuncRva) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
writer.WriteUInt16(0);// padding
|
||||||
|
writer.WriteUInt16(0xA148);
|
||||||
|
writer.WriteUInt64(imageBase + managedFuncRva);
|
||||||
|
writer.WriteUInt16(0xE0FF);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class ItaniumCpuArch : CpuArch {
|
||||||
|
public override uint GetStubAlignment(StubType stubType) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
return 16;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetStubSize(StubType stubType) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
return 0x30;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetStubCodeOffset(StubType stubType) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
return 0x20;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool TryGetExportedRvaFromStubCore(ref DataReader reader, IPEImage peImage, out uint funcRva) {
|
||||||
|
funcRva = 0;
|
||||||
|
|
||||||
|
// From ExportStubIA64Template in coreclr/src/ilasm/writer.cpp
|
||||||
|
//
|
||||||
|
// ld8 r9 = [gp] ;;
|
||||||
|
// ld8 r10 = [r9],8
|
||||||
|
// nop.i ;;
|
||||||
|
// ld8 gp = [r9]
|
||||||
|
// mov b6 = r10
|
||||||
|
// br.cond.sptk.few b6
|
||||||
|
//
|
||||||
|
// 0x0B, 0x48, 0x00, 0x02, 0x18, 0x10, 0xA0, 0x40,
|
||||||
|
// 0x24, 0x30, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00,
|
||||||
|
// 0x10, 0x08, 0x00, 0x12, 0x18, 0x10, 0x60, 0x50,
|
||||||
|
// 0x04, 0x80, 0x03, 0x00, 0x60, 0x00, 0x80, 0x00,
|
||||||
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,//address of the template
|
||||||
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //address of VTFixup slot
|
||||||
|
ulong addrTemplate = reader.ReadUInt64();
|
||||||
|
ulong absAddr = reader.ReadUInt64();
|
||||||
|
reader.Position = (uint)peImage.ToFileOffset((RVA)(addrTemplate - peImage.ImageNTHeaders.OptionalHeader.ImageBase));
|
||||||
|
if (reader.ReadUInt64() != 0x40A010180200480BUL)
|
||||||
|
return false;
|
||||||
|
if (reader.ReadUInt64() != 0x0004000000283024UL)
|
||||||
|
return false;
|
||||||
|
if (reader.ReadUInt64() != 0x5060101812000810UL)
|
||||||
|
return false;
|
||||||
|
if (reader.ReadUInt64() != 0x0080006000038004UL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ulong rva = absAddr - peImage.ImageNTHeaders.OptionalHeader.ImageBase;
|
||||||
|
if (rva > uint.MaxValue)
|
||||||
|
return false;
|
||||||
|
funcRva = (uint)rva;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteStubRelocs(StubType stubType, RelocDirectory relocDirectory, IChunk chunk, uint stubOffset) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
relocDirectory.Add(chunk, stubOffset + 0x20);
|
||||||
|
relocDirectory.Add(chunk, stubOffset + 0x28);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteStub(StubType stubType, DataWriter writer, ulong imageBase, uint stubRva, uint managedFuncRva) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
writer.WriteUInt64(0x40A010180200480BUL);
|
||||||
|
writer.WriteUInt64(0x0004000000283024UL);
|
||||||
|
writer.WriteUInt64(0x5060101812000810UL);
|
||||||
|
writer.WriteUInt64(0x0080006000038004UL);
|
||||||
|
writer.WriteUInt64(imageBase + stubRva);
|
||||||
|
writer.WriteUInt64(imageBase + managedFuncRva);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class ArmCpuArch : CpuArch {
|
||||||
|
public override uint GetStubAlignment(StubType stubType) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetStubSize(StubType stubType) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
return 8;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetStubCodeOffset(StubType stubType) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool TryGetExportedRvaFromStubCore(ref DataReader reader, IPEImage peImage, out uint funcRva) {
|
||||||
|
funcRva = 0;
|
||||||
|
|
||||||
|
// DFF800F0 ldr.w pc,[pc]
|
||||||
|
// xxxxxxxx
|
||||||
|
if (reader.ReadUInt32() != 0xF000F8DF)
|
||||||
|
return false;
|
||||||
|
funcRva = reader.ReadUInt32() - (uint)peImage.ImageNTHeaders.OptionalHeader.ImageBase;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteStubRelocs(StubType stubType, RelocDirectory relocDirectory, IChunk chunk, uint stubOffset) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
relocDirectory.Add(chunk, stubOffset + 4);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteStub(StubType stubType, DataWriter writer, ulong imageBase, uint stubRva, uint managedFuncRva) {
|
||||||
|
switch (stubType) {
|
||||||
|
case StubType.Export:
|
||||||
|
case StubType.EntryPoint:
|
||||||
|
writer.WriteUInt32(0xF000F8DF);
|
||||||
|
writer.WriteUInt32((uint)imageBase + managedFuncRva);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,462 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// A custom attribute
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CustomAttribute : ICustomAttribute {
|
||||||
|
ICustomAttributeType ctor;
|
||||||
|
byte[] rawData;
|
||||||
|
readonly IList<CAArgument> arguments;
|
||||||
|
readonly IList<CANamedArgument> namedArguments;
|
||||||
|
uint caBlobOffset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the custom attribute constructor
|
||||||
|
/// </summary>
|
||||||
|
public ICustomAttributeType Constructor {
|
||||||
|
get => ctor;
|
||||||
|
set => ctor = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the attribute type
|
||||||
|
/// </summary>
|
||||||
|
public ITypeDefOrRef AttributeType => ctor?.DeclaringType;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the full name of the attribute type
|
||||||
|
/// </summary>
|
||||||
|
public string TypeFullName {
|
||||||
|
get {
|
||||||
|
if (ctor is MemberRef mrCtor)
|
||||||
|
return mrCtor.GetDeclaringTypeFullName() ?? string.Empty;
|
||||||
|
|
||||||
|
if (ctor is MethodDef mdCtor) {
|
||||||
|
var declType = mdCtor.DeclaringType;
|
||||||
|
if (declType is not null)
|
||||||
|
return declType.FullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the attribute type
|
||||||
|
/// </summary>
|
||||||
|
internal string TypeName {
|
||||||
|
get {
|
||||||
|
if (ctor is MemberRef mrCtor)
|
||||||
|
return mrCtor.GetDeclaringTypeName() ?? string.Empty;
|
||||||
|
|
||||||
|
if (ctor is MethodDef mdCtor) {
|
||||||
|
var declType = mdCtor.DeclaringType;
|
||||||
|
if (declType is not null)
|
||||||
|
return declType.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if the raw custom attribute blob hasn't been parsed
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRawBlob => rawData is not null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the raw custom attribute blob or <c>null</c> if the CA was successfully parsed.
|
||||||
|
/// </summary>
|
||||||
|
public byte[] RawData => rawData;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all constructor arguments
|
||||||
|
/// </summary>
|
||||||
|
public IList<CAArgument> ConstructorArguments => arguments;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="ConstructorArguments"/> is not empty
|
||||||
|
/// </summary>
|
||||||
|
public bool HasConstructorArguments => arguments.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all named arguments (field and property values)
|
||||||
|
/// </summary>
|
||||||
|
public IList<CANamedArgument> NamedArguments => namedArguments;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="NamedArguments"/> is not empty
|
||||||
|
/// </summary>
|
||||||
|
public bool HasNamedArguments => namedArguments.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all <see cref="CANamedArgument"/>s that are field arguments
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<CANamedArgument> Fields {
|
||||||
|
get {
|
||||||
|
var namedArguments = this.namedArguments;
|
||||||
|
int count = namedArguments.Count;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
var namedArg = namedArguments[i];
|
||||||
|
if (namedArg.IsField)
|
||||||
|
yield return namedArg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all <see cref="CANamedArgument"/>s that are property arguments
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<CANamedArgument> Properties {
|
||||||
|
get {
|
||||||
|
var namedArguments = this.namedArguments;
|
||||||
|
int count = namedArguments.Count;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
var namedArg = namedArguments[i];
|
||||||
|
if (namedArg.IsProperty)
|
||||||
|
yield return namedArg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the #Blob offset or 0 if unknown
|
||||||
|
/// </summary>
|
||||||
|
public uint BlobOffset => caBlobOffset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ctor">Custom attribute constructor</param>
|
||||||
|
/// <param name="rawData">Raw custom attribute blob</param>
|
||||||
|
public CustomAttribute(ICustomAttributeType ctor, byte[] rawData)
|
||||||
|
: this(ctor, null, null, 0) => this.rawData = rawData;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ctor">Custom attribute constructor</param>
|
||||||
|
public CustomAttribute(ICustomAttributeType ctor)
|
||||||
|
: this(ctor, null, null, 0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ctor">Custom attribute constructor</param>
|
||||||
|
/// <param name="arguments">Constructor arguments or <c>null</c> if none</param>
|
||||||
|
public CustomAttribute(ICustomAttributeType ctor, IEnumerable<CAArgument> arguments)
|
||||||
|
: this(ctor, arguments, null) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ctor">Custom attribute constructor</param>
|
||||||
|
/// <param name="namedArguments">Named arguments or <c>null</c> if none</param>
|
||||||
|
public CustomAttribute(ICustomAttributeType ctor, IEnumerable<CANamedArgument> namedArguments)
|
||||||
|
: this(ctor, null, namedArguments) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ctor">Custom attribute constructor</param>
|
||||||
|
/// <param name="arguments">Constructor arguments or <c>null</c> if none</param>
|
||||||
|
/// <param name="namedArguments">Named arguments or <c>null</c> if none</param>
|
||||||
|
public CustomAttribute(ICustomAttributeType ctor, IEnumerable<CAArgument> arguments, IEnumerable<CANamedArgument> namedArguments)
|
||||||
|
: this(ctor, arguments, namedArguments, 0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ctor">Custom attribute constructor</param>
|
||||||
|
/// <param name="arguments">Constructor arguments or <c>null</c> if none</param>
|
||||||
|
/// <param name="namedArguments">Named arguments or <c>null</c> if none</param>
|
||||||
|
/// <param name="caBlobOffset">Original custom attribute #Blob offset or 0</param>
|
||||||
|
public CustomAttribute(ICustomAttributeType ctor, IEnumerable<CAArgument> arguments, IEnumerable<CANamedArgument> namedArguments, uint caBlobOffset) {
|
||||||
|
this.ctor = ctor;
|
||||||
|
this.arguments = arguments is null ? new List<CAArgument>() : new List<CAArgument>(arguments);
|
||||||
|
this.namedArguments = namedArguments is null ? new List<CANamedArgument>() : new List<CANamedArgument>(namedArguments);
|
||||||
|
this.caBlobOffset = caBlobOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ctor">Custom attribute constructor</param>
|
||||||
|
/// <param name="arguments">Constructor arguments. The list is now owned by this instance.</param>
|
||||||
|
/// <param name="namedArguments">Named arguments. The list is now owned by this instance.</param>
|
||||||
|
/// <param name="caBlobOffset">Original custom attribute #Blob offset or 0</param>
|
||||||
|
internal CustomAttribute(ICustomAttributeType ctor, List<CAArgument> arguments, List<CANamedArgument> namedArguments, uint caBlobOffset) {
|
||||||
|
this.ctor = ctor;
|
||||||
|
this.arguments = arguments ?? new List<CAArgument>();
|
||||||
|
this.namedArguments = namedArguments ?? new List<CANamedArgument>();
|
||||||
|
this.caBlobOffset = caBlobOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the field named <paramref name="name"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of field</param>
|
||||||
|
/// <returns>A <see cref="CANamedArgument"/> instance or <c>null</c> if not found</returns>
|
||||||
|
public CANamedArgument GetField(string name) => GetNamedArgument(name, true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the field named <paramref name="name"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of field</param>
|
||||||
|
/// <returns>A <see cref="CANamedArgument"/> instance or <c>null</c> if not found</returns>
|
||||||
|
public CANamedArgument GetField(UTF8String name) => GetNamedArgument(name, true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the property named <paramref name="name"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of property</param>
|
||||||
|
/// <returns>A <see cref="CANamedArgument"/> instance or <c>null</c> if not found</returns>
|
||||||
|
public CANamedArgument GetProperty(string name) => GetNamedArgument(name, false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the property named <paramref name="name"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of property</param>
|
||||||
|
/// <returns>A <see cref="CANamedArgument"/> instance or <c>null</c> if not found</returns>
|
||||||
|
public CANamedArgument GetProperty(UTF8String name) => GetNamedArgument(name, false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the property/field named <paramref name="name"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of property/field</param>
|
||||||
|
/// <param name="isField"><c>true</c> if it's a field, <c>false</c> if it's a property</param>
|
||||||
|
/// <returns>A <see cref="CANamedArgument"/> instance or <c>null</c> if not found</returns>
|
||||||
|
public CANamedArgument GetNamedArgument(string name, bool isField) {
|
||||||
|
var namedArguments = this.namedArguments;
|
||||||
|
int count = namedArguments.Count;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
var namedArg = namedArguments[i];
|
||||||
|
if (namedArg.IsField == isField && UTF8String.ToSystemStringOrEmpty(namedArg.Name) == name)
|
||||||
|
return namedArg;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the property/field named <paramref name="name"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of property/field</param>
|
||||||
|
/// <param name="isField"><c>true</c> if it's a field, <c>false</c> if it's a property</param>
|
||||||
|
/// <returns>A <see cref="CANamedArgument"/> instance or <c>null</c> if not found</returns>
|
||||||
|
public CANamedArgument GetNamedArgument(UTF8String name, bool isField) {
|
||||||
|
var namedArguments = this.namedArguments;
|
||||||
|
int count = namedArguments.Count;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
var namedArg = namedArguments[i];
|
||||||
|
if (namedArg.IsField == isField && UTF8String.Equals(namedArg.Name, name))
|
||||||
|
return namedArg;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString() => TypeFullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A custom attribute constructor argument
|
||||||
|
/// </summary>
|
||||||
|
public struct CAArgument : ICloneable {
|
||||||
|
TypeSig type;
|
||||||
|
object value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the argument type
|
||||||
|
/// </summary>
|
||||||
|
public TypeSig Type {
|
||||||
|
readonly get => type;
|
||||||
|
set => type = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the argument value
|
||||||
|
/// </summary>
|
||||||
|
public object Value {
|
||||||
|
readonly get => value;
|
||||||
|
set => this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Argument type</param>
|
||||||
|
public CAArgument(TypeSig type) {
|
||||||
|
this.type = type;
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Argument type</param>
|
||||||
|
/// <param name="value">Argument value</param>
|
||||||
|
public CAArgument(TypeSig type, object value) {
|
||||||
|
this.type = type;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly object ICloneable.Clone() => Clone();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clones this instance and any <see cref="CAArgument"/>s and <see cref="CANamedArgument"/>s
|
||||||
|
/// referenced from this instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public readonly CAArgument Clone() {
|
||||||
|
var value = this.value;
|
||||||
|
if (value is CAArgument)
|
||||||
|
value = ((CAArgument)value).Clone();
|
||||||
|
else if (value is IList<CAArgument> args) {
|
||||||
|
var newArgs = new List<CAArgument>(args.Count);
|
||||||
|
int count = args.Count;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
var arg = args[i];
|
||||||
|
newArgs.Add(arg.Clone());
|
||||||
|
}
|
||||||
|
value = newArgs;
|
||||||
|
}
|
||||||
|
return new CAArgument(type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override readonly string ToString() => $"{value ?? "null"} ({type})";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A custom attribute field/property argument
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CANamedArgument : ICloneable {
|
||||||
|
bool isField;
|
||||||
|
TypeSig type;
|
||||||
|
UTF8String name;
|
||||||
|
CAArgument argument;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if it's a field
|
||||||
|
/// </summary>
|
||||||
|
public bool IsField {
|
||||||
|
get => isField;
|
||||||
|
set => isField = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if it's a property
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProperty {
|
||||||
|
get => !isField;
|
||||||
|
set => isField = !value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the field/property type
|
||||||
|
/// </summary>
|
||||||
|
public TypeSig Type {
|
||||||
|
get => type;
|
||||||
|
set => type = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the property/field name
|
||||||
|
/// </summary>
|
||||||
|
public UTF8String Name {
|
||||||
|
get => name;
|
||||||
|
set => name = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the argument
|
||||||
|
/// </summary>
|
||||||
|
public CAArgument Argument {
|
||||||
|
get => argument;
|
||||||
|
set => argument = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the argument type
|
||||||
|
/// </summary>
|
||||||
|
public TypeSig ArgumentType {
|
||||||
|
get => argument.Type;
|
||||||
|
set => argument.Type = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the argument value
|
||||||
|
/// </summary>
|
||||||
|
public object Value {
|
||||||
|
get => argument.Value;
|
||||||
|
set => argument.Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public CANamedArgument() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isField"><c>true</c> if field, <c>false</c> if property</param>
|
||||||
|
public CANamedArgument(bool isField) => this.isField = isField;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isField"><c>true</c> if field, <c>false</c> if property</param>
|
||||||
|
/// <param name="type">Field/property type</param>
|
||||||
|
public CANamedArgument(bool isField, TypeSig type) {
|
||||||
|
this.isField = isField;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isField"><c>true</c> if field, <c>false</c> if property</param>
|
||||||
|
/// <param name="type">Field/property type</param>
|
||||||
|
/// <param name="name">Name of field/property</param>
|
||||||
|
public CANamedArgument(bool isField, TypeSig type, UTF8String name) {
|
||||||
|
this.isField = isField;
|
||||||
|
this.type = type;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isField"><c>true</c> if field, <c>false</c> if property</param>
|
||||||
|
/// <param name="type">Field/property type</param>
|
||||||
|
/// <param name="name">Name of field/property</param>
|
||||||
|
/// <param name="argument">Field/property argument</param>
|
||||||
|
public CANamedArgument(bool isField, TypeSig type, UTF8String name, CAArgument argument) {
|
||||||
|
this.isField = isField;
|
||||||
|
this.type = type;
|
||||||
|
this.name = name;
|
||||||
|
this.argument = argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
object ICloneable.Clone() => Clone();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clones this instance and any <see cref="CAArgument"/>s referenced from this instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public CANamedArgument Clone() => new CANamedArgument(isField, type, name, argument.Clone());
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString() => $"({(isField ? "field" : "property")}) {type} {name} = {Value ?? "null"} ({ArgumentType})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using dnlib.Utils;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Stores <see cref="CustomAttribute"/>s
|
||||||
|
/// </summary>
|
||||||
|
public class CustomAttributeCollection : LazyList<CustomAttribute, object> {
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public CustomAttributeCollection() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">Initial length of the list</param>
|
||||||
|
/// <param name="context">Context passed to <paramref name="readOriginalValue"/></param>
|
||||||
|
/// <param name="readOriginalValue">Delegate instance that returns original values</param>
|
||||||
|
public CustomAttributeCollection(int length, object context, Func<object, int, CustomAttribute> readOriginalValue)
|
||||||
|
: base(length, context, readOriginalValue) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether a custom attribute is present
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fullName">Full name of custom attribute type</param>
|
||||||
|
/// <returns><c>true</c> if the custom attribute type is present, <c>false</c> otherwise</returns>
|
||||||
|
public bool IsDefined(string fullName) => Find(fullName) is not null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all custom attributes of a certain type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fullName">Full name of custom attribute type that should be removed</param>
|
||||||
|
public void RemoveAll(string fullName) {
|
||||||
|
if (fullName == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = Count - 1; i >= 0; i--) {
|
||||||
|
var ca = this[i];
|
||||||
|
if (ca is not null && fullName.EndsWith(ca.TypeName, StringComparison.Ordinal) && ca.TypeFullName == fullName)
|
||||||
|
RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a custom attribute
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fullName">Full name of custom attribute type</param>
|
||||||
|
/// <returns>A <see cref="CustomAttribute"/> or <c>null</c> if it wasn't found</returns>
|
||||||
|
public CustomAttribute Find(string fullName) {
|
||||||
|
if (fullName == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
foreach (var ca in this) {
|
||||||
|
if (ca is not null && fullName.EndsWith(ca.TypeName, StringComparison.Ordinal) && ca.TypeFullName == fullName)
|
||||||
|
return ca;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all custom attributes of a certain type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fullName">Full name of custom attribute type</param>
|
||||||
|
/// <returns>All <see cref="CustomAttribute"/>s of the requested type</returns>
|
||||||
|
public IEnumerable<CustomAttribute> FindAll(string fullName) {
|
||||||
|
if (fullName == null)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
foreach (var ca in this) {
|
||||||
|
if (ca is not null && fullName.EndsWith(ca.TypeName, StringComparison.Ordinal) && ca.TypeFullName == fullName)
|
||||||
|
yield return ca;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a custom attribute
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="attrType">Custom attribute type</param>
|
||||||
|
/// <returns>The first <see cref="CustomAttribute"/> found or <c>null</c> if none found</returns>
|
||||||
|
public CustomAttribute Find(IType attrType) => Find(attrType, 0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a custom attribute
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="attrType">Custom attribute type</param>
|
||||||
|
/// <param name="options">Attribute type comparison flags</param>
|
||||||
|
/// <returns>The first <see cref="CustomAttribute"/> found or <c>null</c> if none found</returns>
|
||||||
|
public CustomAttribute Find(IType attrType, SigComparerOptions options) {
|
||||||
|
var comparer = new SigComparer(options);
|
||||||
|
foreach (var ca in this) {
|
||||||
|
if (comparer.Equals(ca.AttributeType, attrType))
|
||||||
|
return ca;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all custom attributes of a certain type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="attrType">Custom attribute type</param>
|
||||||
|
/// <returns>All <see cref="CustomAttribute"/>s of the requested type</returns>
|
||||||
|
public IEnumerable<CustomAttribute> FindAll(IType attrType) => FindAll(attrType, 0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all custom attributes of a certain type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="attrType">Custom attribute type</param>
|
||||||
|
/// <param name="options">Attribute type comparison flags</param>
|
||||||
|
/// <returns>All <see cref="CustomAttribute"/>s of the requested type</returns>
|
||||||
|
public IEnumerable<CustomAttribute> FindAll(IType attrType, SigComparerOptions options) {
|
||||||
|
var comparer = new SigComparer(options);
|
||||||
|
foreach (var ca in this) {
|
||||||
|
if (comparer.Equals(ca.AttributeType, attrType))
|
||||||
|
yield return ca;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,599 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using dnlib.IO;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Searches for a type according to custom attribute search rules: first try the
|
||||||
|
/// current assembly, and if that fails, try mscorlib
|
||||||
|
/// </summary>
|
||||||
|
sealed class CAAssemblyRefFinder : IAssemblyRefFinder {
|
||||||
|
readonly ModuleDef module;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">The module to search first</param>
|
||||||
|
public CAAssemblyRefFinder(ModuleDef module) => this.module = module;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public AssemblyRef FindAssemblyRef(TypeRef nonNestedTypeRef) {
|
||||||
|
var modAsm = module.Assembly;
|
||||||
|
if (modAsm is not null) {
|
||||||
|
var type = modAsm.Find(nonNestedTypeRef);
|
||||||
|
// If the user added a new type with the same name as a corelib type, don't return it,
|
||||||
|
// only return the type if it is this module's original type.
|
||||||
|
if (type is TypeDefMD td && td.ReaderModule == module)
|
||||||
|
return module.UpdateRowId(new AssemblyRefUser(modAsm));
|
||||||
|
}
|
||||||
|
else if (module.Find(nonNestedTypeRef) is not null)
|
||||||
|
return AssemblyRef.CurrentAssembly;
|
||||||
|
|
||||||
|
var corLibAsm = module.Context.AssemblyResolver.Resolve(module.CorLibTypes.AssemblyRef, module);
|
||||||
|
if (corLibAsm is not null) {
|
||||||
|
var type = corLibAsm.Find(nonNestedTypeRef);
|
||||||
|
if (type is not null)
|
||||||
|
return module.CorLibTypes.AssemblyRef;
|
||||||
|
}
|
||||||
|
if (nonNestedTypeRef.Namespace == "System" || nonNestedTypeRef.Namespace.StartsWith("System."))
|
||||||
|
return module.CorLibTypes.AssemblyRef;
|
||||||
|
|
||||||
|
if (modAsm is not null)
|
||||||
|
return module.UpdateRowId(new AssemblyRefUser(modAsm));
|
||||||
|
return AssemblyRef.CurrentAssembly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Thrown by CustomAttributeReader when it fails to parse a custom attribute blob
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public class CABlobParserException : Exception {
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public CABlobParserException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">Error message</param>
|
||||||
|
public CABlobParserException(string message)
|
||||||
|
: base(message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">Error message</param>
|
||||||
|
/// <param name="innerException">Other exception</param>
|
||||||
|
public CABlobParserException(string message, Exception innerException)
|
||||||
|
: base(message, innerException) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info"></param>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
protected CABlobParserException(SerializationInfo info, StreamingContext context)
|
||||||
|
: base(info, context) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads custom attributes from the #Blob stream
|
||||||
|
/// </summary>
|
||||||
|
public struct CustomAttributeReader {
|
||||||
|
readonly ModuleDef module;
|
||||||
|
DataReader reader;
|
||||||
|
readonly uint caBlobOffset;
|
||||||
|
readonly GenericParamContext gpContext;
|
||||||
|
GenericArguments genericArguments;
|
||||||
|
RecursionCounter recursionCounter;
|
||||||
|
bool verifyReadAllBytes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a custom attribute
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readerModule">Reader module</param>
|
||||||
|
/// <param name="ctor">Custom attribute constructor</param>
|
||||||
|
/// <param name="offset">Offset of custom attribute in the #Blob stream</param>
|
||||||
|
/// <returns>A new <see cref="CustomAttribute"/> instance</returns>
|
||||||
|
public static CustomAttribute Read(ModuleDefMD readerModule, ICustomAttributeType ctor, uint offset) => Read(readerModule, ctor, offset, new GenericParamContext());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a custom attribute
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readerModule">Reader module</param>
|
||||||
|
/// <param name="ctor">Custom attribute constructor</param>
|
||||||
|
/// <param name="offset">Offset of custom attribute in the #Blob stream</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
/// <returns>A new <see cref="CustomAttribute"/> instance</returns>
|
||||||
|
public static CustomAttribute Read(ModuleDefMD readerModule, ICustomAttributeType ctor, uint offset, GenericParamContext gpContext) {
|
||||||
|
var caReader = new CustomAttributeReader(readerModule, offset, gpContext);
|
||||||
|
try {
|
||||||
|
if (ctor is null)
|
||||||
|
return caReader.CreateRaw(ctor);
|
||||||
|
return caReader.Read(ctor);
|
||||||
|
}
|
||||||
|
catch (CABlobParserException) {
|
||||||
|
return caReader.CreateRaw(ctor);
|
||||||
|
}
|
||||||
|
catch (IOException) {
|
||||||
|
return caReader.CreateRaw(ctor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomAttribute CreateRaw(ICustomAttributeType ctor) => new CustomAttribute(ctor, GetRawBlob());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a custom attribute
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Owner module</param>
|
||||||
|
/// <param name="caBlob">CA blob</param>
|
||||||
|
/// <param name="ctor">Custom attribute constructor</param>
|
||||||
|
/// <returns>A new <see cref="CustomAttribute"/> instance</returns>
|
||||||
|
public static CustomAttribute Read(ModuleDef module, byte[] caBlob, ICustomAttributeType ctor) =>
|
||||||
|
Read(module, ByteArrayDataReaderFactory.CreateReader(caBlob), ctor, new GenericParamContext());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a custom attribute
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Owner module</param>
|
||||||
|
/// <param name="reader">A reader positioned at the the first byte of the CA blob</param>
|
||||||
|
/// <param name="ctor">Custom attribute constructor</param>
|
||||||
|
/// <returns>A new <see cref="CustomAttribute"/> instance</returns>
|
||||||
|
public static CustomAttribute Read(ModuleDef module, DataReader reader, ICustomAttributeType ctor) =>
|
||||||
|
Read(module, ref reader, ctor, new GenericParamContext());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a custom attribute
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Owner module</param>
|
||||||
|
/// <param name="caBlob">CA blob</param>
|
||||||
|
/// <param name="ctor">Custom attribute constructor</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
/// <returns>A new <see cref="CustomAttribute"/> instance</returns>
|
||||||
|
public static CustomAttribute Read(ModuleDef module, byte[] caBlob, ICustomAttributeType ctor, GenericParamContext gpContext) =>
|
||||||
|
Read(module, ByteArrayDataReaderFactory.CreateReader(caBlob), ctor, gpContext);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a custom attribute
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Owner module</param>
|
||||||
|
/// <param name="reader">A stream positioned at the the first byte of the CA blob</param>
|
||||||
|
/// <param name="ctor">Custom attribute constructor</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
/// <returns>A new <see cref="CustomAttribute"/> instance</returns>
|
||||||
|
public static CustomAttribute Read(ModuleDef module, DataReader reader, ICustomAttributeType ctor, GenericParamContext gpContext) =>
|
||||||
|
Read(module, ref reader, ctor, gpContext);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a custom attribute
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Owner module</param>
|
||||||
|
/// <param name="reader">A stream positioned at the the first byte of the CA blob</param>
|
||||||
|
/// <param name="ctor">Custom attribute constructor</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
/// <returns>A new <see cref="CustomAttribute"/> instance</returns>
|
||||||
|
static CustomAttribute Read(ModuleDef module, ref DataReader reader, ICustomAttributeType ctor, GenericParamContext gpContext) {
|
||||||
|
var caReader = new CustomAttributeReader(module, ref reader, gpContext);
|
||||||
|
CustomAttribute ca;
|
||||||
|
try {
|
||||||
|
if (ctor is null)
|
||||||
|
ca = caReader.CreateRaw(ctor);
|
||||||
|
else
|
||||||
|
ca = caReader.Read(ctor);
|
||||||
|
}
|
||||||
|
catch (CABlobParserException) {
|
||||||
|
ca = caReader.CreateRaw(ctor);
|
||||||
|
}
|
||||||
|
catch (IOException) {
|
||||||
|
ca = caReader.CreateRaw(ctor);
|
||||||
|
}
|
||||||
|
return ca;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads custom attribute named arguments
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Owner module</param>
|
||||||
|
/// <param name="reader">A reader positioned at the the first byte of the CA blob</param>
|
||||||
|
/// <param name="numNamedArgs">Number of named arguments to read from <paramref name="reader"/></param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
/// <returns>A list of <see cref="CANamedArgument"/>s or <c>null</c> if some error
|
||||||
|
/// occurred.</returns>
|
||||||
|
internal static List<CANamedArgument> ReadNamedArguments(ModuleDef module, ref DataReader reader, int numNamedArgs, GenericParamContext gpContext) {
|
||||||
|
try {
|
||||||
|
var caReader = new CustomAttributeReader(module, ref reader, gpContext);
|
||||||
|
var namedArgs = caReader.ReadNamedArguments(numNamedArgs);
|
||||||
|
reader.CurrentOffset = caReader.reader.CurrentOffset;
|
||||||
|
return namedArgs;
|
||||||
|
}
|
||||||
|
catch (CABlobParserException) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (IOException) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomAttributeReader(ModuleDefMD readerModule, uint offset, GenericParamContext gpContext) {
|
||||||
|
module = readerModule;
|
||||||
|
caBlobOffset = offset;
|
||||||
|
reader = readerModule.BlobStream.CreateReader(offset);
|
||||||
|
genericArguments = null;
|
||||||
|
recursionCounter = new RecursionCounter();
|
||||||
|
verifyReadAllBytes = false;
|
||||||
|
this.gpContext = gpContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomAttributeReader(ModuleDef module, ref DataReader reader, GenericParamContext gpContext) {
|
||||||
|
this.module = module;
|
||||||
|
caBlobOffset = 0;
|
||||||
|
this.reader = reader;
|
||||||
|
genericArguments = null;
|
||||||
|
recursionCounter = new RecursionCounter();
|
||||||
|
verifyReadAllBytes = false;
|
||||||
|
this.gpContext = gpContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] GetRawBlob() => reader.ToArray();
|
||||||
|
|
||||||
|
CustomAttribute Read(ICustomAttributeType ctor) {
|
||||||
|
var methodSig = ctor?.MethodSig;
|
||||||
|
if (methodSig is null)
|
||||||
|
throw new CABlobParserException("ctor is null or not a method");
|
||||||
|
|
||||||
|
if (ctor is MemberRef mrCtor && mrCtor.Class is TypeSpec owner && owner.TypeSig is GenericInstSig gis) {
|
||||||
|
genericArguments = new GenericArguments();
|
||||||
|
genericArguments.PushTypeArgs(gis.GenericArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
var methodSigParams = methodSig.Params;
|
||||||
|
bool isEmpty = methodSigParams.Count == 0 && reader.Position == reader.Length;
|
||||||
|
if (!isEmpty && reader.ReadUInt16() != 1)
|
||||||
|
throw new CABlobParserException("Invalid CA blob prolog");
|
||||||
|
|
||||||
|
var ctorArgs = new List<CAArgument>(methodSigParams.Count);
|
||||||
|
int count = methodSigParams.Count;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
ctorArgs.Add(ReadFixedArg(FixTypeSig(methodSigParams[i])));
|
||||||
|
|
||||||
|
// Some tools don't write the next ushort if there are no named arguments.
|
||||||
|
int numNamedArgs = reader.Position == reader.Length ? 0 : reader.ReadUInt16();
|
||||||
|
var namedArgs = ReadNamedArguments(numNamedArgs);
|
||||||
|
|
||||||
|
// verifyReadAllBytes will be set when we guess the underlying type of an enum.
|
||||||
|
// To make sure we guessed right, verify that we read all bytes.
|
||||||
|
if (verifyReadAllBytes && reader.Position != reader.Length)
|
||||||
|
throw new CABlobParserException("Not all CA blob bytes were read");
|
||||||
|
|
||||||
|
return new CustomAttribute(ctor, ctorArgs, namedArgs, caBlobOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CANamedArgument> ReadNamedArguments(int numNamedArgs) {
|
||||||
|
if ((uint)numNamedArgs >= 0x4000_0000 || numNamedArgs * 4 > reader.BytesLeft)
|
||||||
|
return null;
|
||||||
|
var namedArgs = new List<CANamedArgument>(numNamedArgs);
|
||||||
|
for (int i = 0; i < numNamedArgs; i++) {
|
||||||
|
if (reader.Position == reader.Length)
|
||||||
|
break;
|
||||||
|
namedArgs.Add(ReadNamedArgument());
|
||||||
|
}
|
||||||
|
return namedArgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeSig FixTypeSig(TypeSig type) => SubstituteGenericParameter(type.RemoveModifiers()).RemoveModifiers();
|
||||||
|
|
||||||
|
TypeSig SubstituteGenericParameter(TypeSig type) {
|
||||||
|
if (genericArguments is null)
|
||||||
|
return type;
|
||||||
|
return genericArguments.Resolve(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
CAArgument ReadFixedArg(TypeSig argType) {
|
||||||
|
if (!recursionCounter.Increment())
|
||||||
|
throw new CABlobParserException("Too much recursion");
|
||||||
|
if (argType is null)
|
||||||
|
throw new CABlobParserException("null argType");
|
||||||
|
CAArgument result;
|
||||||
|
|
||||||
|
if (argType is SZArraySig arrayType)
|
||||||
|
result = ReadArrayArgument(arrayType);
|
||||||
|
else
|
||||||
|
result = ReadElem(argType);
|
||||||
|
|
||||||
|
recursionCounter.Decrement();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAArgument ReadElem(TypeSig argType) {
|
||||||
|
if (argType is null)
|
||||||
|
throw new CABlobParserException("null argType");
|
||||||
|
var value = ReadValue((SerializationType)argType.ElementType, argType, out var realArgType);
|
||||||
|
if (realArgType is null)
|
||||||
|
throw new CABlobParserException("Invalid arg type");
|
||||||
|
|
||||||
|
// One example when this is true is when prop/field type is object and
|
||||||
|
// value type is string[]
|
||||||
|
if (value is CAArgument)
|
||||||
|
return (CAArgument)value;
|
||||||
|
|
||||||
|
return new CAArgument(realArgType, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
object ReadValue(SerializationType etype, TypeSig argType, out TypeSig realArgType) {
|
||||||
|
if (!recursionCounter.Increment())
|
||||||
|
throw new CABlobParserException("Too much recursion");
|
||||||
|
|
||||||
|
object result;
|
||||||
|
switch (etype) {
|
||||||
|
case SerializationType.Boolean:
|
||||||
|
realArgType = module.CorLibTypes.Boolean;
|
||||||
|
result = reader.ReadByte() != 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SerializationType.Char:
|
||||||
|
realArgType = module.CorLibTypes.Char;
|
||||||
|
result = reader.ReadChar();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SerializationType.I1:
|
||||||
|
realArgType = module.CorLibTypes.SByte;
|
||||||
|
result = reader.ReadSByte();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SerializationType.U1:
|
||||||
|
realArgType = module.CorLibTypes.Byte;
|
||||||
|
result = reader.ReadByte();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SerializationType.I2:
|
||||||
|
realArgType = module.CorLibTypes.Int16;
|
||||||
|
result = reader.ReadInt16();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SerializationType.U2:
|
||||||
|
realArgType = module.CorLibTypes.UInt16;
|
||||||
|
result = reader.ReadUInt16();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SerializationType.I4:
|
||||||
|
realArgType = module.CorLibTypes.Int32;
|
||||||
|
result = reader.ReadInt32();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SerializationType.U4:
|
||||||
|
realArgType = module.CorLibTypes.UInt32;
|
||||||
|
result = reader.ReadUInt32();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SerializationType.I8:
|
||||||
|
realArgType = module.CorLibTypes.Int64;
|
||||||
|
result = reader.ReadInt64();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SerializationType.U8:
|
||||||
|
realArgType = module.CorLibTypes.UInt64;
|
||||||
|
result = reader.ReadUInt64();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SerializationType.R4:
|
||||||
|
realArgType = module.CorLibTypes.Single;
|
||||||
|
result = reader.ReadSingle();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SerializationType.R8:
|
||||||
|
realArgType = module.CorLibTypes.Double;
|
||||||
|
result = reader.ReadDouble();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SerializationType.String:
|
||||||
|
realArgType = module.CorLibTypes.String;
|
||||||
|
result = ReadUTF8String();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// It's ET.ValueType if it's eg. a ctor enum arg type
|
||||||
|
case (SerializationType)ElementType.ValueType:
|
||||||
|
if (argType is null)
|
||||||
|
throw new CABlobParserException("Invalid element type");
|
||||||
|
realArgType = argType;
|
||||||
|
result = ReadEnumValue(GetEnumUnderlyingType(argType));
|
||||||
|
break;
|
||||||
|
|
||||||
|
// It's ET.Object if it's a ctor object arg type
|
||||||
|
case (SerializationType)ElementType.Object:
|
||||||
|
case SerializationType.TaggedObject:
|
||||||
|
realArgType = ReadFieldOrPropType();
|
||||||
|
var arraySig = realArgType as SZArraySig;
|
||||||
|
if (arraySig is not null)
|
||||||
|
result = ReadArrayArgument(arraySig);
|
||||||
|
else
|
||||||
|
result = ReadValue((SerializationType)realArgType.ElementType, realArgType, out var tmpType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// It's ET.Class if it's eg. a ctor System.Type arg type
|
||||||
|
case (SerializationType)ElementType.Class:
|
||||||
|
var tdr = argType as TypeDefOrRefSig;
|
||||||
|
if (tdr is not null && tdr.DefinitionAssembly.IsCorLib() && tdr.Namespace == "System") {
|
||||||
|
if (tdr.TypeName == "Type") {
|
||||||
|
result = ReadValue(SerializationType.Type, tdr, out realArgType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (tdr.TypeName == "String") {
|
||||||
|
result = ReadValue(SerializationType.String, tdr, out realArgType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (tdr.TypeName == "Object") {
|
||||||
|
result = ReadValue(SerializationType.TaggedObject, tdr, out realArgType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assume it's an enum that couldn't be resolved
|
||||||
|
realArgType = argType;
|
||||||
|
result = ReadEnumValue(null);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SerializationType.Type:
|
||||||
|
realArgType = argType;
|
||||||
|
result = ReadType(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SerializationType.Enum:
|
||||||
|
realArgType = ReadType(false);
|
||||||
|
result = ReadEnumValue(GetEnumUnderlyingType(realArgType));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new CABlobParserException("Invalid element type");
|
||||||
|
}
|
||||||
|
|
||||||
|
recursionCounter.Decrement();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
object ReadEnumValue(TypeSig underlyingType) {
|
||||||
|
if (underlyingType is not null) {
|
||||||
|
if (underlyingType.ElementType < ElementType.Boolean || underlyingType.ElementType > ElementType.U8)
|
||||||
|
throw new CABlobParserException("Invalid enum underlying type");
|
||||||
|
return ReadValue((SerializationType)underlyingType.ElementType, underlyingType, out var realArgType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We couldn't resolve the type ref. It should be an enum, but we don't know for sure.
|
||||||
|
// Most enums use Int32 as the underlying type. Assume that's true also in this case.
|
||||||
|
// Since we're guessing, verify that we've read all CA blob bytes. If we haven't, then
|
||||||
|
// we probably guessed wrong.
|
||||||
|
verifyReadAllBytes = true;
|
||||||
|
return reader.ReadInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeSig ReadType(bool canReturnNull) {
|
||||||
|
var name = ReadUTF8String();
|
||||||
|
if (canReturnNull && name is null)
|
||||||
|
return null;
|
||||||
|
var asmRefFinder = new CAAssemblyRefFinder(module);
|
||||||
|
var type = TypeNameParser.ParseAsTypeSigReflection(module, UTF8String.ToSystemStringOrEmpty(name), asmRefFinder, gpContext);
|
||||||
|
if (type is null)
|
||||||
|
throw new CABlobParserException("Could not parse type");
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the enum's underlying type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">An enum type</param>
|
||||||
|
/// <returns>The underlying type or <c>null</c> if we couldn't resolve the type ref</returns>
|
||||||
|
/// <exception cref="CABlobParserException">If <paramref name="type"/> is not an enum or <c>null</c></exception>
|
||||||
|
static TypeSig GetEnumUnderlyingType(TypeSig type) {
|
||||||
|
if (type is null)
|
||||||
|
throw new CABlobParserException("null enum type");
|
||||||
|
var td = GetTypeDef(type);
|
||||||
|
if (td is null)
|
||||||
|
return null;
|
||||||
|
if (!td.IsEnum)
|
||||||
|
throw new CABlobParserException("Not an enum");
|
||||||
|
return td.GetEnumUnderlyingType().RemoveModifiers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts <paramref name="type"/> to a <see cref="TypeDef"/>, possibly resolving
|
||||||
|
/// a <see cref="TypeRef"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type</param>
|
||||||
|
/// <returns>A <see cref="TypeDef"/> or <c>null</c> if we couldn't resolve the
|
||||||
|
/// <see cref="TypeRef"/> or if <paramref name="type"/> is a type spec</returns>
|
||||||
|
static TypeDef GetTypeDef(TypeSig type) {
|
||||||
|
if (type is TypeDefOrRefSig tdr) {
|
||||||
|
var td = tdr.TypeDef;
|
||||||
|
if (td is not null)
|
||||||
|
return td;
|
||||||
|
|
||||||
|
var tr = tdr.TypeRef;
|
||||||
|
if (tr is not null)
|
||||||
|
return tr.Resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAArgument ReadArrayArgument(SZArraySig arrayType) {
|
||||||
|
if (!recursionCounter.Increment())
|
||||||
|
throw new CABlobParserException("Too much recursion");
|
||||||
|
var arg = new CAArgument(arrayType);
|
||||||
|
|
||||||
|
int arrayCount = reader.ReadInt32();
|
||||||
|
if (arrayCount == -1) { // -1 if it's null
|
||||||
|
}
|
||||||
|
else if (arrayCount < 0 || arrayCount > reader.BytesLeft)
|
||||||
|
throw new CABlobParserException("Array is too big");
|
||||||
|
else {
|
||||||
|
var array = new List<CAArgument>(arrayCount);
|
||||||
|
arg.Value = array;
|
||||||
|
for (int i = 0; i < arrayCount; i++)
|
||||||
|
array.Add(ReadFixedArg(FixTypeSig(arrayType.Next)));
|
||||||
|
}
|
||||||
|
|
||||||
|
recursionCounter.Decrement();
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
CANamedArgument ReadNamedArgument() {
|
||||||
|
var isField = (SerializationType)reader.ReadByte() switch {
|
||||||
|
SerializationType.Property => false,
|
||||||
|
SerializationType.Field => true,
|
||||||
|
_ => throw new CABlobParserException("Named argument is not a field/property"),
|
||||||
|
};
|
||||||
|
var fieldPropType = ReadFieldOrPropType();
|
||||||
|
var name = ReadUTF8String();
|
||||||
|
var argument = ReadFixedArg(fieldPropType);
|
||||||
|
|
||||||
|
return new CANamedArgument(isField, fieldPropType, name, argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeSig ReadFieldOrPropType() {
|
||||||
|
if (!recursionCounter.Increment())
|
||||||
|
throw new CABlobParserException("Too much recursion");
|
||||||
|
var result = (SerializationType)reader.ReadByte() switch {
|
||||||
|
SerializationType.Boolean => module.CorLibTypes.Boolean,
|
||||||
|
SerializationType.Char => module.CorLibTypes.Char,
|
||||||
|
SerializationType.I1 => module.CorLibTypes.SByte,
|
||||||
|
SerializationType.U1 => module.CorLibTypes.Byte,
|
||||||
|
SerializationType.I2 => module.CorLibTypes.Int16,
|
||||||
|
SerializationType.U2 => module.CorLibTypes.UInt16,
|
||||||
|
SerializationType.I4 => module.CorLibTypes.Int32,
|
||||||
|
SerializationType.U4 => module.CorLibTypes.UInt32,
|
||||||
|
SerializationType.I8 => module.CorLibTypes.Int64,
|
||||||
|
SerializationType.U8 => module.CorLibTypes.UInt64,
|
||||||
|
SerializationType.R4 => module.CorLibTypes.Single,
|
||||||
|
SerializationType.R8 => module.CorLibTypes.Double,
|
||||||
|
SerializationType.String => module.CorLibTypes.String,
|
||||||
|
SerializationType.SZArray => new SZArraySig(ReadFieldOrPropType()),
|
||||||
|
SerializationType.Type => new ClassSig(module.CorLibTypes.GetTypeRef("System", "Type")),
|
||||||
|
SerializationType.TaggedObject => module.CorLibTypes.Object,
|
||||||
|
SerializationType.Enum => ReadType(false),
|
||||||
|
_ => throw new CABlobParserException("Invalid type"),
|
||||||
|
};
|
||||||
|
recursionCounter.Decrement();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTF8String ReadUTF8String() {
|
||||||
|
if (reader.ReadByte() == 0xFF)
|
||||||
|
return null;
|
||||||
|
reader.Position--;
|
||||||
|
if (!reader.TryReadCompressedUInt32(out uint len))
|
||||||
|
throw new CABlobParserException("Could not read compressed UInt32");
|
||||||
|
if (len == 0)
|
||||||
|
return UTF8String.Empty;
|
||||||
|
return new UTF8String(reader.ReadBytes((int)len));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,227 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
using dnlib.DotNet.MD;
|
||||||
|
using dnlib.DotNet.Pdb;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// A high-level representation of a row in the DeclSecurity table
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("{Action} Count={SecurityAttributes.Count}")]
|
||||||
|
public abstract class DeclSecurity : IHasCustomAttribute, IHasCustomDebugInformation {
|
||||||
|
/// <summary>
|
||||||
|
/// The row id in its table
|
||||||
|
/// </summary>
|
||||||
|
protected uint rid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public MDToken MDToken => new MDToken(Table.DeclSecurity, rid);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint Rid {
|
||||||
|
get => rid;
|
||||||
|
set => rid = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomAttributeTag => 8;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column DeclSecurity.Action
|
||||||
|
/// </summary>
|
||||||
|
public SecurityAction Action {
|
||||||
|
get => action;
|
||||||
|
set => action = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected SecurityAction action;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column DeclSecurity.PermissionSet
|
||||||
|
/// </summary>
|
||||||
|
public IList<SecurityAttribute> SecurityAttributes {
|
||||||
|
get {
|
||||||
|
if (securityAttributes is null)
|
||||||
|
InitializeSecurityAttributes();
|
||||||
|
return securityAttributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected IList<SecurityAttribute> securityAttributes;
|
||||||
|
/// <summary>Initializes <see cref="securityAttributes"/></summary>
|
||||||
|
protected virtual void InitializeSecurityAttributes() =>
|
||||||
|
Interlocked.CompareExchange(ref securityAttributes, new List<SecurityAttribute>(), null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom attributes
|
||||||
|
/// </summary>
|
||||||
|
public CustomAttributeCollection CustomAttributes {
|
||||||
|
get {
|
||||||
|
if (customAttributes is null)
|
||||||
|
InitializeCustomAttributes();
|
||||||
|
return customAttributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected CustomAttributeCollection customAttributes;
|
||||||
|
/// <summary>Initializes <see cref="customAttributes"/></summary>
|
||||||
|
protected virtual void InitializeCustomAttributes() =>
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomAttributes => CustomAttributes.Count > 0;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomDebugInformationTag => 8;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom debug infos
|
||||||
|
/// </summary>
|
||||||
|
public IList<PdbCustomDebugInfo> CustomDebugInfos {
|
||||||
|
get {
|
||||||
|
if (customDebugInfos is null)
|
||||||
|
InitializeCustomDebugInfos();
|
||||||
|
return customDebugInfos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected IList<PdbCustomDebugInfo> customDebugInfos;
|
||||||
|
/// <summary>Initializes <see cref="customDebugInfos"/></summary>
|
||||||
|
protected virtual void InitializeCustomDebugInfos() =>
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, new List<PdbCustomDebugInfo>(), null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="SecurityAttributes"/> is not empty
|
||||||
|
/// </summary>
|
||||||
|
public bool HasSecurityAttributes => SecurityAttributes.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the blob data or <c>null</c> if there's none
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Blob data or <c>null</c></returns>
|
||||||
|
public abstract byte[] GetBlob();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the .NET Framework 1.x XML string or null if it's not a .NET Framework 1.x format
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string GetNet1xXmlString() => GetNet1xXmlStringInternal(SecurityAttributes);
|
||||||
|
|
||||||
|
internal static string GetNet1xXmlStringInternal(IList<SecurityAttribute> secAttrs) {
|
||||||
|
if (secAttrs is null || secAttrs.Count != 1)
|
||||||
|
return null;
|
||||||
|
var sa = secAttrs[0];
|
||||||
|
if (sa is null || sa.TypeFullName != "System.Security.Permissions.PermissionSetAttribute")
|
||||||
|
return null;
|
||||||
|
if (sa.NamedArguments.Count != 1)
|
||||||
|
return null;
|
||||||
|
var na = sa.NamedArguments[0];
|
||||||
|
if (na is null || !na.IsProperty || na.Name != "XML")
|
||||||
|
return null;
|
||||||
|
if (na.ArgumentType.GetElementType() != ElementType.String)
|
||||||
|
return null;
|
||||||
|
var arg = na.Argument;
|
||||||
|
if (arg.Type.GetElementType() != ElementType.String)
|
||||||
|
return null;
|
||||||
|
var utf8 = arg.Value as UTF8String;
|
||||||
|
if (utf8 is not null)
|
||||||
|
return utf8;
|
||||||
|
if (arg.Value is string s)
|
||||||
|
return s;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A DeclSecurity row created by the user and not present in the original .NET file
|
||||||
|
/// </summary>
|
||||||
|
public class DeclSecurityUser : DeclSecurity {
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public DeclSecurityUser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action">The security action</param>
|
||||||
|
/// <param name="securityAttrs">The security attributes (now owned by this)</param>
|
||||||
|
public DeclSecurityUser(SecurityAction action, IList<SecurityAttribute> securityAttrs) {
|
||||||
|
this.action = action;
|
||||||
|
securityAttributes = securityAttrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override byte[] GetBlob() => null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Created from a row in the DeclSecurity table
|
||||||
|
/// </summary>
|
||||||
|
sealed class DeclSecurityMD : DeclSecurity, IMDTokenProviderMD {
|
||||||
|
/// <summary>The module where this instance is located</summary>
|
||||||
|
readonly ModuleDefMD readerModule;
|
||||||
|
|
||||||
|
readonly uint origRid;
|
||||||
|
readonly uint permissionSet;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint OrigRid => origRid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeSecurityAttributes() {
|
||||||
|
var gpContext = new GenericParamContext();
|
||||||
|
var tmp = DeclSecurityReader.Read(readerModule, permissionSet, gpContext);
|
||||||
|
Interlocked.CompareExchange(ref securityAttributes, tmp, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomAttributes() {
|
||||||
|
var list = readerModule.Metadata.GetCustomAttributeRidList(Table.DeclSecurity, origRid);
|
||||||
|
var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index]));
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, tmp, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomDebugInfos() {
|
||||||
|
var list = new List<PdbCustomDebugInfo>();
|
||||||
|
var gpContext = new GenericParamContext();
|
||||||
|
readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), gpContext, list);
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, list, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readerModule">The module which contains this <c>DeclSecurity</c> row</param>
|
||||||
|
/// <param name="rid">Row ID</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If <paramref name="readerModule"/> is <c>null</c></exception>
|
||||||
|
/// <exception cref="ArgumentException">If <paramref name="rid"/> is invalid</exception>
|
||||||
|
public DeclSecurityMD(ModuleDefMD readerModule, uint rid) {
|
||||||
|
#if DEBUG
|
||||||
|
if (readerModule is null)
|
||||||
|
throw new ArgumentNullException("readerModule");
|
||||||
|
if (readerModule.TablesStream.DeclSecurityTable.IsInvalidRID(rid))
|
||||||
|
throw new BadImageFormatException($"DeclSecurity rid {rid} does not exist");
|
||||||
|
#endif
|
||||||
|
origRid = rid;
|
||||||
|
this.rid = rid;
|
||||||
|
this.readerModule = readerModule;
|
||||||
|
bool b = readerModule.TablesStream.TryReadDeclSecurityRow(origRid, out var row);
|
||||||
|
Debug.Assert(b);
|
||||||
|
permissionSet = row.PermissionSet;
|
||||||
|
action = (SecurityAction)row.Action;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override byte[] GetBlob() => readerModule.BlobStream.Read(permissionSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using dnlib.IO;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Reads <c>DeclSecurity</c> blobs
|
||||||
|
/// </summary>
|
||||||
|
public struct DeclSecurityReader {
|
||||||
|
DataReader reader;
|
||||||
|
readonly ModuleDef module;
|
||||||
|
readonly GenericParamContext gpContext;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <c>DeclSecurity</c> blob
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Module that will own the returned list</param>
|
||||||
|
/// <param name="sig"><c>#Blob</c> offset of <c>DeclSecurity</c> signature</param>
|
||||||
|
/// <returns>A list of <see cref="SecurityAttribute"/>s</returns>
|
||||||
|
public static IList<SecurityAttribute> Read(ModuleDefMD module, uint sig) => Read(module, module.BlobStream.CreateReader(sig), new GenericParamContext());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <c>DeclSecurity</c> blob
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Module that will own the returned list</param>
|
||||||
|
/// <param name="sig"><c>#Blob</c> offset of <c>DeclSecurity</c> signature</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
/// <returns>A list of <see cref="SecurityAttribute"/>s</returns>
|
||||||
|
public static IList<SecurityAttribute> Read(ModuleDefMD module, uint sig, GenericParamContext gpContext) => Read(module, module.BlobStream.CreateReader(sig), gpContext);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <c>DeclSecurity</c> blob
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Module that will own the returned list</param>
|
||||||
|
/// <param name="blob"><c>DeclSecurity</c> blob</param>
|
||||||
|
/// <returns>A list of <see cref="SecurityAttribute"/>s</returns>
|
||||||
|
public static IList<SecurityAttribute> Read(ModuleDef module, byte[] blob) => Read(module, ByteArrayDataReaderFactory.CreateReader(blob), new GenericParamContext());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <c>DeclSecurity</c> blob
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Module that will own the returned list</param>
|
||||||
|
/// <param name="blob"><c>DeclSecurity</c> blob</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>///
|
||||||
|
/// <returns>A list of <see cref="SecurityAttribute"/>s</returns>
|
||||||
|
public static IList<SecurityAttribute> Read(ModuleDef module, byte[] blob, GenericParamContext gpContext) => Read(module, ByteArrayDataReaderFactory.CreateReader(blob), gpContext);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <c>DeclSecurity</c> blob
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Module that will own the returned list</param>
|
||||||
|
/// <param name="signature"><c>DeclSecurity</c> stream that will be owned by us</param>
|
||||||
|
/// <returns>A list of <see cref="SecurityAttribute"/>s</returns>
|
||||||
|
public static IList<SecurityAttribute> Read(ModuleDef module, DataReader signature) => Read(module, signature, new GenericParamContext());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <c>DeclSecurity</c> blob
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Module that will own the returned list</param>
|
||||||
|
/// <param name="signature"><c>DeclSecurity</c> stream that will be owned by us</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
/// <returns>A list of <see cref="SecurityAttribute"/>s</returns>
|
||||||
|
public static IList<SecurityAttribute> Read(ModuleDef module, DataReader signature, GenericParamContext gpContext) {
|
||||||
|
var reader = new DeclSecurityReader(module, signature, gpContext);
|
||||||
|
return reader.Read();
|
||||||
|
}
|
||||||
|
|
||||||
|
DeclSecurityReader(ModuleDef module, DataReader reader, GenericParamContext gpContext) {
|
||||||
|
this.reader = reader;
|
||||||
|
this.module = module;
|
||||||
|
this.gpContext = gpContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
IList<SecurityAttribute> Read() {
|
||||||
|
try {
|
||||||
|
if (reader.Position >= reader.Length)
|
||||||
|
return new List<SecurityAttribute>();
|
||||||
|
|
||||||
|
if (reader.ReadByte() == '.')
|
||||||
|
return ReadBinaryFormat();
|
||||||
|
reader.Position--;
|
||||||
|
return ReadXmlFormat();
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return new List<SecurityAttribute>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the new (.NET Framework 2.0+) DeclSecurity blob format
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
IList<SecurityAttribute> ReadBinaryFormat() {
|
||||||
|
int numAttrs = (int)reader.ReadCompressedUInt32();
|
||||||
|
var list = new List<SecurityAttribute>(numAttrs);
|
||||||
|
|
||||||
|
for (int i = 0; i < numAttrs; i++) {
|
||||||
|
var name = ReadUTF8String();
|
||||||
|
// Use CA search rules. Some tools don't write the fully qualified name.
|
||||||
|
var attrRef = TypeNameParser.ParseReflection(module, UTF8String.ToSystemStringOrEmpty(name), new CAAssemblyRefFinder(module), gpContext);
|
||||||
|
/*int blobLength = (int)*/reader.ReadCompressedUInt32();
|
||||||
|
int numNamedArgs = (int)reader.ReadCompressedUInt32();
|
||||||
|
var namedArgs = CustomAttributeReader.ReadNamedArguments(module, ref reader, numNamedArgs, gpContext);
|
||||||
|
if (namedArgs is null)
|
||||||
|
throw new ApplicationException("Could not read named arguments");
|
||||||
|
list.Add(new SecurityAttribute(attrRef, namedArgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the old (.NET Framework 1.x) DeclSecurity blob format
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
IList<SecurityAttribute> ReadXmlFormat() {
|
||||||
|
var xml = reader.ReadUtf16String((int)reader.Length / 2);
|
||||||
|
var sa = SecurityAttribute.CreateFromXml(module, xml);
|
||||||
|
return new List<SecurityAttribute> { sa };
|
||||||
|
}
|
||||||
|
|
||||||
|
UTF8String ReadUTF8String() {
|
||||||
|
uint len = reader.ReadCompressedUInt32();
|
||||||
|
return len == 0 ? UTF8String.Empty : new UTF8String(reader.ReadBytes((int)len));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,209 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// See CorHdr.h/CorElementType
|
||||||
|
/// </summary>
|
||||||
|
public enum ElementType : byte {
|
||||||
|
/// <summary/>
|
||||||
|
End = 0x00,
|
||||||
|
/// <summary>System.Void</summary>
|
||||||
|
Void = 0x01,
|
||||||
|
/// <summary>System.Boolean</summary>
|
||||||
|
Boolean = 0x02,
|
||||||
|
/// <summary>System.Char</summary>
|
||||||
|
Char = 0x03,
|
||||||
|
/// <summary>System.SByte</summary>
|
||||||
|
I1 = 0x04,
|
||||||
|
/// <summary>System.Byte</summary>
|
||||||
|
U1 = 0x05,
|
||||||
|
/// <summary>System.Int16</summary>
|
||||||
|
I2 = 0x06,
|
||||||
|
/// <summary>System.UInt16</summary>
|
||||||
|
U2 = 0x07,
|
||||||
|
/// <summary>System.Int32</summary>
|
||||||
|
I4 = 0x08,
|
||||||
|
/// <summary>System.UInt32</summary>
|
||||||
|
U4 = 0x09,
|
||||||
|
/// <summary>System.Int64</summary>
|
||||||
|
I8 = 0x0A,
|
||||||
|
/// <summary>System.UInt64</summary>
|
||||||
|
U8 = 0x0B,
|
||||||
|
/// <summary>System.Single</summary>
|
||||||
|
R4 = 0x0C,
|
||||||
|
/// <summary>System.Double</summary>
|
||||||
|
R8 = 0x0D,
|
||||||
|
/// <summary>System.String</summary>
|
||||||
|
String = 0x0E,
|
||||||
|
/// <summary>Pointer type (*)</summary>
|
||||||
|
Ptr = 0x0F,
|
||||||
|
/// <summary>ByRef type (&)</summary>
|
||||||
|
ByRef = 0x10,
|
||||||
|
/// <summary>Value type</summary>
|
||||||
|
ValueType = 0x11,
|
||||||
|
/// <summary>Reference type</summary>
|
||||||
|
Class = 0x12,
|
||||||
|
/// <summary>Type generic parameter</summary>
|
||||||
|
Var = 0x13,
|
||||||
|
/// <summary>Multidimensional array ([*], [,], [,,], ...)</summary>
|
||||||
|
Array = 0x14,
|
||||||
|
/// <summary>Generic instance type</summary>
|
||||||
|
GenericInst = 0x15,
|
||||||
|
/// <summary>Typed byref</summary>
|
||||||
|
TypedByRef = 0x16,
|
||||||
|
/// <summary>Value array (don't use)</summary>
|
||||||
|
ValueArray = 0x17,
|
||||||
|
/// <summary>System.IntPtr</summary>
|
||||||
|
I = 0x18,
|
||||||
|
/// <summary>System.UIntPtr</summary>
|
||||||
|
U = 0x19,
|
||||||
|
/// <summary>native real (don't use)</summary>
|
||||||
|
R = 0x1A,
|
||||||
|
/// <summary>Function pointer</summary>
|
||||||
|
FnPtr = 0x1B,
|
||||||
|
/// <summary>System.Object</summary>
|
||||||
|
Object = 0x1C,
|
||||||
|
/// <summary>Single-dimension, zero lower bound array ([])</summary>
|
||||||
|
SZArray = 0x1D,
|
||||||
|
/// <summary>Method generic parameter</summary>
|
||||||
|
MVar = 0x1E,
|
||||||
|
/// <summary>Required C modifier</summary>
|
||||||
|
CModReqd = 0x1F,
|
||||||
|
/// <summary>Optional C modifier</summary>
|
||||||
|
CModOpt = 0x20,
|
||||||
|
/// <summary>Used internally by the CLR (don't use)</summary>
|
||||||
|
Internal = 0x21,
|
||||||
|
/// <summary>Module (don't use)</summary>
|
||||||
|
Module = 0x3F,
|
||||||
|
/// <summary>Sentinel (method sigs only)</summary>
|
||||||
|
Sentinel = 0x41,
|
||||||
|
/// <summary>Pinned type (locals only)</summary>
|
||||||
|
Pinned = 0x45,
|
||||||
|
}
|
||||||
|
|
||||||
|
public static partial class Extensions {
|
||||||
|
/// <summary>
|
||||||
|
/// Returns <c>true</c> if it's an integer or a floating point type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="etype">Element type</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool IsPrimitive(this ElementType etype) {
|
||||||
|
switch (etype) {
|
||||||
|
case ElementType.Boolean:
|
||||||
|
case ElementType.Char:
|
||||||
|
case ElementType.I1:
|
||||||
|
case ElementType.U1:
|
||||||
|
case ElementType.I2:
|
||||||
|
case ElementType.U2:
|
||||||
|
case ElementType.I4:
|
||||||
|
case ElementType.U4:
|
||||||
|
case ElementType.I8:
|
||||||
|
case ElementType.U8:
|
||||||
|
case ElementType.R4:
|
||||||
|
case ElementType.R8:
|
||||||
|
case ElementType.I:
|
||||||
|
case ElementType.U:
|
||||||
|
case ElementType.R:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the size of the element type in bytes or <c>-1</c> if it's unknown
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="etype">Element type</param>
|
||||||
|
/// <param name="ptrSize">Size of a pointer</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int GetPrimitiveSize(this ElementType etype, int ptrSize = -1) {
|
||||||
|
switch (etype) {
|
||||||
|
case ElementType.Boolean:
|
||||||
|
case ElementType.I1:
|
||||||
|
case ElementType.U1:
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case ElementType.Char:
|
||||||
|
case ElementType.I2:
|
||||||
|
case ElementType.U2:
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case ElementType.I4:
|
||||||
|
case ElementType.U4:
|
||||||
|
case ElementType.R4:
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case ElementType.I8:
|
||||||
|
case ElementType.U8:
|
||||||
|
case ElementType.R8:
|
||||||
|
return 8;
|
||||||
|
|
||||||
|
case ElementType.Ptr:
|
||||||
|
case ElementType.FnPtr:
|
||||||
|
case ElementType.I:
|
||||||
|
case ElementType.U:
|
||||||
|
return ptrSize;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether it's a value type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="etype">this</param>
|
||||||
|
/// <returns><c>true</c> if it's a value type, <c>false</c> if it's not a value type or
|
||||||
|
/// if we couldn't determine whether it's a value type. Eg., it could be a generic
|
||||||
|
/// instance type.</returns>
|
||||||
|
public static bool IsValueType(this ElementType etype) {
|
||||||
|
switch (etype) {
|
||||||
|
case ElementType.Void:
|
||||||
|
case ElementType.Boolean:
|
||||||
|
case ElementType.Char:
|
||||||
|
case ElementType.I1:
|
||||||
|
case ElementType.U1:
|
||||||
|
case ElementType.I2:
|
||||||
|
case ElementType.U2:
|
||||||
|
case ElementType.I4:
|
||||||
|
case ElementType.U4:
|
||||||
|
case ElementType.I8:
|
||||||
|
case ElementType.U8:
|
||||||
|
case ElementType.R4:
|
||||||
|
case ElementType.R8:
|
||||||
|
case ElementType.ValueType:
|
||||||
|
case ElementType.TypedByRef:
|
||||||
|
case ElementType.ValueArray:
|
||||||
|
case ElementType.I:
|
||||||
|
case ElementType.U:
|
||||||
|
case ElementType.R:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case ElementType.GenericInst:
|
||||||
|
// We don't have enough info to determine whether this is a value type
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case ElementType.End:
|
||||||
|
case ElementType.String:
|
||||||
|
case ElementType.Ptr:
|
||||||
|
case ElementType.ByRef:
|
||||||
|
case ElementType.Class:
|
||||||
|
case ElementType.Var:
|
||||||
|
case ElementType.Array:
|
||||||
|
case ElementType.FnPtr:
|
||||||
|
case ElementType.Object:
|
||||||
|
case ElementType.SZArray:
|
||||||
|
case ElementType.MVar:
|
||||||
|
case ElementType.CModReqd:
|
||||||
|
case ElementType.CModOpt:
|
||||||
|
case ElementType.Internal:
|
||||||
|
case ElementType.Module:
|
||||||
|
case ElementType.Sentinel:
|
||||||
|
case ElementType.Pinned:
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,296 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// A CIL opcode. If the high byte is 0 or if it's <see cref="UNKNOWN1"/>, it's a 1-byte opcode,
|
||||||
|
/// else it's a two-byte opcode and the highest byte is the first byte of the opcode.
|
||||||
|
/// </summary>
|
||||||
|
public enum Code : ushort {
|
||||||
|
#pragma warning disable 1591 // disable XML doc warning
|
||||||
|
UNKNOWN1 = 0x0100,
|
||||||
|
UNKNOWN2 = 0x0101,
|
||||||
|
Add = 0x0058,
|
||||||
|
Add_Ovf = 0x00D6,
|
||||||
|
Add_Ovf_Un = 0x00D7,
|
||||||
|
And = 0x005F,
|
||||||
|
Arglist = 0xFE00,
|
||||||
|
Beq = 0x003B,
|
||||||
|
Beq_S = 0x002E,
|
||||||
|
Bge = 0x003C,
|
||||||
|
Bge_S = 0x002F,
|
||||||
|
Bge_Un = 0x0041,
|
||||||
|
Bge_Un_S = 0x0034,
|
||||||
|
Bgt = 0x003D,
|
||||||
|
Bgt_S = 0x0030,
|
||||||
|
Bgt_Un = 0x0042,
|
||||||
|
Bgt_Un_S = 0x0035,
|
||||||
|
Ble = 0x003E,
|
||||||
|
Ble_S = 0x0031,
|
||||||
|
Ble_Un = 0x0043,
|
||||||
|
Ble_Un_S = 0x0036,
|
||||||
|
Blt = 0x003F,
|
||||||
|
Blt_S = 0x0032,
|
||||||
|
Blt_Un = 0x0044,
|
||||||
|
Blt_Un_S = 0x0037,
|
||||||
|
Bne_Un = 0x0040,
|
||||||
|
Bne_Un_S = 0x0033,
|
||||||
|
Box = 0x008C,
|
||||||
|
Br = 0x0038,
|
||||||
|
Br_S = 0x002B,
|
||||||
|
Break = 0x0001,
|
||||||
|
Brfalse = 0x0039,
|
||||||
|
Brfalse_S = 0x002C,
|
||||||
|
Brtrue = 0x003A,
|
||||||
|
Brtrue_S = 0x002D,
|
||||||
|
Call = 0x0028,
|
||||||
|
Calli = 0x0029,
|
||||||
|
Callvirt = 0x006F,
|
||||||
|
Castclass = 0x0074,
|
||||||
|
Ceq = 0xFE01,
|
||||||
|
Cgt = 0xFE02,
|
||||||
|
Cgt_Un = 0xFE03,
|
||||||
|
Ckfinite = 0x00C3,
|
||||||
|
Clt = 0xFE04,
|
||||||
|
Clt_Un = 0xFE05,
|
||||||
|
Constrained = 0xFE16,
|
||||||
|
Conv_I = 0x00D3,
|
||||||
|
Conv_I1 = 0x0067,
|
||||||
|
Conv_I2 = 0x0068,
|
||||||
|
Conv_I4 = 0x0069,
|
||||||
|
Conv_I8 = 0x006A,
|
||||||
|
Conv_Ovf_I = 0x00D4,
|
||||||
|
Conv_Ovf_I_Un = 0x008A,
|
||||||
|
Conv_Ovf_I1 = 0x00B3,
|
||||||
|
Conv_Ovf_I1_Un = 0x0082,
|
||||||
|
Conv_Ovf_I2 = 0x00B5,
|
||||||
|
Conv_Ovf_I2_Un = 0x0083,
|
||||||
|
Conv_Ovf_I4 = 0x00B7,
|
||||||
|
Conv_Ovf_I4_Un = 0x0084,
|
||||||
|
Conv_Ovf_I8 = 0x00B9,
|
||||||
|
Conv_Ovf_I8_Un = 0x0085,
|
||||||
|
Conv_Ovf_U = 0x00D5,
|
||||||
|
Conv_Ovf_U_Un = 0x008B,
|
||||||
|
Conv_Ovf_U1 = 0x00B4,
|
||||||
|
Conv_Ovf_U1_Un = 0x0086,
|
||||||
|
Conv_Ovf_U2 = 0x00B6,
|
||||||
|
Conv_Ovf_U2_Un = 0x0087,
|
||||||
|
Conv_Ovf_U4 = 0x00B8,
|
||||||
|
Conv_Ovf_U4_Un = 0x0088,
|
||||||
|
Conv_Ovf_U8 = 0x00BA,
|
||||||
|
Conv_Ovf_U8_Un = 0x0089,
|
||||||
|
Conv_R_Un = 0x0076,
|
||||||
|
Conv_R4 = 0x006B,
|
||||||
|
Conv_R8 = 0x006C,
|
||||||
|
Conv_U = 0x00E0,
|
||||||
|
Conv_U1 = 0x00D2,
|
||||||
|
Conv_U2 = 0x00D1,
|
||||||
|
Conv_U4 = 0x006D,
|
||||||
|
Conv_U8 = 0x006E,
|
||||||
|
Cpblk = 0xFE17,
|
||||||
|
Cpobj = 0x0070,
|
||||||
|
Div = 0x005B,
|
||||||
|
Div_Un = 0x005C,
|
||||||
|
Dup = 0x0025,
|
||||||
|
Endfilter = 0xFE11,
|
||||||
|
Endfinally = 0x00DC,
|
||||||
|
Initblk = 0xFE18,
|
||||||
|
Initobj = 0xFE15,
|
||||||
|
Isinst = 0x0075,
|
||||||
|
Jmp = 0x0027,
|
||||||
|
Ldarg = 0xFE09,
|
||||||
|
Ldarg_0 = 0x0002,
|
||||||
|
Ldarg_1 = 0x0003,
|
||||||
|
Ldarg_2 = 0x0004,
|
||||||
|
Ldarg_3 = 0x0005,
|
||||||
|
Ldarg_S = 0x000E,
|
||||||
|
Ldarga = 0xFE0A,
|
||||||
|
Ldarga_S = 0x000F,
|
||||||
|
Ldc_I4 = 0x0020,
|
||||||
|
Ldc_I4_0 = 0x0016,
|
||||||
|
Ldc_I4_1 = 0x0017,
|
||||||
|
Ldc_I4_2 = 0x0018,
|
||||||
|
Ldc_I4_3 = 0x0019,
|
||||||
|
Ldc_I4_4 = 0x001A,
|
||||||
|
Ldc_I4_5 = 0x001B,
|
||||||
|
Ldc_I4_6 = 0x001C,
|
||||||
|
Ldc_I4_7 = 0x001D,
|
||||||
|
Ldc_I4_8 = 0x001E,
|
||||||
|
Ldc_I4_M1 = 0x0015,
|
||||||
|
Ldc_I4_S = 0x001F,
|
||||||
|
Ldc_I8 = 0x0021,
|
||||||
|
Ldc_R4 = 0x0022,
|
||||||
|
Ldc_R8 = 0x0023,
|
||||||
|
Ldelem = 0x00A3,
|
||||||
|
Ldelem_I = 0x0097,
|
||||||
|
Ldelem_I1 = 0x0090,
|
||||||
|
Ldelem_I2 = 0x0092,
|
||||||
|
Ldelem_I4 = 0x0094,
|
||||||
|
Ldelem_I8 = 0x0096,
|
||||||
|
Ldelem_R4 = 0x0098,
|
||||||
|
Ldelem_R8 = 0x0099,
|
||||||
|
Ldelem_Ref = 0x009A,
|
||||||
|
Ldelem_U1 = 0x0091,
|
||||||
|
Ldelem_U2 = 0x0093,
|
||||||
|
Ldelem_U4 = 0x0095,
|
||||||
|
Ldelema = 0x008F,
|
||||||
|
Ldfld = 0x007B,
|
||||||
|
Ldflda = 0x007C,
|
||||||
|
Ldftn = 0xFE06,
|
||||||
|
Ldind_I = 0x004D,
|
||||||
|
Ldind_I1 = 0x0046,
|
||||||
|
Ldind_I2 = 0x0048,
|
||||||
|
Ldind_I4 = 0x004A,
|
||||||
|
Ldind_I8 = 0x004C,
|
||||||
|
Ldind_R4 = 0x004E,
|
||||||
|
Ldind_R8 = 0x004F,
|
||||||
|
Ldind_Ref = 0x0050,
|
||||||
|
Ldind_U1 = 0x0047,
|
||||||
|
Ldind_U2 = 0x0049,
|
||||||
|
Ldind_U4 = 0x004B,
|
||||||
|
Ldlen = 0x008E,
|
||||||
|
Ldloc = 0xFE0C,
|
||||||
|
Ldloc_0 = 0x0006,
|
||||||
|
Ldloc_1 = 0x0007,
|
||||||
|
Ldloc_2 = 0x0008,
|
||||||
|
Ldloc_3 = 0x0009,
|
||||||
|
Ldloc_S = 0x0011,
|
||||||
|
Ldloca = 0xFE0D,
|
||||||
|
Ldloca_S = 0x0012,
|
||||||
|
Ldnull = 0x0014,
|
||||||
|
Ldobj = 0x0071,
|
||||||
|
Ldsfld = 0x007E,
|
||||||
|
Ldsflda = 0x007F,
|
||||||
|
Ldstr = 0x0072,
|
||||||
|
Ldtoken = 0x00D0,
|
||||||
|
Ldvirtftn = 0xFE07,
|
||||||
|
Leave = 0x00DD,
|
||||||
|
Leave_S = 0x00DE,
|
||||||
|
Localloc = 0xFE0F,
|
||||||
|
Mkrefany = 0x00C6,
|
||||||
|
Mul = 0x005A,
|
||||||
|
Mul_Ovf = 0x00D8,
|
||||||
|
Mul_Ovf_Un = 0x00D9,
|
||||||
|
Neg = 0x0065,
|
||||||
|
Newarr = 0x008D,
|
||||||
|
Newobj = 0x0073,
|
||||||
|
No = 0xFE19,
|
||||||
|
Nop = 0x0000,
|
||||||
|
Not = 0x0066,
|
||||||
|
Or = 0x0060,
|
||||||
|
Pop = 0x0026,
|
||||||
|
Prefix1 = 0x00FE,
|
||||||
|
Prefix2 = 0x00FD,
|
||||||
|
Prefix3 = 0x00FC,
|
||||||
|
Prefix4 = 0x00FB,
|
||||||
|
Prefix5 = 0x00FA,
|
||||||
|
Prefix6 = 0x00F9,
|
||||||
|
Prefix7 = 0x00F8,
|
||||||
|
Prefixref = 0x00FF,
|
||||||
|
Readonly = 0xFE1E,
|
||||||
|
Refanytype = 0xFE1D,
|
||||||
|
Refanyval = 0x00C2,
|
||||||
|
Rem = 0x005D,
|
||||||
|
Rem_Un = 0x005E,
|
||||||
|
Ret = 0x002A,
|
||||||
|
Rethrow = 0xFE1A,
|
||||||
|
Shl = 0x0062,
|
||||||
|
Shr = 0x0063,
|
||||||
|
Shr_Un = 0x0064,
|
||||||
|
Sizeof = 0xFE1C,
|
||||||
|
Starg = 0xFE0B,
|
||||||
|
Starg_S = 0x0010,
|
||||||
|
Stelem = 0x00A4,
|
||||||
|
Stelem_I = 0x009B,
|
||||||
|
Stelem_I1 = 0x009C,
|
||||||
|
Stelem_I2 = 0x009D,
|
||||||
|
Stelem_I4 = 0x009E,
|
||||||
|
Stelem_I8 = 0x009F,
|
||||||
|
Stelem_R4 = 0x00A0,
|
||||||
|
Stelem_R8 = 0x00A1,
|
||||||
|
Stelem_Ref = 0x00A2,
|
||||||
|
Stfld = 0x007D,
|
||||||
|
Stind_I = 0x00DF,
|
||||||
|
Stind_I1 = 0x0052,
|
||||||
|
Stind_I2 = 0x0053,
|
||||||
|
Stind_I4 = 0x0054,
|
||||||
|
Stind_I8 = 0x0055,
|
||||||
|
Stind_R4 = 0x0056,
|
||||||
|
Stind_R8 = 0x0057,
|
||||||
|
Stind_Ref = 0x0051,
|
||||||
|
Stloc = 0xFE0E,
|
||||||
|
Stloc_0 = 0x000A,
|
||||||
|
Stloc_1 = 0x000B,
|
||||||
|
Stloc_2 = 0x000C,
|
||||||
|
Stloc_3 = 0x000D,
|
||||||
|
Stloc_S = 0x0013,
|
||||||
|
Stobj = 0x0081,
|
||||||
|
Stsfld = 0x0080,
|
||||||
|
Sub = 0x0059,
|
||||||
|
Sub_Ovf = 0x00DA,
|
||||||
|
Sub_Ovf_Un = 0x00DB,
|
||||||
|
Switch = 0x0045,
|
||||||
|
Tailcall = 0xFE14,
|
||||||
|
Throw = 0x007A,
|
||||||
|
Unaligned = 0xFE12,
|
||||||
|
Unbox = 0x0079,
|
||||||
|
Unbox_Any = 0x00A5,
|
||||||
|
Volatile = 0xFE13,
|
||||||
|
Xor = 0x0061,
|
||||||
|
#pragma warning restore
|
||||||
|
}
|
||||||
|
|
||||||
|
public static partial class Extensions {
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether a <see cref="Code"/> is experimental
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code">The code</param>
|
||||||
|
/// <returns><c>true</c> if the <see cref="Code"/> is experimental; otherwise, <c>false</c></returns>
|
||||||
|
public static bool IsExperimental(this Code code) {
|
||||||
|
byte hi = (byte)((ushort)code >> 8);
|
||||||
|
|
||||||
|
return hi >= 0xF0 && hi <= 0xFB;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a <see cref="Code"/> to an <see cref="OpCode"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code">The code</param>
|
||||||
|
/// <returns>A <see cref="OpCode"/> or <c>null</c> if it's invalid</returns>
|
||||||
|
public static OpCode ToOpCode(this Code code) {
|
||||||
|
byte hi = (byte)((ushort)code >> 8);
|
||||||
|
byte lo = (byte)code;
|
||||||
|
if (hi == 0)
|
||||||
|
return OpCodes.OneByteOpCodes[lo];
|
||||||
|
if (hi == 0xFE)
|
||||||
|
return OpCodes.TwoByteOpCodes[lo];
|
||||||
|
if (code == Code.UNKNOWN1)
|
||||||
|
return OpCodes.UNKNOWN1;
|
||||||
|
if (code == Code.UNKNOWN2)
|
||||||
|
return OpCodes.UNKNOWN2;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a <see cref="Code"/> to an <see cref="OpCode"/>, using a module context to look
|
||||||
|
/// up potential experimental opcodes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code">The code</param>
|
||||||
|
/// <param name="context">The module context</param>
|
||||||
|
/// <returns>A <see cref="OpCode"/> or <c>null</c> if it's invalid</returns>
|
||||||
|
public static OpCode ToOpCode(this Code code, ModuleContext context) {
|
||||||
|
byte hi = (byte)((ushort)code >> 8);
|
||||||
|
byte lo = (byte)code;
|
||||||
|
if (hi == 0)
|
||||||
|
return OpCodes.OneByteOpCodes[lo];
|
||||||
|
if (hi == 0xFE)
|
||||||
|
return OpCodes.TwoByteOpCodes[lo];
|
||||||
|
if (context.GetExperimentalOpCode(hi, lo) is OpCode op)
|
||||||
|
return op;
|
||||||
|
if (code == Code.UNKNOWN1)
|
||||||
|
return OpCodes.UNKNOWN1;
|
||||||
|
if (code == Code.UNKNOWN2)
|
||||||
|
return OpCodes.UNKNOWN2;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,569 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using SR = System.Reflection;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
using System.IO;
|
||||||
|
using dnlib.DotNet.MD;
|
||||||
|
using dnlib.IO;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="DynamicMethodBodyReader"/> options
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum DynamicMethodBodyReaderOptions {
|
||||||
|
/// <summary>
|
||||||
|
/// No option is enabled
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Some fields/methods have an unknown declaring type and don't have a context with
|
||||||
|
/// that information. If this is enabled, the reader will try to guess it but it doesn't
|
||||||
|
/// always work. If you get an <see cref="ArgumentException"/>, try enabling this option.
|
||||||
|
/// </summary>
|
||||||
|
UnknownDeclaringType = 0x00000001,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads code from a DynamicMethod
|
||||||
|
/// </summary>
|
||||||
|
public class DynamicMethodBodyReader : MethodBodyReaderBase, ISignatureReaderHelper {
|
||||||
|
static readonly ReflectionFieldInfo rtdmOwnerFieldInfo = new ReflectionFieldInfo("m_owner");
|
||||||
|
static readonly ReflectionFieldInfo dmResolverFieldInfo = new ReflectionFieldInfo("m_resolver", "_resolver");
|
||||||
|
static readonly ReflectionFieldInfo rslvCodeFieldInfo = new ReflectionFieldInfo("m_code");
|
||||||
|
static readonly ReflectionFieldInfo rslvDynamicScopeFieldInfo = new ReflectionFieldInfo("m_scope");
|
||||||
|
static readonly ReflectionFieldInfo rslvMethodFieldInfo = new ReflectionFieldInfo("m_method");
|
||||||
|
static readonly ReflectionFieldInfo rslvLocalsFieldInfo = new ReflectionFieldInfo("m_localSignature");
|
||||||
|
static readonly ReflectionFieldInfo rslvMaxStackFieldInfo = new ReflectionFieldInfo("m_stackSize");
|
||||||
|
static readonly ReflectionFieldInfo rslvExceptionsFieldInfo = new ReflectionFieldInfo("m_exceptions");
|
||||||
|
static readonly ReflectionFieldInfo rslvExceptionHeaderFieldInfo = new ReflectionFieldInfo("m_exceptionHeader");
|
||||||
|
static readonly ReflectionFieldInfo scopeTokensFieldInfo = new ReflectionFieldInfo("m_tokens");
|
||||||
|
static readonly ReflectionFieldInfo gfiFieldHandleFieldInfo = new ReflectionFieldInfo("m_field", "m_fieldHandle");
|
||||||
|
static readonly ReflectionFieldInfo gfiContextFieldInfo = new ReflectionFieldInfo("m_context");
|
||||||
|
static readonly ReflectionFieldInfo gmiMethodHandleFieldInfo = new ReflectionFieldInfo("m_method", "m_methodHandle");
|
||||||
|
static readonly ReflectionFieldInfo gmiContextFieldInfo = new ReflectionFieldInfo("m_context");
|
||||||
|
static readonly ReflectionFieldInfo ehCatchAddrFieldInfo = new ReflectionFieldInfo("m_catchAddr");
|
||||||
|
static readonly ReflectionFieldInfo ehCatchClassFieldInfo = new ReflectionFieldInfo("m_catchClass");
|
||||||
|
static readonly ReflectionFieldInfo ehCatchEndAddrFieldInfo = new ReflectionFieldInfo("m_catchEndAddr");
|
||||||
|
static readonly ReflectionFieldInfo ehCurrentCatchFieldInfo = new ReflectionFieldInfo("m_currentCatch");
|
||||||
|
static readonly ReflectionFieldInfo ehTypeFieldInfo = new ReflectionFieldInfo("m_type");
|
||||||
|
static readonly ReflectionFieldInfo ehStartAddrFieldInfo = new ReflectionFieldInfo("m_startAddr");
|
||||||
|
static readonly ReflectionFieldInfo ehEndAddrFieldInfo = new ReflectionFieldInfo("m_endAddr");
|
||||||
|
static readonly ReflectionFieldInfo ehEndFinallyFieldInfo = new ReflectionFieldInfo("m_endFinally");
|
||||||
|
static readonly ReflectionFieldInfo vamMethodFieldInfo = new ReflectionFieldInfo("m_method");
|
||||||
|
static readonly ReflectionFieldInfo vamDynamicMethodFieldInfo = new ReflectionFieldInfo("m_dynamicMethod");
|
||||||
|
static readonly ReflectionFieldInfo dmDynamicILInfoFieldInfo = new ReflectionFieldInfo("m_DynamicILInfo", "_dynamicILInfo");
|
||||||
|
static readonly ReflectionFieldInfo dynILInfoMaxStackFieldInfo = new ReflectionFieldInfo("m_maxStackSize");
|
||||||
|
|
||||||
|
readonly ModuleDef module;
|
||||||
|
readonly Importer importer;
|
||||||
|
readonly GenericParamContext gpContext;
|
||||||
|
readonly MethodDef method;
|
||||||
|
readonly int codeSize;
|
||||||
|
readonly int maxStack;
|
||||||
|
readonly bool initLocals;
|
||||||
|
readonly List<object> tokens;
|
||||||
|
readonly IList<object> ehInfos;
|
||||||
|
readonly byte[] ehHeader;
|
||||||
|
readonly string methodName;
|
||||||
|
readonly DynamicMethodBodyReaderOptions options;
|
||||||
|
|
||||||
|
class ReflectionFieldInfo {
|
||||||
|
SR.FieldInfo fieldInfo;
|
||||||
|
readonly string fieldName1;
|
||||||
|
readonly string fieldName2;
|
||||||
|
|
||||||
|
public ReflectionFieldInfo(string fieldName) => fieldName1 = fieldName;
|
||||||
|
|
||||||
|
public ReflectionFieldInfo(string fieldName1, string fieldName2) {
|
||||||
|
this.fieldName1 = fieldName1;
|
||||||
|
this.fieldName2 = fieldName2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Read(object instance) {
|
||||||
|
if (fieldInfo is null)
|
||||||
|
InitializeField(instance.GetType());
|
||||||
|
if (fieldInfo is null)
|
||||||
|
throw new Exception($"Couldn't find field '{fieldName1}' or '{fieldName2}'");
|
||||||
|
|
||||||
|
return fieldInfo.GetValue(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Exists(object instance) {
|
||||||
|
InitializeField(instance.GetType());
|
||||||
|
return fieldInfo is not null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeField(Type type) {
|
||||||
|
if (fieldInfo is not null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const SR.BindingFlags flags = SR.BindingFlags.Instance | SR.BindingFlags.Public | SR.BindingFlags.NonPublic;
|
||||||
|
fieldInfo = type.GetField(fieldName1, flags);
|
||||||
|
if (fieldInfo is null && fieldName2 is not null)
|
||||||
|
fieldInfo = type.GetField(fieldName2, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Module that will own the method body</param>
|
||||||
|
/// <param name="obj">This can be one of several supported types: the delegate instance
|
||||||
|
/// created by DynamicMethod.CreateDelegate(), a DynamicMethod instance, a RTDynamicMethod
|
||||||
|
/// instance or a DynamicResolver instance.</param>
|
||||||
|
public DynamicMethodBodyReader(ModuleDef module, object obj)
|
||||||
|
: this(module, obj, new GenericParamContext()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Module that will own the method body</param>
|
||||||
|
/// <param name="obj">This can be one of several supported types: the delegate instance
|
||||||
|
/// created by DynamicMethod.CreateDelegate(), a DynamicMethod instance, a RTDynamicMethod
|
||||||
|
/// instance or a DynamicResolver instance.</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
public DynamicMethodBodyReader(ModuleDef module, object obj, GenericParamContext gpContext)
|
||||||
|
: this(module, obj, new Importer(module, ImporterOptions.TryToUseDefs, gpContext), DynamicMethodBodyReaderOptions.None) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Module that will own the method body</param>
|
||||||
|
/// <param name="obj">This can be one of several supported types: the delegate instance
|
||||||
|
/// created by DynamicMethod.CreateDelegate(), a DynamicMethod instance, a RTDynamicMethod
|
||||||
|
/// instance or a DynamicResolver instance.</param>
|
||||||
|
/// <param name="importer">Importer</param>
|
||||||
|
public DynamicMethodBodyReader(ModuleDef module, object obj, Importer importer)
|
||||||
|
: this(module, obj, importer, DynamicMethodBodyReaderOptions.None) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Module that will own the method body</param>
|
||||||
|
/// <param name="obj">This can be one of several supported types: the delegate instance
|
||||||
|
/// created by DynamicMethod.CreateDelegate(), a DynamicMethod instance, a RTDynamicMethod
|
||||||
|
/// instance or a DynamicResolver instance.</param>
|
||||||
|
/// <param name="importer">Importer</param>
|
||||||
|
/// <param name="options">Options</param>
|
||||||
|
public DynamicMethodBodyReader(ModuleDef module, object obj, Importer importer, DynamicMethodBodyReaderOptions options)
|
||||||
|
: base(module.Context) {
|
||||||
|
this.module = module;
|
||||||
|
this.importer = importer;
|
||||||
|
this.options = options;
|
||||||
|
gpContext = importer.gpContext;
|
||||||
|
methodName = null;
|
||||||
|
|
||||||
|
if (obj is null)
|
||||||
|
throw new ArgumentNullException(nameof(obj));
|
||||||
|
|
||||||
|
if (obj is Delegate del) {
|
||||||
|
obj = del.Method;
|
||||||
|
if (obj is null)
|
||||||
|
throw new Exception("Delegate.Method is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.GetType().ToString() == "System.Reflection.Emit.DynamicMethod+RTDynamicMethod") {
|
||||||
|
obj = rtdmOwnerFieldInfo.Read(obj) as DynamicMethod;
|
||||||
|
if (obj is null)
|
||||||
|
throw new Exception("RTDynamicMethod.m_owner is null or invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj is DynamicMethod dynMethod) {
|
||||||
|
methodName = dynMethod.Name;
|
||||||
|
obj = dmResolverFieldInfo.Read(obj) ?? dmDynamicILInfoFieldInfo.Read(obj);;
|
||||||
|
if (obj is null)
|
||||||
|
throw new Exception("No resolver found");
|
||||||
|
}
|
||||||
|
|
||||||
|
string objTypeName = obj.GetType().ToString();
|
||||||
|
bool isILInfo = objTypeName == "System.Reflection.Emit.DynamicILInfo";
|
||||||
|
if (objTypeName != "System.Reflection.Emit.DynamicResolver" && !isILInfo)
|
||||||
|
throw new Exception("Couldn't find DynamicResolver or DynamicILInfo");
|
||||||
|
|
||||||
|
var code = rslvCodeFieldInfo.Read(obj) as byte[];
|
||||||
|
if (code is null)
|
||||||
|
throw new Exception("No code");
|
||||||
|
codeSize = code.Length;
|
||||||
|
var delMethod = rslvMethodFieldInfo.Read(obj) as DynamicMethod;
|
||||||
|
if (delMethod is null)
|
||||||
|
throw new Exception("No method");
|
||||||
|
initLocals = delMethod.InitLocals;
|
||||||
|
maxStack = isILInfo ? (int)dynILInfoMaxStackFieldInfo.Read(obj) : (int)rslvMaxStackFieldInfo.Read(obj);
|
||||||
|
|
||||||
|
var scope = rslvDynamicScopeFieldInfo.Read(obj);
|
||||||
|
if (scope is null)
|
||||||
|
throw new Exception("No scope");
|
||||||
|
var tokensList = scopeTokensFieldInfo.Read(scope) as System.Collections.IList;
|
||||||
|
if (tokensList is null)
|
||||||
|
throw new Exception("No tokens");
|
||||||
|
tokens = new List<object>(tokensList.Count);
|
||||||
|
for (int i = 0; i < tokensList.Count; i++)
|
||||||
|
tokens.Add(tokensList[i]);
|
||||||
|
|
||||||
|
if (isILInfo)
|
||||||
|
ehHeader = rslvExceptionsFieldInfo.Read(obj) as byte[];
|
||||||
|
else {
|
||||||
|
ehInfos = (IList<object>)rslvExceptionsFieldInfo.Read(obj);
|
||||||
|
ehHeader = rslvExceptionHeaderFieldInfo.Read(obj) as byte[];
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateLocals(rslvLocalsFieldInfo.Read(obj) as byte[]);
|
||||||
|
reader = ByteArrayDataReaderFactory.CreateReader(code);
|
||||||
|
method = CreateMethodDef(delMethod);
|
||||||
|
parameters = method.Parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExceptionInfo {
|
||||||
|
public int[] CatchAddr;
|
||||||
|
public Type[] CatchClass;
|
||||||
|
public int[] CatchEndAddr;
|
||||||
|
public int CurrentCatch;
|
||||||
|
public int[] Type;
|
||||||
|
public int StartAddr;
|
||||||
|
public int EndAddr;
|
||||||
|
public int EndFinally;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<ExceptionInfo> CreateExceptionInfos(IList<object> ehInfos) {
|
||||||
|
if (ehInfos is null)
|
||||||
|
return new List<ExceptionInfo>();
|
||||||
|
|
||||||
|
var infos = new List<ExceptionInfo>(ehInfos.Count);
|
||||||
|
|
||||||
|
int count = ehInfos.Count;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
var ehInfo = ehInfos[i];
|
||||||
|
var eh = new ExceptionInfo {
|
||||||
|
CatchAddr = (int[])ehCatchAddrFieldInfo.Read(ehInfo),
|
||||||
|
CatchClass = (Type[])ehCatchClassFieldInfo.Read(ehInfo),
|
||||||
|
CatchEndAddr = (int[])ehCatchEndAddrFieldInfo.Read(ehInfo),
|
||||||
|
CurrentCatch = (int)ehCurrentCatchFieldInfo.Read(ehInfo),
|
||||||
|
Type = (int[])ehTypeFieldInfo.Read(ehInfo),
|
||||||
|
StartAddr = (int)ehStartAddrFieldInfo.Read(ehInfo),
|
||||||
|
EndAddr = (int)ehEndAddrFieldInfo.Read(ehInfo),
|
||||||
|
EndFinally = (int)ehEndFinallyFieldInfo.Read(ehInfo),
|
||||||
|
};
|
||||||
|
infos.Add(eh);
|
||||||
|
}
|
||||||
|
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateLocals(byte[] localsSig) {
|
||||||
|
if (localsSig is null || localsSig.Length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var sig = SignatureReader.ReadSig(this, module.CorLibTypes, localsSig, gpContext) as LocalSig;
|
||||||
|
if (sig is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var sigLocals = sig.Locals;
|
||||||
|
int count = sigLocals.Count;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
locals.Add(new Local(sigLocals[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef CreateMethodDef(SR.MethodBase delMethod) {
|
||||||
|
bool isStatic = true;
|
||||||
|
var method = new MethodDefUser();
|
||||||
|
|
||||||
|
var retType = GetReturnType(delMethod);
|
||||||
|
var pms = GetParameters(delMethod);
|
||||||
|
if (isStatic)
|
||||||
|
method.Signature = MethodSig.CreateStatic(retType, pms.ToArray());
|
||||||
|
else
|
||||||
|
method.Signature = MethodSig.CreateInstance(retType, pms.ToArray());
|
||||||
|
|
||||||
|
method.Parameters.UpdateParameterTypes();
|
||||||
|
method.ImplAttributes = MethodImplAttributes.IL;
|
||||||
|
method.Attributes = MethodAttributes.PrivateScope;
|
||||||
|
if (isStatic)
|
||||||
|
method.Attributes |= MethodAttributes.Static;
|
||||||
|
|
||||||
|
return module.UpdateRowId(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeSig GetReturnType(SR.MethodBase mb) {
|
||||||
|
if (mb is SR.MethodInfo mi)
|
||||||
|
return importer.ImportAsTypeSig(mi.ReturnType);
|
||||||
|
return module.CorLibTypes.Void;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TypeSig> GetParameters(SR.MethodBase delMethod) {
|
||||||
|
var pms = new List<TypeSig>();
|
||||||
|
foreach (var param in delMethod.GetParameters())
|
||||||
|
pms.Add(importer.ImportAsTypeSig(param.ParameterType));
|
||||||
|
return pms;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the code
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool Read() {
|
||||||
|
ReadInstructionsNumBytes((uint)codeSize);
|
||||||
|
CreateExceptionHandlers();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateExceptionHandlers() {
|
||||||
|
if (ehHeader is not null) {
|
||||||
|
if (ehHeader.Length < 4)
|
||||||
|
return;
|
||||||
|
var reader = new BinaryReader(new MemoryStream(ehHeader));
|
||||||
|
byte b = reader.ReadByte();
|
||||||
|
if ((b & 0x40) == 0) { // DynamicResolver only checks bit 6
|
||||||
|
// Calculate num ehs exactly the same way that DynamicResolver does
|
||||||
|
int numHandlers = (ushort)((reader.ReadByte() - 2) / 12);
|
||||||
|
reader.ReadUInt16();
|
||||||
|
for (int i = 0; i < numHandlers; i++) {
|
||||||
|
if (reader.BaseStream.Position + 12 > reader.BaseStream.Length)
|
||||||
|
break;
|
||||||
|
var eh = new ExceptionHandler();
|
||||||
|
eh.HandlerType = (ExceptionHandlerType)reader.ReadUInt16();
|
||||||
|
int offs = reader.ReadUInt16();
|
||||||
|
eh.TryStart = GetInstructionThrow((uint)offs);
|
||||||
|
eh.TryEnd = GetInstruction((uint)(reader.ReadByte() + offs));
|
||||||
|
offs = reader.ReadUInt16();
|
||||||
|
eh.HandlerStart = GetInstructionThrow((uint)offs);
|
||||||
|
eh.HandlerEnd = GetInstruction((uint)(reader.ReadByte() + offs));
|
||||||
|
|
||||||
|
if (eh.IsCatch)
|
||||||
|
eh.CatchType = ReadToken(reader.ReadUInt32()) as ITypeDefOrRef;
|
||||||
|
else if (eh.IsFilter)
|
||||||
|
eh.FilterStart = GetInstruction(reader.ReadUInt32());
|
||||||
|
else
|
||||||
|
reader.ReadUInt32();
|
||||||
|
|
||||||
|
exceptionHandlers.Add(eh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
reader.BaseStream.Position--;
|
||||||
|
int numHandlers = (ushort)(((reader.ReadUInt32() >> 8) - 4) / 24);
|
||||||
|
for (int i = 0; i < numHandlers; i++) {
|
||||||
|
if (reader.BaseStream.Position + 24 > reader.BaseStream.Length)
|
||||||
|
break;
|
||||||
|
var eh = new ExceptionHandler();
|
||||||
|
eh.HandlerType = (ExceptionHandlerType)reader.ReadUInt32();
|
||||||
|
var offs = reader.ReadUInt32();
|
||||||
|
eh.TryStart = GetInstructionThrow((uint)offs);
|
||||||
|
eh.TryEnd = GetInstruction((uint)(reader.ReadUInt32() + offs));
|
||||||
|
offs = reader.ReadUInt32();
|
||||||
|
eh.HandlerStart = GetInstructionThrow((uint)offs);
|
||||||
|
eh.HandlerEnd = GetInstruction((uint)(reader.ReadUInt32() + offs));
|
||||||
|
|
||||||
|
if (eh.IsCatch)
|
||||||
|
eh.CatchType = ReadToken(reader.ReadUInt32()) as ITypeDefOrRef;
|
||||||
|
else if (eh.IsFilter)
|
||||||
|
eh.FilterStart = GetInstruction(reader.ReadUInt32());
|
||||||
|
else
|
||||||
|
reader.ReadUInt32();
|
||||||
|
|
||||||
|
exceptionHandlers.Add(eh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ehInfos is not null) {
|
||||||
|
foreach (var ehInfo in CreateExceptionInfos(ehInfos)) {
|
||||||
|
var tryStart = GetInstructionThrow((uint)ehInfo.StartAddr);
|
||||||
|
var tryEnd = GetInstruction((uint)ehInfo.EndAddr);
|
||||||
|
var endFinally = ehInfo.EndFinally < 0 ? null : GetInstruction((uint)ehInfo.EndFinally);
|
||||||
|
for (int i = 0; i < ehInfo.CurrentCatch; i++) {
|
||||||
|
var eh = new ExceptionHandler();
|
||||||
|
eh.HandlerType = (ExceptionHandlerType)ehInfo.Type[i];
|
||||||
|
eh.TryStart = tryStart;
|
||||||
|
eh.TryEnd = eh.IsFinally ? endFinally : tryEnd;
|
||||||
|
eh.FilterStart = null; // not supported by DynamicMethod.ILGenerator
|
||||||
|
eh.HandlerStart = GetInstructionThrow((uint)ehInfo.CatchAddr[i]);
|
||||||
|
eh.HandlerEnd = GetInstruction((uint)ehInfo.CatchEndAddr[i]);
|
||||||
|
eh.CatchType = importer.Import(ehInfo.CatchClass[i]);
|
||||||
|
exceptionHandlers.Add(eh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the created method. Must be called after <see cref="Read()"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A new <see cref="CilBody"/> instance</returns>
|
||||||
|
public MethodDef GetMethod() {
|
||||||
|
var cilBody = new CilBody(initLocals, instructions, exceptionHandlers, locals);
|
||||||
|
cilBody.MaxStack = (ushort)Math.Min(maxStack, ushort.MaxValue);
|
||||||
|
instructions = null;
|
||||||
|
exceptionHandlers = null;
|
||||||
|
locals = null;
|
||||||
|
method.Body = cilBody;
|
||||||
|
method.Name = methodName;
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override IField ReadInlineField(Instruction instr) => ReadToken(reader.ReadUInt32()) as IField;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override IMethod ReadInlineMethod(Instruction instr) => ReadToken(reader.ReadUInt32()) as IMethod;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override MethodSig ReadInlineSig(Instruction instr) => ReadToken(reader.ReadUInt32()) as MethodSig;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override string ReadInlineString(Instruction instr) => ReadToken(reader.ReadUInt32()) as string ?? string.Empty;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override ITokenOperand ReadInlineTok(Instruction instr) => ReadToken(reader.ReadUInt32()) as ITokenOperand;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override ITypeDefOrRef ReadInlineType(Instruction instr) => ReadToken(reader.ReadUInt32()) as ITypeDefOrRef;
|
||||||
|
|
||||||
|
object ReadToken(uint token) {
|
||||||
|
uint rid = token & 0x00FFFFFF;
|
||||||
|
switch (token >> 24) {
|
||||||
|
case 0x02:
|
||||||
|
return ImportType(rid);
|
||||||
|
|
||||||
|
case 0x04:
|
||||||
|
return ImportField(rid);
|
||||||
|
|
||||||
|
case 0x06:
|
||||||
|
case 0x0A:
|
||||||
|
return ImportMethod(rid);
|
||||||
|
|
||||||
|
case 0x11:
|
||||||
|
return ImportSignature(rid);
|
||||||
|
|
||||||
|
case 0x70:
|
||||||
|
return Resolve(rid) as string;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IMethod ImportMethod(uint rid) {
|
||||||
|
var obj = Resolve(rid);
|
||||||
|
if (obj is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (obj is RuntimeMethodHandle) {
|
||||||
|
if ((options & DynamicMethodBodyReaderOptions.UnknownDeclaringType) != 0) {
|
||||||
|
// Sometimes it's a generic type but obj != `GenericMethodInfo`, so pass in 'default' and the
|
||||||
|
// runtime will try to figure out the declaring type. https://github.com/0xd4d/dnlib/issues/298
|
||||||
|
return importer.Import(SR.MethodBase.GetMethodFromHandle((RuntimeMethodHandle)obj, default));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return importer.Import(SR.MethodBase.GetMethodFromHandle((RuntimeMethodHandle)obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.GetType().ToString() == "System.Reflection.Emit.GenericMethodInfo") {
|
||||||
|
var context = (RuntimeTypeHandle)gmiContextFieldInfo.Read(obj);
|
||||||
|
var method = SR.MethodBase.GetMethodFromHandle((RuntimeMethodHandle)gmiMethodHandleFieldInfo.Read(obj), context);
|
||||||
|
return importer.Import(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.GetType().ToString() == "System.Reflection.Emit.VarArgMethod") {
|
||||||
|
var method = GetVarArgMethod(obj);
|
||||||
|
if (!(method is DynamicMethod))
|
||||||
|
return importer.Import(method);
|
||||||
|
obj = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj is DynamicMethod dm)
|
||||||
|
throw new Exception("DynamicMethod calls another DynamicMethod");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR.MethodInfo GetVarArgMethod(object obj) {
|
||||||
|
if (vamDynamicMethodFieldInfo.Exists(obj)) {
|
||||||
|
// .NET Framework 4.0+
|
||||||
|
var method = vamMethodFieldInfo.Read(obj) as SR.MethodInfo;
|
||||||
|
var dynMethod = vamDynamicMethodFieldInfo.Read(obj) as DynamicMethod;
|
||||||
|
return dynMethod ?? method;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// .NET Framework 2.0
|
||||||
|
// This is either a DynamicMethod or a MethodInfo
|
||||||
|
return vamMethodFieldInfo.Read(obj) as SR.MethodInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IField ImportField(uint rid) {
|
||||||
|
var obj = Resolve(rid);
|
||||||
|
if (obj is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (obj is RuntimeFieldHandle) {
|
||||||
|
if ((options & DynamicMethodBodyReaderOptions.UnknownDeclaringType) != 0) {
|
||||||
|
// Sometimes it's a generic type but obj != `GenericFieldInfo`, so pass in 'default' and the
|
||||||
|
// runtime will try to figure out the declaring type. https://github.com/0xd4d/dnlib/issues/298
|
||||||
|
return importer.Import(SR.FieldInfo.GetFieldFromHandle((RuntimeFieldHandle)obj, default));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return importer.Import(SR.FieldInfo.GetFieldFromHandle((RuntimeFieldHandle)obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.GetType().ToString() == "System.Reflection.Emit.GenericFieldInfo") {
|
||||||
|
var context = (RuntimeTypeHandle)gfiContextFieldInfo.Read(obj);
|
||||||
|
var field = SR.FieldInfo.GetFieldFromHandle((RuntimeFieldHandle)gfiFieldHandleFieldInfo.Read(obj), context);
|
||||||
|
return importer.Import(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ITypeDefOrRef ImportType(uint rid) {
|
||||||
|
var obj = Resolve(rid);
|
||||||
|
if (obj is RuntimeTypeHandle)
|
||||||
|
return importer.Import(Type.GetTypeFromHandle((RuntimeTypeHandle)obj));
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallingConventionSig ImportSignature(uint rid) {
|
||||||
|
var sig = Resolve(rid) as byte[];
|
||||||
|
if (sig is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return SignatureReader.ReadSig(this, module.CorLibTypes, sig, gpContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
object Resolve(uint index) {
|
||||||
|
if (index >= (uint)tokens.Count)
|
||||||
|
return null;
|
||||||
|
return tokens[(int)index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void RestoreMethod(MethodDef method) {
|
||||||
|
base.RestoreMethod(method);
|
||||||
|
|
||||||
|
var body = method.Body;
|
||||||
|
body.InitLocals = initLocals;
|
||||||
|
body.MaxStack = (ushort)Math.Min(maxStack, ushort.MaxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
ITypeDefOrRef ISignatureReaderHelper.ResolveTypeDefOrRef(uint codedToken, GenericParamContext gpContext) {
|
||||||
|
if (!CodedToken.TypeDefOrRef.Decode(codedToken, out uint token))
|
||||||
|
return null;
|
||||||
|
switch (MDToken.ToTable(token)) {
|
||||||
|
case Table.TypeDef:
|
||||||
|
case Table.TypeRef:
|
||||||
|
case Table.TypeSpec:
|
||||||
|
return module.ResolveToken(token) as ITypeDefOrRef;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeSig ISignatureReaderHelper.ConvertRTInternalAddress(IntPtr address) => importer.ImportAsTypeSig(MethodTableToTypeConverter.Convert(address));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// A CIL method exception handler
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ExceptionHandler {
|
||||||
|
/// <summary>
|
||||||
|
/// First instruction of try block
|
||||||
|
/// </summary>
|
||||||
|
public Instruction TryStart;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// One instruction past the end of try block or <c>null</c> if it ends at the end
|
||||||
|
/// of the method.
|
||||||
|
/// </summary>
|
||||||
|
public Instruction TryEnd;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Start of filter handler or <c>null</c> if none. The end of filter handler is
|
||||||
|
/// always <see cref="HandlerStart"/>.
|
||||||
|
/// </summary>
|
||||||
|
public Instruction FilterStart;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// First instruction of try handler block
|
||||||
|
/// </summary>
|
||||||
|
public Instruction HandlerStart;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// One instruction past the end of try handler block or <c>null</c> if it ends at the end
|
||||||
|
/// of the method.
|
||||||
|
/// </summary>
|
||||||
|
public Instruction HandlerEnd;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The catch type if <see cref="IsCatch"/> is <see langword="true" />
|
||||||
|
/// </summary>
|
||||||
|
public ITypeDefOrRef CatchType;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Type of exception handler clause
|
||||||
|
/// </summary>
|
||||||
|
public ExceptionHandlerType HandlerType;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if it's a `catch` handler
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCatch => ((uint)HandlerType & 7) == (uint)ExceptionHandlerType.Catch;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if it's a `filter` handler
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFilter => (HandlerType & ExceptionHandlerType.Filter) != 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if it's a `finally` handler
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFinally => (HandlerType & ExceptionHandlerType.Finally) != 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if it's a `fault` handler
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFault => (HandlerType & ExceptionHandlerType.Fault) != 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public ExceptionHandler() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handlerType">Exception clause type</param>
|
||||||
|
public ExceptionHandler(ExceptionHandlerType handlerType) => HandlerType = handlerType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// Type of exception handler. See CorHdr.h/CorExceptionFlag
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum ExceptionHandlerType {
|
||||||
|
/// <summary/>
|
||||||
|
Catch = 0x0000,
|
||||||
|
/// <summary/>
|
||||||
|
Filter = 0x0001,
|
||||||
|
/// <summary/>
|
||||||
|
Finally = 0x0002,
|
||||||
|
/// <summary/>
|
||||||
|
Fault = 0x0004,
|
||||||
|
/// <summary/>
|
||||||
|
Duplicated = 0x0008,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods
|
||||||
|
/// </summary>
|
||||||
|
public static partial class Extensions {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// CIL opcode flow control
|
||||||
|
/// </summary>
|
||||||
|
public enum FlowControl {
|
||||||
|
/// <summary/>
|
||||||
|
Branch,
|
||||||
|
/// <summary/>
|
||||||
|
Break,
|
||||||
|
/// <summary/>
|
||||||
|
Call,
|
||||||
|
/// <summary/>
|
||||||
|
Cond_Branch,
|
||||||
|
/// <summary/>
|
||||||
|
Meta,
|
||||||
|
/// <summary/>
|
||||||
|
Next,
|
||||||
|
/// <summary/>
|
||||||
|
Phi,
|
||||||
|
/// <summary/>
|
||||||
|
Return,
|
||||||
|
/// <summary/>
|
||||||
|
Throw,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,827 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using dnlib.DotNet.Pdb;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// A CIL instruction (opcode + operand)
|
||||||
|
/// </summary>
|
||||||
|
public sealed class Instruction {
|
||||||
|
/// <summary>
|
||||||
|
/// The opcode
|
||||||
|
/// </summary>
|
||||||
|
public OpCode OpCode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The opcode operand
|
||||||
|
/// </summary>
|
||||||
|
public object Operand;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Offset of the instruction in the method body
|
||||||
|
/// </summary>
|
||||||
|
public uint Offset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PDB sequence point or <c>null</c> if none
|
||||||
|
/// </summary>
|
||||||
|
public SequencePoint SequencePoint;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public Instruction() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">Opcode</param>
|
||||||
|
public Instruction(OpCode opCode) => OpCode = opCode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">Opcode</param>
|
||||||
|
/// <param name="operand">The operand</param>
|
||||||
|
public Instruction(OpCode opCode, object operand) {
|
||||||
|
OpCode = opCode;
|
||||||
|
Operand = operand;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with no operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode) {
|
||||||
|
if (opCode.OperandType != OperandType.InlineNone)
|
||||||
|
throw new ArgumentException("Must be a no-operand opcode", nameof(opCode));
|
||||||
|
return new Instruction(opCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a <see cref="byte"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="value">The value</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, byte value) {
|
||||||
|
if (opCode.Code != Code.Unaligned)
|
||||||
|
throw new ArgumentException("Opcode does not have a byte operand", nameof(opCode));
|
||||||
|
return new Instruction(opCode, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a <see cref="sbyte"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="value">The value</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, sbyte value) {
|
||||||
|
if (opCode.Code != Code.Ldc_I4_S)
|
||||||
|
throw new ArgumentException("Opcode does not have a sbyte operand", nameof(opCode));
|
||||||
|
return new Instruction(opCode, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with an <see cref="int"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="value">The value</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, int value) {
|
||||||
|
if (opCode.OperandType != OperandType.InlineI)
|
||||||
|
throw new ArgumentException("Opcode does not have an int32 operand", nameof(opCode));
|
||||||
|
return new Instruction(opCode, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a <see cref="long"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="value">The value</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, long value) {
|
||||||
|
if (opCode.OperandType != OperandType.InlineI8)
|
||||||
|
throw new ArgumentException("Opcode does not have an int64 operand", nameof(opCode));
|
||||||
|
return new Instruction(opCode, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a <see cref="float"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="value">The value</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, float value) {
|
||||||
|
if (opCode.OperandType != OperandType.ShortInlineR)
|
||||||
|
throw new ArgumentException("Opcode does not have a real4 operand", nameof(opCode));
|
||||||
|
return new Instruction(opCode, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a <see cref="double"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="value">The value</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, double value) {
|
||||||
|
if (opCode.OperandType != OperandType.InlineR)
|
||||||
|
throw new ArgumentException("Opcode does not have a real8 operand", nameof(opCode));
|
||||||
|
return new Instruction(opCode, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a string operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="s">The string</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, string s) {
|
||||||
|
if (opCode.OperandType != OperandType.InlineString)
|
||||||
|
throw new ArgumentException("Opcode does not have a string operand", nameof(opCode));
|
||||||
|
return new Instruction(opCode, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with an instruction target operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="target">Target instruction</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, Instruction target) {
|
||||||
|
if (opCode.OperandType != OperandType.ShortInlineBrTarget && opCode.OperandType != OperandType.InlineBrTarget)
|
||||||
|
throw new ArgumentException("Opcode does not have an instruction operand", nameof(opCode));
|
||||||
|
return new Instruction(opCode, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with an instruction target list operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="targets">The targets</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, IList<Instruction> targets) {
|
||||||
|
if (opCode.OperandType != OperandType.InlineSwitch)
|
||||||
|
throw new ArgumentException("Opcode does not have a targets array operand", nameof(opCode));
|
||||||
|
return new Instruction(opCode, targets);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a type operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="type">The type</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, ITypeDefOrRef type) {
|
||||||
|
if (opCode.OperandType != OperandType.InlineType && opCode.OperandType != OperandType.InlineTok)
|
||||||
|
throw new ArgumentException("Opcode does not have a type operand", nameof(opCode));
|
||||||
|
return new Instruction(opCode, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a type operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="type">The type</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, CorLibTypeSig type) => Create(opCode, type.TypeDefOrRef);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a method/field operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="mr">The method/field</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, MemberRef mr) {
|
||||||
|
if (opCode.OperandType != OperandType.InlineField && opCode.OperandType != OperandType.InlineMethod && opCode.OperandType != OperandType.InlineTok)
|
||||||
|
throw new ArgumentException("Opcode does not have a field operand", nameof(opCode));
|
||||||
|
return new Instruction(opCode, mr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a field operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="field">The field</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, IField field) {
|
||||||
|
if (opCode.OperandType != OperandType.InlineField && opCode.OperandType != OperandType.InlineTok)
|
||||||
|
throw new ArgumentException("Opcode does not have a field operand", nameof(opCode));
|
||||||
|
return new Instruction(opCode, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a method operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="method">The method</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, IMethod method) {
|
||||||
|
if (opCode.OperandType != OperandType.InlineMethod && opCode.OperandType != OperandType.InlineTok)
|
||||||
|
throw new ArgumentException("Opcode does not have a method operand", nameof(opCode));
|
||||||
|
return new Instruction(opCode, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a token operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="token">The token</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, ITokenOperand token) {
|
||||||
|
if (opCode.OperandType != OperandType.InlineTok)
|
||||||
|
throw new ArgumentException("Opcode does not have a token operand", nameof(opCode));
|
||||||
|
return new Instruction(opCode, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a method signature operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="methodSig">The method signature</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, MethodSig methodSig) {
|
||||||
|
if (opCode.OperandType != OperandType.InlineSig)
|
||||||
|
throw new ArgumentException("Opcode does not have a method sig operand", nameof(opCode));
|
||||||
|
return new Instruction(opCode, methodSig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a method parameter operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="parameter">The method parameter</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, Parameter parameter) {
|
||||||
|
if (opCode.OperandType != OperandType.ShortInlineVar && opCode.OperandType != OperandType.InlineVar)
|
||||||
|
throw new ArgumentException("Opcode does not have a method parameter operand", nameof(opCode));
|
||||||
|
return new Instruction(opCode, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a method local operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opCode">The opcode</param>
|
||||||
|
/// <param name="local">The method local</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction Create(OpCode opCode, Local local) {
|
||||||
|
if (opCode.OperandType != OperandType.ShortInlineVar && opCode.OperandType != OperandType.InlineVar)
|
||||||
|
throw new ArgumentException("Opcode does not have a method local operand", nameof(opCode));
|
||||||
|
return new Instruction(opCode, local);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <c>ldci4</c> instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Operand value</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public static Instruction CreateLdcI4(int value) {
|
||||||
|
switch (value) {
|
||||||
|
case -1:return OpCodes.Ldc_I4_M1.ToInstruction();
|
||||||
|
case 0: return OpCodes.Ldc_I4_0.ToInstruction();
|
||||||
|
case 1: return OpCodes.Ldc_I4_1.ToInstruction();
|
||||||
|
case 2: return OpCodes.Ldc_I4_2.ToInstruction();
|
||||||
|
case 3: return OpCodes.Ldc_I4_3.ToInstruction();
|
||||||
|
case 4: return OpCodes.Ldc_I4_4.ToInstruction();
|
||||||
|
case 5: return OpCodes.Ldc_I4_5.ToInstruction();
|
||||||
|
case 6: return OpCodes.Ldc_I4_6.ToInstruction();
|
||||||
|
case 7: return OpCodes.Ldc_I4_7.ToInstruction();
|
||||||
|
case 8: return OpCodes.Ldc_I4_8.ToInstruction();
|
||||||
|
}
|
||||||
|
if (sbyte.MinValue <= value && value <= sbyte.MaxValue)
|
||||||
|
return new Instruction(OpCodes.Ldc_I4_S, (sbyte)value);
|
||||||
|
return new Instruction(OpCodes.Ldc_I4, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size in bytes of the instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int GetSize() {
|
||||||
|
var opCode = OpCode;
|
||||||
|
switch (opCode.OperandType) {
|
||||||
|
case OperandType.InlineBrTarget:
|
||||||
|
case OperandType.InlineField:
|
||||||
|
case OperandType.InlineI:
|
||||||
|
case OperandType.InlineMethod:
|
||||||
|
case OperandType.InlineSig:
|
||||||
|
case OperandType.InlineString:
|
||||||
|
case OperandType.InlineTok:
|
||||||
|
case OperandType.InlineType:
|
||||||
|
case OperandType.ShortInlineR:
|
||||||
|
return opCode.Size + 4;
|
||||||
|
|
||||||
|
case OperandType.InlineI8:
|
||||||
|
case OperandType.InlineR:
|
||||||
|
return opCode.Size + 8;
|
||||||
|
|
||||||
|
case OperandType.InlineNone:
|
||||||
|
case OperandType.InlinePhi:
|
||||||
|
default:
|
||||||
|
return opCode.Size;
|
||||||
|
|
||||||
|
case OperandType.InlineSwitch:
|
||||||
|
var targets = Operand as IList<Instruction>;
|
||||||
|
return opCode.Size + 4 + (targets is null ? 0 : targets.Count * 4);
|
||||||
|
|
||||||
|
case OperandType.InlineVar:
|
||||||
|
return opCode.Size + 2;
|
||||||
|
|
||||||
|
case OperandType.ShortInlineBrTarget:
|
||||||
|
case OperandType.ShortInlineI:
|
||||||
|
case OperandType.ShortInlineVar:
|
||||||
|
return opCode.Size + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsSystemVoid(TypeSig type) => type.RemovePinnedAndModifiers().GetElementType() == ElementType.Void;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates <paramref name="stack"/> with the new stack size
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stack">Current stack size</param>
|
||||||
|
public void UpdateStack(ref int stack) => UpdateStack(ref stack, false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates <paramref name="stack"/> with the new stack size
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stack">Current stack size</param>
|
||||||
|
/// <param name="methodHasReturnValue"><c>true</c> if the method has a return value,
|
||||||
|
/// <c>false</c> otherwise</param>
|
||||||
|
public void UpdateStack(ref int stack, bool methodHasReturnValue) {
|
||||||
|
CalculateStackUsage(methodHasReturnValue, out int pushes, out int pops);
|
||||||
|
if (pops == -1)
|
||||||
|
stack = 0;
|
||||||
|
else
|
||||||
|
stack += pushes - pops;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates stack usage
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pushes">Updated with number of stack pushes</param>
|
||||||
|
/// <param name="pops">Updated with number of stack pops or <c>-1</c> if the stack should
|
||||||
|
/// be cleared.</param>
|
||||||
|
public void CalculateStackUsage(out int pushes, out int pops) => CalculateStackUsage(false, out pushes, out pops);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates stack usage
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="methodHasReturnValue"><c>true</c> if method has a return value</param>
|
||||||
|
/// <param name="pushes">Updated with number of stack pushes</param>
|
||||||
|
/// <param name="pops">Updated with number of stack pops or <c>-1</c> if the stack should
|
||||||
|
/// be cleared.</param>
|
||||||
|
public void CalculateStackUsage(bool methodHasReturnValue, out int pushes, out int pops) {
|
||||||
|
var opCode = OpCode;
|
||||||
|
if (opCode.FlowControl == FlowControl.Call)
|
||||||
|
CalculateStackUsageCall(opCode.Code, out pushes, out pops);
|
||||||
|
else
|
||||||
|
CalculateStackUsageNonCall(opCode, methodHasReturnValue, out pushes, out pops);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CalculateStackUsageCall(Code code, out int pushes, out int pops) {
|
||||||
|
pushes = 0;
|
||||||
|
pops = 0;
|
||||||
|
|
||||||
|
// It doesn't push or pop anything. The stack should be empty when JMP is executed.
|
||||||
|
if (code == Code.Jmp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
MethodSig sig;
|
||||||
|
var op = Operand;
|
||||||
|
if (op is IMethod method)
|
||||||
|
sig = method.MethodSig;
|
||||||
|
else
|
||||||
|
sig = op as MethodSig; // calli instruction
|
||||||
|
if (sig is null)
|
||||||
|
return;
|
||||||
|
bool implicitThis = sig.ImplicitThis;
|
||||||
|
if (!IsSystemVoid(sig.RetType) || (code == Code.Newobj && sig.HasThis))
|
||||||
|
pushes++;
|
||||||
|
|
||||||
|
pops += sig.Params.Count;
|
||||||
|
var paramsAfterSentinel = sig.ParamsAfterSentinel;
|
||||||
|
if (paramsAfterSentinel is not null)
|
||||||
|
pops += paramsAfterSentinel.Count;
|
||||||
|
if (implicitThis && code != Code.Newobj)
|
||||||
|
pops++;
|
||||||
|
if (code == Code.Calli)
|
||||||
|
pops++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CalculateStackUsageNonCall(OpCode opCode, bool hasReturnValue, out int pushes, out int pops) {
|
||||||
|
switch (opCode.StackBehaviourPush) {
|
||||||
|
case StackBehaviour.Push0:
|
||||||
|
pushes = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case StackBehaviour.Push1:
|
||||||
|
case StackBehaviour.Pushi:
|
||||||
|
case StackBehaviour.Pushi8:
|
||||||
|
case StackBehaviour.Pushr4:
|
||||||
|
case StackBehaviour.Pushr8:
|
||||||
|
case StackBehaviour.Pushref:
|
||||||
|
pushes = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case StackBehaviour.Push1_push1:
|
||||||
|
pushes = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case StackBehaviour.Varpush: // only call, calli, callvirt which are handled elsewhere
|
||||||
|
default:
|
||||||
|
pushes = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (opCode.StackBehaviourPop) {
|
||||||
|
case StackBehaviour.Pop0:
|
||||||
|
pops = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case StackBehaviour.Pop1:
|
||||||
|
case StackBehaviour.Popi:
|
||||||
|
case StackBehaviour.Popref:
|
||||||
|
pops = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case StackBehaviour.Pop1_pop1:
|
||||||
|
case StackBehaviour.Popi_pop1:
|
||||||
|
case StackBehaviour.Popi_popi:
|
||||||
|
case StackBehaviour.Popi_popi8:
|
||||||
|
case StackBehaviour.Popi_popr4:
|
||||||
|
case StackBehaviour.Popi_popr8:
|
||||||
|
case StackBehaviour.Popref_pop1:
|
||||||
|
case StackBehaviour.Popref_popi:
|
||||||
|
pops = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case StackBehaviour.Popi_popi_popi:
|
||||||
|
case StackBehaviour.Popref_popi_popi:
|
||||||
|
case StackBehaviour.Popref_popi_popi8:
|
||||||
|
case StackBehaviour.Popref_popi_popr4:
|
||||||
|
case StackBehaviour.Popref_popi_popr8:
|
||||||
|
case StackBehaviour.Popref_popi_popref:
|
||||||
|
case StackBehaviour.Popref_popi_pop1:
|
||||||
|
pops = 3;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case StackBehaviour.PopAll:
|
||||||
|
pops = -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case StackBehaviour.Varpop: // call, calli, callvirt, newobj (all handled elsewhere), and ret
|
||||||
|
if (hasReturnValue)
|
||||||
|
pops = 1;
|
||||||
|
else
|
||||||
|
pops = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
pops = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether it's one of the <c>leave</c> instructions
|
||||||
|
/// </summary>
|
||||||
|
public bool IsLeave() => OpCode == OpCodes.Leave || OpCode == OpCodes.Leave_S;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether it's one of the <c>br</c> instructions
|
||||||
|
/// </summary>
|
||||||
|
public bool IsBr() => OpCode == OpCodes.Br || OpCode == OpCodes.Br_S;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether it's one of the <c>brfalse</c> instructions
|
||||||
|
/// </summary>
|
||||||
|
public bool IsBrfalse() => OpCode == OpCodes.Brfalse || OpCode == OpCodes.Brfalse_S;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether it's one of the <c>brtrue</c> instructions
|
||||||
|
/// </summary>
|
||||||
|
public bool IsBrtrue() => OpCode == OpCodes.Brtrue || OpCode == OpCodes.Brtrue_S;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether it's one of the conditional branch instructions (bcc, brtrue, brfalse)
|
||||||
|
/// </summary>
|
||||||
|
public bool IsConditionalBranch() {
|
||||||
|
switch (OpCode.Code) {
|
||||||
|
case Code.Bge:
|
||||||
|
case Code.Bge_S:
|
||||||
|
case Code.Bge_Un:
|
||||||
|
case Code.Bge_Un_S:
|
||||||
|
case Code.Blt:
|
||||||
|
case Code.Blt_S:
|
||||||
|
case Code.Blt_Un:
|
||||||
|
case Code.Blt_Un_S:
|
||||||
|
case Code.Bgt:
|
||||||
|
case Code.Bgt_S:
|
||||||
|
case Code.Bgt_Un:
|
||||||
|
case Code.Bgt_Un_S:
|
||||||
|
case Code.Ble:
|
||||||
|
case Code.Ble_S:
|
||||||
|
case Code.Ble_Un:
|
||||||
|
case Code.Ble_Un_S:
|
||||||
|
case Code.Brfalse:
|
||||||
|
case Code.Brfalse_S:
|
||||||
|
case Code.Brtrue:
|
||||||
|
case Code.Brtrue_S:
|
||||||
|
case Code.Beq:
|
||||||
|
case Code.Beq_S:
|
||||||
|
case Code.Bne_Un:
|
||||||
|
case Code.Bne_Un_S:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether this is one of the <c>ldc.i4</c> instructions
|
||||||
|
/// </summary>
|
||||||
|
public bool IsLdcI4() {
|
||||||
|
switch (OpCode.Code) {
|
||||||
|
case Code.Ldc_I4_M1:
|
||||||
|
case Code.Ldc_I4_0:
|
||||||
|
case Code.Ldc_I4_1:
|
||||||
|
case Code.Ldc_I4_2:
|
||||||
|
case Code.Ldc_I4_3:
|
||||||
|
case Code.Ldc_I4_4:
|
||||||
|
case Code.Ldc_I4_5:
|
||||||
|
case Code.Ldc_I4_6:
|
||||||
|
case Code.Ldc_I4_7:
|
||||||
|
case Code.Ldc_I4_8:
|
||||||
|
case Code.Ldc_I4_S:
|
||||||
|
case Code.Ldc_I4:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a <c>ldc.i4</c> instruction's operand
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The integer value</returns>
|
||||||
|
/// <exception cref="InvalidOperationException"><see cref="OpCode"/> isn't one of the
|
||||||
|
/// <c>ldc.i4</c> opcodes</exception>
|
||||||
|
public int GetLdcI4Value() =>
|
||||||
|
OpCode.Code switch {
|
||||||
|
Code.Ldc_I4_M1 => -1,
|
||||||
|
Code.Ldc_I4_0 => 0,
|
||||||
|
Code.Ldc_I4_1 => 1,
|
||||||
|
Code.Ldc_I4_2 => 2,
|
||||||
|
Code.Ldc_I4_3 => 3,
|
||||||
|
Code.Ldc_I4_4 => 4,
|
||||||
|
Code.Ldc_I4_5 => 5,
|
||||||
|
Code.Ldc_I4_6 => 6,
|
||||||
|
Code.Ldc_I4_7 => 7,
|
||||||
|
Code.Ldc_I4_8 => 8,
|
||||||
|
Code.Ldc_I4_S => (sbyte)Operand,
|
||||||
|
Code.Ldc_I4 => (int)Operand,
|
||||||
|
_ => throw new InvalidOperationException($"Not a ldc.i4 instruction: {this}"),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether it's one of the <c>ldarg</c> instructions, but does <c>not</c> check
|
||||||
|
/// whether it's one of the <c>ldarga</c> instructions.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsLdarg() {
|
||||||
|
switch (OpCode.Code) {
|
||||||
|
case Code.Ldarg:
|
||||||
|
case Code.Ldarg_S:
|
||||||
|
case Code.Ldarg_0:
|
||||||
|
case Code.Ldarg_1:
|
||||||
|
case Code.Ldarg_2:
|
||||||
|
case Code.Ldarg_3:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether it's one of the <c>ldloc</c> instructions, but does <c>not</c> check
|
||||||
|
/// whether it's one of the <c>ldloca</c> instructions.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsLdloc() {
|
||||||
|
switch (OpCode.Code) {
|
||||||
|
case Code.Ldloc:
|
||||||
|
case Code.Ldloc_0:
|
||||||
|
case Code.Ldloc_1:
|
||||||
|
case Code.Ldloc_2:
|
||||||
|
case Code.Ldloc_3:
|
||||||
|
case Code.Ldloc_S:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether it's one of the <c>starg</c> instructions
|
||||||
|
/// </summary>
|
||||||
|
public bool IsStarg() {
|
||||||
|
switch (OpCode.Code) {
|
||||||
|
case Code.Starg:
|
||||||
|
case Code.Starg_S:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether it's one of the <c>stloc</c> instructions
|
||||||
|
/// </summary>
|
||||||
|
public bool IsStloc() {
|
||||||
|
switch (OpCode.Code) {
|
||||||
|
case Code.Stloc:
|
||||||
|
case Code.Stloc_0:
|
||||||
|
case Code.Stloc_1:
|
||||||
|
case Code.Stloc_2:
|
||||||
|
case Code.Stloc_3:
|
||||||
|
case Code.Stloc_S:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the local if it's a <c>ldloc</c>, <c>stloc</c> or <c>ldloca</c> instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="locals">The locals</param>
|
||||||
|
/// <returns>The local or <c>null</c> if it's not a <c>ldloc</c>, <c>stloc</c> or <c>ldloca</c>
|
||||||
|
/// instruction or if the local doesn't exist.</returns>
|
||||||
|
public Local GetLocal(IList<Local> locals) {
|
||||||
|
int index;
|
||||||
|
var code = OpCode.Code;
|
||||||
|
switch (code) {
|
||||||
|
case Code.Ldloc:
|
||||||
|
case Code.Ldloc_S:
|
||||||
|
case Code.Stloc:
|
||||||
|
case Code.Stloc_S:
|
||||||
|
case Code.Ldloca:
|
||||||
|
case Code.Ldloca_S:
|
||||||
|
return Operand as Local;
|
||||||
|
|
||||||
|
case Code.Ldloc_0:
|
||||||
|
case Code.Ldloc_1:
|
||||||
|
case Code.Ldloc_2:
|
||||||
|
case Code.Ldloc_3:
|
||||||
|
index = code - Code.Ldloc_0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Stloc_0:
|
||||||
|
case Code.Stloc_1:
|
||||||
|
case Code.Stloc_2:
|
||||||
|
case Code.Stloc_3:
|
||||||
|
index = code - Code.Stloc_0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((uint)index < (uint)locals.Count)
|
||||||
|
return locals[index];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the index of the instruction's parameter operand or <c>-1</c> if the parameter
|
||||||
|
/// is missing or if it's not an instruction with a parameter operand.
|
||||||
|
/// </summary>
|
||||||
|
public int GetParameterIndex() {
|
||||||
|
switch (OpCode.Code) {
|
||||||
|
case Code.Ldarg_0: return 0;
|
||||||
|
case Code.Ldarg_1: return 1;
|
||||||
|
case Code.Ldarg_2: return 2;
|
||||||
|
case Code.Ldarg_3: return 3;
|
||||||
|
|
||||||
|
case Code.Starg:
|
||||||
|
case Code.Starg_S:
|
||||||
|
case Code.Ldarga:
|
||||||
|
case Code.Ldarga_S:
|
||||||
|
case Code.Ldarg:
|
||||||
|
case Code.Ldarg_S:
|
||||||
|
var parameter = Operand as Parameter;
|
||||||
|
if (parameter is not null)
|
||||||
|
return parameter.Index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a method parameter
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameters">All parameters</param>
|
||||||
|
/// <returns>A parameter or <c>null</c> if it doesn't exist</returns>
|
||||||
|
public Parameter GetParameter(IList<Parameter> parameters) {
|
||||||
|
int i = GetParameterIndex();
|
||||||
|
if ((uint)i < (uint)parameters.Count)
|
||||||
|
return parameters[i];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an argument type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="methodSig">Method signature</param>
|
||||||
|
/// <param name="declaringType">Declaring type (only needed if it's an instance method)</param>
|
||||||
|
/// <returns>The type or <c>null</c> if it doesn't exist</returns>
|
||||||
|
public TypeSig GetArgumentType(MethodSig methodSig, ITypeDefOrRef declaringType) {
|
||||||
|
if (methodSig is null)
|
||||||
|
return null;
|
||||||
|
int index = GetParameterIndex();
|
||||||
|
if (index == 0 && methodSig.ImplicitThis) {
|
||||||
|
if (declaringType is null)
|
||||||
|
return null;
|
||||||
|
TypeSig declSig;
|
||||||
|
bool isValueType;
|
||||||
|
if (declaringType is TypeSpec spec) {
|
||||||
|
declSig = spec.TypeSig;
|
||||||
|
isValueType = declSig.IsValueType;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Consistent with ParameterList.UpdateThisParameterType
|
||||||
|
var td = declaringType.ResolveTypeDef();
|
||||||
|
if (td is null)
|
||||||
|
return declaringType.ToTypeSig();
|
||||||
|
isValueType = td.IsValueType;
|
||||||
|
ClassOrValueTypeSig cvSig = isValueType ? new ValueTypeSig(td) : new ClassSig(td);
|
||||||
|
if (td.HasGenericParameters) {
|
||||||
|
int gpCount = td.GenericParameters.Count;
|
||||||
|
var genArgs = new List<TypeSig>(gpCount);
|
||||||
|
for (int i = 0; i < gpCount; i++)
|
||||||
|
genArgs.Add(new GenericVar(i, td));
|
||||||
|
declSig = new GenericInstSig(cvSig, genArgs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
declSig = cvSig;
|
||||||
|
}
|
||||||
|
return isValueType ? new ByRefSig(declSig) : declSig;
|
||||||
|
}
|
||||||
|
if (methodSig.ImplicitThis)
|
||||||
|
index--;
|
||||||
|
if ((uint)index < (uint)methodSig.Params.Count)
|
||||||
|
return methodSig.Params[index];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clone this instance. The <see cref="Operand"/> and <see cref="SequencePoint"/> fields
|
||||||
|
/// are shared by this instance and the created instance.
|
||||||
|
/// </summary>
|
||||||
|
public Instruction Clone() =>
|
||||||
|
new Instruction {
|
||||||
|
Offset = Offset,
|
||||||
|
OpCode = OpCode,
|
||||||
|
Operand = Operand,
|
||||||
|
SequencePoint = SequencePoint,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString() => InstructionPrinter.ToString(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static partial class Extensions {
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the opcode or <see cref="OpCodes.UNKNOWN1"/> if <paramref name="self"/> is <c>null</c>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static OpCode GetOpCode(this Instruction self) => self?.OpCode ?? OpCodes.UNKNOWN1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the operand or <c>null</c> if <paramref name="self"/> is <c>null</c>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static object GetOperand(this Instruction self) => self?.Operand;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the offset or 0 if <paramref name="self"/> is <c>null</c>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static uint GetOffset(this Instruction self) => self?.Offset ?? 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the sequence point or <c>null</c> if <paramref name="self"/> is <c>null</c>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static dnlib.DotNet.Pdb.SequencePoint GetSequencePoint(this Instruction self) => self?.SequencePoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,168 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// Converts instructions to strings
|
||||||
|
/// </summary>
|
||||||
|
public static class InstructionPrinter {
|
||||||
|
/// <summary>
|
||||||
|
/// Converts an instruction to a string
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The instruction</param>
|
||||||
|
/// <returns>The result</returns>
|
||||||
|
public static string ToString(Instruction instr) {
|
||||||
|
if (instr is null)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.Append($"IL_{instr.Offset:X4}: ");
|
||||||
|
sb.Append(instr.OpCode.Name);
|
||||||
|
AddOperandString(sb, instr, " ");
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the instruction's operand as a string
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The instruction</param>
|
||||||
|
/// <returns>The operand as a string</returns>
|
||||||
|
public static string GetOperandString(Instruction instr) {
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
AddOperandString(sb, instr, string.Empty);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add an instruction's operand to <paramref name="sb"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sb">Place result here</param>
|
||||||
|
/// <param name="instr">The instruction</param>
|
||||||
|
public static void AddOperandString(StringBuilder sb, Instruction instr) => AddOperandString(sb, instr, string.Empty);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add an instruction's operand to <paramref name="sb"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sb">Place result here</param>
|
||||||
|
/// <param name="instr">The instruction</param>
|
||||||
|
/// <param name="extra">A string that will be added before the operand, if there's
|
||||||
|
/// an operand.</param>
|
||||||
|
public static void AddOperandString(StringBuilder sb, Instruction instr, string extra) {
|
||||||
|
var op = instr.Operand;
|
||||||
|
switch (instr.OpCode.OperandType) {
|
||||||
|
case OperandType.InlineBrTarget:
|
||||||
|
case OperandType.ShortInlineBrTarget:
|
||||||
|
sb.Append(extra);
|
||||||
|
AddInstructionTarget(sb, op as Instruction);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OperandType.InlineField:
|
||||||
|
case OperandType.InlineMethod:
|
||||||
|
case OperandType.InlineTok:
|
||||||
|
case OperandType.InlineType:
|
||||||
|
sb.Append(extra);
|
||||||
|
if (op is IFullName)
|
||||||
|
sb.Append((op as IFullName).FullName);
|
||||||
|
else if (op is not null)
|
||||||
|
sb.Append(op.ToString());
|
||||||
|
else
|
||||||
|
sb.Append("null");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OperandType.InlineI:
|
||||||
|
case OperandType.InlineI8:
|
||||||
|
case OperandType.InlineR:
|
||||||
|
case OperandType.ShortInlineI:
|
||||||
|
case OperandType.ShortInlineR:
|
||||||
|
sb.Append($"{extra}{op}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OperandType.InlineSig:
|
||||||
|
sb.Append(extra);
|
||||||
|
FullNameFactory.MethodFullNameSB(null, (UTF8String)null, op as MethodSig, null, null, null, sb);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OperandType.InlineString:
|
||||||
|
sb.Append(extra);
|
||||||
|
EscapeString(sb, op as string, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OperandType.InlineSwitch:
|
||||||
|
var targets = op as IList<Instruction>;
|
||||||
|
if (targets is null)
|
||||||
|
sb.Append("null");
|
||||||
|
else {
|
||||||
|
sb.Append('(');
|
||||||
|
for (int i = 0; i < targets.Count; i++) {
|
||||||
|
if (i != 0)
|
||||||
|
sb.Append(',');
|
||||||
|
AddInstructionTarget(sb, targets[i]);
|
||||||
|
}
|
||||||
|
sb.Append(')');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OperandType.InlineVar:
|
||||||
|
case OperandType.ShortInlineVar:
|
||||||
|
sb.Append(extra);
|
||||||
|
if (op is null)
|
||||||
|
sb.Append("null");
|
||||||
|
else
|
||||||
|
sb.Append(op.ToString());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OperandType.InlineNone:
|
||||||
|
case OperandType.InlinePhi:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AddInstructionTarget(StringBuilder sb, Instruction targetInstr) {
|
||||||
|
if (targetInstr is null)
|
||||||
|
sb.Append("null");
|
||||||
|
else
|
||||||
|
sb.Append($"IL_{targetInstr.Offset:X4}");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EscapeString(StringBuilder sb, string s, bool addQuotes) {
|
||||||
|
if (s is null) {
|
||||||
|
sb.Append("null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addQuotes)
|
||||||
|
sb.Append('"');
|
||||||
|
|
||||||
|
foreach (var c in s) {
|
||||||
|
if ((int)c < 0x20) {
|
||||||
|
switch (c) {
|
||||||
|
case '\a': sb.Append(@"\a"); break;
|
||||||
|
case '\b': sb.Append(@"\b"); break;
|
||||||
|
case '\f': sb.Append(@"\f"); break;
|
||||||
|
case '\n': sb.Append(@"\n"); break;
|
||||||
|
case '\r': sb.Append(@"\r"); break;
|
||||||
|
case '\t': sb.Append(@"\t"); break;
|
||||||
|
case '\v': sb.Append(@"\v"); break;
|
||||||
|
default:
|
||||||
|
sb.Append($@"\u{(int)c:X4}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (c == '\\' || c == '"') {
|
||||||
|
sb.Append('\\');
|
||||||
|
sb.Append(c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
sb.Append(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addQuotes)
|
||||||
|
sb.Append('"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// Thrown when invalid data is detected while parsing a .NET method
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public class InvalidMethodException : Exception {
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public InvalidMethodException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="msg">Error message</param>
|
||||||
|
public InvalidMethodException(string msg)
|
||||||
|
: base(msg) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="msg">Error message</param>
|
||||||
|
/// <param name="innerException">The inner exception or <c>null</c> if none</param>
|
||||||
|
public InvalidMethodException(string msg, Exception innerException)
|
||||||
|
: base(msg, innerException) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info"></param>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
protected InvalidMethodException(SerializationInfo info, StreamingContext context)
|
||||||
|
: base(info, context) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,195 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using dnlib.Utils;
|
||||||
|
using dnlib.DotNet.Pdb;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// A collection of <see cref="Local"/>s
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("Count = {Count}")]
|
||||||
|
[DebuggerTypeProxy(typeof(LocalList_CollectionDebugView))]
|
||||||
|
public sealed class LocalList : IListListener<Local>, IList<Local> {
|
||||||
|
readonly LazyList<Local> locals;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the number of locals
|
||||||
|
/// </summary>
|
||||||
|
public int Count => locals.Count;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the list of locals
|
||||||
|
/// </summary>
|
||||||
|
public IList<Local> Locals => locals;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the N'th local
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">The local index</param>
|
||||||
|
public Local this[int index] {
|
||||||
|
get => locals[index];
|
||||||
|
set => locals[index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public LocalList() => locals = new LazyList<Local>(this);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="locals">All locals that will be owned by this instance</param>
|
||||||
|
public LocalList(IList<Local> locals) {
|
||||||
|
this.locals = new LazyList<Local>(this);
|
||||||
|
for (int i = 0; i < locals.Count; i++)
|
||||||
|
this.locals.Add(locals[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a new local and then returns it
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="local">The local that should be added to the list</param>
|
||||||
|
/// <returns>The input is always returned</returns>
|
||||||
|
public Local Add(Local local) {
|
||||||
|
locals.Add(local);
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
void IListListener<Local>.OnLazyAdd(int index, ref Local value) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
void IListListener<Local>.OnAdd(int index, Local value) => value.Index = index;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
void IListListener<Local>.OnRemove(int index, Local value) => value.Index = -1;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
void IListListener<Local>.OnResize(int index) {
|
||||||
|
for (int i = index; i < locals.Count_NoLock; i++)
|
||||||
|
locals.Get_NoLock(i).Index = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
void IListListener<Local>.OnClear() {
|
||||||
|
foreach (var local in locals.GetEnumerable_NoLock())
|
||||||
|
local.Index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int IndexOf(Local item) => locals.IndexOf(item);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Insert(int index, Local item) => locals.Insert(index, item);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void RemoveAt(int index) => locals.RemoveAt(index);
|
||||||
|
|
||||||
|
void ICollection<Local>.Add(Local item) => locals.Add(item);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Clear() => locals.Clear();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool Contains(Local item) => locals.Contains(item);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void CopyTo(Local[] array, int arrayIndex) => locals.CopyTo(array, arrayIndex);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool IsReadOnly => false;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool Remove(Local item) => locals.Remove(item);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public LazyList<Local>.Enumerator GetEnumerator() => locals.GetEnumerator();
|
||||||
|
IEnumerator<Local> IEnumerable<Local>.GetEnumerator() => locals.GetEnumerator();
|
||||||
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => ((IEnumerable<Local>)this).GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A method local
|
||||||
|
/// </summary>
|
||||||
|
public sealed class Local : IVariable {
|
||||||
|
TypeSig typeSig;
|
||||||
|
int index;
|
||||||
|
string name;
|
||||||
|
PdbLocalAttributes attributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the type of the local
|
||||||
|
/// </summary>
|
||||||
|
public TypeSig Type {
|
||||||
|
get => typeSig;
|
||||||
|
set => typeSig = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Local index
|
||||||
|
/// </summary>
|
||||||
|
public int Index {
|
||||||
|
get => index;
|
||||||
|
internal set => index = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name. This property is obsolete, use <see cref="PdbLocal"/> to get/set the name stored in the PDB file.
|
||||||
|
/// </summary>
|
||||||
|
public string Name {
|
||||||
|
get => name;
|
||||||
|
set => name = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the attributes. This property is obsolete, use <see cref="PdbLocal"/> to get/set the attributes stored in the PDB file.
|
||||||
|
/// </summary>
|
||||||
|
public PdbLocalAttributes Attributes {
|
||||||
|
get => attributes;
|
||||||
|
set => attributes = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SetName(string name) => this.name = name;
|
||||||
|
internal void SetAttributes(PdbLocalAttributes attributes) => this.attributes = attributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeSig">The type</param>
|
||||||
|
public Local(TypeSig typeSig) => this.typeSig = typeSig;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeSig">The type</param>
|
||||||
|
/// <param name="name">Name of local</param>
|
||||||
|
public Local(TypeSig typeSig, string name) {
|
||||||
|
this.typeSig = typeSig;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeSig">The type</param>
|
||||||
|
/// <param name="name">Name of local</param>
|
||||||
|
/// <param name="index">Index, should only be used if you don't add it to the locals list</param>
|
||||||
|
public Local(TypeSig typeSig, string name, int index) {
|
||||||
|
this.typeSig = typeSig;
|
||||||
|
this.name = name;
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString() {
|
||||||
|
var n = name;
|
||||||
|
if (string.IsNullOrEmpty(n))
|
||||||
|
return $"V_{Index}";
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,216 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using dnlib.DotNet.Pdb;
|
||||||
|
using dnlib.PE;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// Method body base class
|
||||||
|
/// </summary>
|
||||||
|
public abstract class MethodBody {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A native method body
|
||||||
|
/// </summary>
|
||||||
|
public sealed class NativeMethodBody : MethodBody {
|
||||||
|
RVA rva;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the RVA of the native method body
|
||||||
|
/// </summary>
|
||||||
|
public RVA RVA {
|
||||||
|
get => rva;
|
||||||
|
set => rva = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public NativeMethodBody() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rva">RVA of method body</param>
|
||||||
|
public NativeMethodBody(RVA rva) => this.rva = rva;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// CIL (managed code) body
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CilBody : MethodBody {
|
||||||
|
bool keepOldMaxStack;
|
||||||
|
bool initLocals;
|
||||||
|
byte headerSize;
|
||||||
|
ushort maxStack;
|
||||||
|
uint localVarSigTok;
|
||||||
|
readonly IList<Instruction> instructions;
|
||||||
|
readonly IList<ExceptionHandler> exceptionHandlers;
|
||||||
|
readonly LocalList localList;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Size of a small header
|
||||||
|
/// </summary>
|
||||||
|
public const byte SMALL_HEADER_SIZE = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets a flag indicating whether the original max stack value should be used.
|
||||||
|
/// </summary>
|
||||||
|
public bool KeepOldMaxStack {
|
||||||
|
get => keepOldMaxStack;
|
||||||
|
set => keepOldMaxStack = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the init locals flag. This is only valid if the method has any locals.
|
||||||
|
/// </summary>
|
||||||
|
public bool InitLocals {
|
||||||
|
get => initLocals;
|
||||||
|
set => initLocals = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the size in bytes of the method body header. The instructions immediately follow
|
||||||
|
/// the header.
|
||||||
|
/// </summary>
|
||||||
|
public byte HeaderSize {
|
||||||
|
get => headerSize;
|
||||||
|
set => headerSize = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if it was a small body header (<see cref="HeaderSize"/> is <c>1</c>)
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSmallHeader => headerSize == SMALL_HEADER_SIZE;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if it was a big body header
|
||||||
|
/// </summary>
|
||||||
|
public bool IsBigHeader => headerSize != SMALL_HEADER_SIZE;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets max stack value from the fat method header.
|
||||||
|
/// </summary>
|
||||||
|
public ushort MaxStack {
|
||||||
|
get => maxStack;
|
||||||
|
set => maxStack = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the locals metadata token
|
||||||
|
/// </summary>
|
||||||
|
public uint LocalVarSigTok {
|
||||||
|
get => localVarSigTok;
|
||||||
|
set => localVarSigTok = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="Instructions"/> is not empty
|
||||||
|
/// </summary>
|
||||||
|
public bool HasInstructions => instructions.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the instructions
|
||||||
|
/// </summary>
|
||||||
|
public IList<Instruction> Instructions => instructions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="ExceptionHandlers"/> is not empty
|
||||||
|
/// </summary>
|
||||||
|
public bool HasExceptionHandlers => exceptionHandlers.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the exception handlers
|
||||||
|
/// </summary>
|
||||||
|
public IList<ExceptionHandler> ExceptionHandlers => exceptionHandlers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="Variables"/> is not empty
|
||||||
|
/// </summary>
|
||||||
|
public bool HasVariables => localList.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the locals
|
||||||
|
/// </summary>
|
||||||
|
public LocalList Variables => localList;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the PDB method. This is <c>null</c> if no PDB has been loaded or if there's
|
||||||
|
/// no PDB info for this method.
|
||||||
|
/// </summary>
|
||||||
|
public PdbMethod PdbMethod {
|
||||||
|
get => pdbMethod;
|
||||||
|
set => pdbMethod = value;
|
||||||
|
}
|
||||||
|
PdbMethod pdbMethod;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="PdbMethod"/> is not <c>null</c>
|
||||||
|
/// </summary>
|
||||||
|
public bool HasPdbMethod => PdbMethod is not null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the total size of the body in the PE file, including header, IL bytes, and exception handlers.
|
||||||
|
/// This property returns 0 if the size is unknown.
|
||||||
|
/// </summary>
|
||||||
|
internal uint MetadataBodySize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public CilBody() {
|
||||||
|
initLocals = true;
|
||||||
|
instructions = new List<Instruction>();
|
||||||
|
exceptionHandlers = new List<ExceptionHandler>();
|
||||||
|
localList = new LocalList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="initLocals">Init locals flag</param>
|
||||||
|
/// <param name="instructions">All instructions. This instance will own the list.</param>
|
||||||
|
/// <param name="exceptionHandlers">All exception handlers. This instance will own the list.</param>
|
||||||
|
/// <param name="locals">All locals. This instance will own the locals in the list.</param>
|
||||||
|
public CilBody(bool initLocals, IList<Instruction> instructions, IList<ExceptionHandler> exceptionHandlers, IList<Local> locals) {
|
||||||
|
this.initLocals = initLocals;
|
||||||
|
this.instructions = instructions;
|
||||||
|
this.exceptionHandlers = exceptionHandlers;
|
||||||
|
localList = new LocalList(locals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shorter instructions are converted to the longer form, eg. <c>Ldc_I4_1</c> is
|
||||||
|
/// converted to <c>Ldc_I4</c> with a <c>1</c> as the operand.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameters">All method parameters, including the hidden 'this' parameter
|
||||||
|
/// if it's an instance method. Use <see cref="MethodDef.Parameters"/>.</param>
|
||||||
|
public void SimplifyMacros(IList<Parameter> parameters) => instructions.SimplifyMacros(localList, parameters);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optimizes instructions by using the shorter form if possible. Eg. <c>Ldc_I4</c> <c>1</c>
|
||||||
|
/// will be replaced with <c>Ldc_I4_1</c>.
|
||||||
|
/// </summary>
|
||||||
|
public void OptimizeMacros() => instructions.OptimizeMacros();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Short branch instructions are converted to the long form, eg. <c>Beq_S</c> is
|
||||||
|
/// converted to <c>Beq</c>.
|
||||||
|
/// </summary>
|
||||||
|
public void SimplifyBranches() => instructions.SimplifyBranches();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optimizes branches by using the smallest possible branch
|
||||||
|
/// </summary>
|
||||||
|
public void OptimizeBranches() => instructions.OptimizeBranches();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates each instruction's offset
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Total size in bytes of all instructions</returns>
|
||||||
|
public uint UpdateInstructionOffsets() => instructions.UpdateInstructionOffsets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,542 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using dnlib.IO;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// Reads strings from #US heap
|
||||||
|
/// </summary>
|
||||||
|
public interface IStringResolver {
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a string from the #US heap
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">String token</param>
|
||||||
|
/// <returns>A string</returns>
|
||||||
|
string ReadUserString(uint token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves instruction operands
|
||||||
|
/// </summary>
|
||||||
|
public interface IInstructionOperandResolver : ITokenResolver, IStringResolver {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static partial class Extensions {
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">An <see cref="IInstructionOperandResolver"/> object</param>
|
||||||
|
/// <param name="token">The metadata token</param>
|
||||||
|
/// <returns>A <see cref="IMDTokenProvider"/> or <c>null</c> if <paramref name="token"/> is invalid</returns>
|
||||||
|
public static IMDTokenProvider ResolveToken(this IInstructionOperandResolver self, uint token) =>
|
||||||
|
self.ResolveToken(token, new GenericParamContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a .NET method body (header, locals, instructions, exception handlers)
|
||||||
|
/// </summary>
|
||||||
|
public sealed class MethodBodyReader : MethodBodyReaderBase {
|
||||||
|
readonly IInstructionOperandResolver opResolver;
|
||||||
|
bool hasReadHeader;
|
||||||
|
byte headerSize;
|
||||||
|
ushort flags;
|
||||||
|
ushort maxStack;
|
||||||
|
uint codeSize;
|
||||||
|
uint localVarSigTok;
|
||||||
|
uint startOfHeader;
|
||||||
|
uint totalBodySize;
|
||||||
|
DataReader? exceptionsReader;
|
||||||
|
readonly GenericParamContext gpContext;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a CIL method body or returns an empty one if <paramref name="reader"/> doesn't
|
||||||
|
/// point to the start of a valid CIL method body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="reader">A reader positioned at the start of a .NET method body</param>
|
||||||
|
/// <param name="method">Use parameters from this method</param>
|
||||||
|
public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, DataReader reader, MethodDef method) =>
|
||||||
|
CreateCilBody(opResolver, reader, null, method.Parameters, new GenericParamContext());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a CIL method body or returns an empty one if <paramref name="reader"/> doesn't
|
||||||
|
/// point to the start of a valid CIL method body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="reader">A reader positioned at the start of a .NET method body</param>
|
||||||
|
/// <param name="method">Use parameters from this method</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, DataReader reader, MethodDef method, GenericParamContext gpContext) =>
|
||||||
|
CreateCilBody(opResolver, reader, null, method.Parameters, gpContext);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a CIL method body or returns an empty one if <paramref name="reader"/> doesn't
|
||||||
|
/// point to the start of a valid CIL method body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="reader">A reader positioned at the start of a .NET method body</param>
|
||||||
|
/// <param name="parameters">Method parameters</param>
|
||||||
|
public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, DataReader reader, IList<Parameter> parameters) =>
|
||||||
|
CreateCilBody(opResolver, reader, null, parameters, new GenericParamContext());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a CIL method body or returns an empty one if <paramref name="reader"/> doesn't
|
||||||
|
/// point to the start of a valid CIL method body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="reader">A reader positioned at the start of a .NET method body</param>
|
||||||
|
/// <param name="parameters">Method parameters</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, DataReader reader, IList<Parameter> parameters, GenericParamContext gpContext) =>
|
||||||
|
CreateCilBody(opResolver, reader, null, parameters, gpContext);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a CIL method body or returns an empty one if <paramref name="reader"/> doesn't
|
||||||
|
/// point to the start of a valid CIL method body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="reader">A reader positioned at the start of a .NET method body</param>
|
||||||
|
/// <param name="parameters">Method parameters</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
/// <param name="context">The module context</param>
|
||||||
|
public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, DataReader reader, IList<Parameter> parameters, GenericParamContext gpContext, ModuleContext context) =>
|
||||||
|
CreateCilBody(opResolver, reader, null, parameters, gpContext, context);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a CIL method body or returns an empty one if <paramref name="code"/> is not
|
||||||
|
/// a valid CIL method body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="code">All code</param>
|
||||||
|
/// <param name="exceptions">Exceptions or <c>null</c> if all exception handlers are in
|
||||||
|
/// <paramref name="code"/></param>
|
||||||
|
/// <param name="parameters">Method parameters</param>
|
||||||
|
public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte[] code, byte[] exceptions, IList<Parameter> parameters) =>
|
||||||
|
CreateCilBody(opResolver, ByteArrayDataReaderFactory.CreateReader(code), exceptions is null ? (DataReader?)null : ByteArrayDataReaderFactory.CreateReader(exceptions), parameters, new GenericParamContext());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a CIL method body or returns an empty one if <paramref name="code"/> is not
|
||||||
|
/// a valid CIL method body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="code">All code</param>
|
||||||
|
/// <param name="exceptions">Exceptions or <c>null</c> if all exception handlers are in
|
||||||
|
/// <paramref name="code"/></param>
|
||||||
|
/// <param name="parameters">Method parameters</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte[] code, byte[] exceptions, IList<Parameter> parameters, GenericParamContext gpContext) =>
|
||||||
|
CreateCilBody(opResolver, ByteArrayDataReaderFactory.CreateReader(code), exceptions is null ? (DataReader?)null : ByteArrayDataReaderFactory.CreateReader(exceptions), parameters, gpContext);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a CIL method body or returns an empty one if <paramref name="codeReader"/> doesn't
|
||||||
|
/// point to the start of a valid CIL method body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="codeReader">A reader positioned at the start of a .NET method body</param>
|
||||||
|
/// <param name="ehReader">Exception handler reader or <c>null</c> if exceptions aren't
|
||||||
|
/// present or if <paramref name="codeReader"/> contains the exception handlers</param>
|
||||||
|
/// <param name="parameters">Method parameters</param>
|
||||||
|
public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, DataReader codeReader, DataReader? ehReader, IList<Parameter> parameters) =>
|
||||||
|
CreateCilBody(opResolver, codeReader, ehReader, parameters, new GenericParamContext());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a CIL method body or returns an empty one if <paramref name="codeReader"/> doesn't
|
||||||
|
/// point to the start of a valid CIL method body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="codeReader">A reader positioned at the start of a .NET method body</param>
|
||||||
|
/// <param name="ehReader">Exception handler reader or <c>null</c> if exceptions aren't
|
||||||
|
/// present or if <paramref name="codeReader"/> contains the exception handlers</param>
|
||||||
|
/// <param name="parameters">Method parameters</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, DataReader codeReader, DataReader? ehReader, IList<Parameter> parameters, GenericParamContext gpContext) =>
|
||||||
|
CreateCilBody(opResolver, codeReader, ehReader, parameters, gpContext, null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a CIL method body or returns an empty one if <paramref name="codeReader"/> doesn't
|
||||||
|
/// point to the start of a valid CIL method body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="codeReader">A reader positioned at the start of a .NET method body</param>
|
||||||
|
/// <param name="ehReader">Exception handler reader or <c>null</c> if exceptions aren't
|
||||||
|
/// present or if <paramref name="codeReader"/> contains the exception handlers</param>
|
||||||
|
/// <param name="parameters">Method parameters</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
/// <param name="context">The module context</param>
|
||||||
|
public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, DataReader codeReader, DataReader? ehReader, IList<Parameter> parameters, GenericParamContext gpContext, ModuleContext context) {
|
||||||
|
var mbReader = new MethodBodyReader(opResolver, codeReader, ehReader, parameters, gpContext, context);
|
||||||
|
if (!mbReader.Read())
|
||||||
|
return new CilBody();
|
||||||
|
return mbReader.CreateCilBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a CIL method body or returns an empty one if <paramref name="code"/> is not
|
||||||
|
/// a valid CIL method body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="code">All code</param>
|
||||||
|
/// <param name="exceptions">Exceptions or <c>null</c> if all exception handlers are in
|
||||||
|
/// <paramref name="code"/></param>
|
||||||
|
/// <param name="parameters">Method parameters</param>
|
||||||
|
/// <param name="flags">Method header flags, eg. 2 if tiny method</param>
|
||||||
|
/// <param name="maxStack">Max stack</param>
|
||||||
|
/// <param name="codeSize">Code size</param>
|
||||||
|
/// <param name="localVarSigTok">Local variable signature token or 0 if none</param>
|
||||||
|
public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte[] code, byte[] exceptions, IList<Parameter> parameters, ushort flags, ushort maxStack, uint codeSize, uint localVarSigTok) =>
|
||||||
|
CreateCilBody(opResolver, code, exceptions, parameters, flags, maxStack, codeSize, localVarSigTok, new GenericParamContext());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a CIL method body or returns an empty one if <paramref name="code"/> is not
|
||||||
|
/// a valid CIL method body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="code">All code</param>
|
||||||
|
/// <param name="exceptions">Exceptions or <c>null</c> if all exception handlers are in
|
||||||
|
/// <paramref name="code"/></param>
|
||||||
|
/// <param name="parameters">Method parameters</param>
|
||||||
|
/// <param name="flags">Method header flags, eg. 2 if tiny method</param>
|
||||||
|
/// <param name="maxStack">Max stack</param>
|
||||||
|
/// <param name="codeSize">Code size</param>
|
||||||
|
/// <param name="localVarSigTok">Local variable signature token or 0 if none</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte[] code, byte[] exceptions, IList<Parameter> parameters, ushort flags, ushort maxStack, uint codeSize, uint localVarSigTok, GenericParamContext gpContext) =>
|
||||||
|
CreateCilBody(opResolver, code, exceptions, parameters, flags, maxStack, codeSize, localVarSigTok, gpContext, null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a CIL method body or returns an empty one if <paramref name="code"/> is not
|
||||||
|
/// a valid CIL method body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="code">All code</param>
|
||||||
|
/// <param name="exceptions">Exceptions or <c>null</c> if all exception handlers are in
|
||||||
|
/// <paramref name="code"/></param>
|
||||||
|
/// <param name="parameters">Method parameters</param>
|
||||||
|
/// <param name="flags">Method header flags, eg. 2 if tiny method</param>
|
||||||
|
/// <param name="maxStack">Max stack</param>
|
||||||
|
/// <param name="codeSize">Code size</param>
|
||||||
|
/// <param name="localVarSigTok">Local variable signature token or 0 if none</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
/// <param name="context">The module context</param>
|
||||||
|
public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte[] code, byte[] exceptions, IList<Parameter> parameters, ushort flags, ushort maxStack, uint codeSize, uint localVarSigTok, GenericParamContext gpContext, ModuleContext context) {
|
||||||
|
var codeReader = ByteArrayDataReaderFactory.CreateReader(code);
|
||||||
|
var ehReader = exceptions is null ? (DataReader?)null : ByteArrayDataReaderFactory.CreateReader(exceptions);
|
||||||
|
var mbReader = new MethodBodyReader(opResolver, codeReader, ehReader, parameters, gpContext, context);
|
||||||
|
mbReader.SetHeader(flags, maxStack, codeSize, localVarSigTok);
|
||||||
|
if (!mbReader.Read())
|
||||||
|
return new CilBody();
|
||||||
|
return mbReader.CreateCilBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="reader">A reader positioned at the start of a .NET method body</param>
|
||||||
|
/// <param name="method">Use parameters from this method</param>
|
||||||
|
public MethodBodyReader(IInstructionOperandResolver opResolver, DataReader reader, MethodDef method)
|
||||||
|
: this(opResolver, reader, null, method.Parameters, new GenericParamContext()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="reader">A reader positioned at the start of a .NET method body</param>
|
||||||
|
/// <param name="method">Use parameters from this method</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
public MethodBodyReader(IInstructionOperandResolver opResolver, DataReader reader, MethodDef method, GenericParamContext gpContext)
|
||||||
|
: this(opResolver, reader, null, method.Parameters, gpContext) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="reader">A reader positioned at the start of a .NET method body</param>
|
||||||
|
/// <param name="parameters">Method parameters</param>
|
||||||
|
public MethodBodyReader(IInstructionOperandResolver opResolver, DataReader reader, IList<Parameter> parameters)
|
||||||
|
: this(opResolver, reader, null, parameters, new GenericParamContext()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="reader">A reader positioned at the start of a .NET method body</param>
|
||||||
|
/// <param name="parameters">Method parameters</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
public MethodBodyReader(IInstructionOperandResolver opResolver, DataReader reader, IList<Parameter> parameters, GenericParamContext gpContext)
|
||||||
|
: this(opResolver, reader, null, parameters, gpContext) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="codeReader">A reader positioned at the start of a .NET method body</param>
|
||||||
|
/// <param name="ehReader">Exception handler reader or <c>null</c> if exceptions aren't
|
||||||
|
/// present or if <paramref name="codeReader"/> contains the exception handlers</param>
|
||||||
|
/// <param name="parameters">Method parameters</param>
|
||||||
|
public MethodBodyReader(IInstructionOperandResolver opResolver, DataReader codeReader, DataReader? ehReader, IList<Parameter> parameters)
|
||||||
|
: this(opResolver, codeReader, ehReader, parameters, new GenericParamContext()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="codeReader">A reader positioned at the start of a .NET method body</param>
|
||||||
|
/// <param name="ehReader">Exception handler reader or <c>null</c> if exceptions aren't
|
||||||
|
/// present or if <paramref name="codeReader"/> contains the exception handlers</param>
|
||||||
|
/// <param name="parameters">Method parameters</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
public MethodBodyReader(IInstructionOperandResolver opResolver, DataReader codeReader, DataReader? ehReader, IList<Parameter> parameters, GenericParamContext gpContext)
|
||||||
|
: this(opResolver, codeReader, ehReader, parameters, gpContext, null) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opResolver">The operand resolver</param>
|
||||||
|
/// <param name="codeReader">A reader positioned at the start of a .NET method body</param>
|
||||||
|
/// <param name="ehReader">Exception handler reader or <c>null</c> if exceptions aren't
|
||||||
|
/// present or if <paramref name="codeReader"/> contains the exception handlers</param>
|
||||||
|
/// <param name="parameters">Method parameters</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
/// <param name="context">Module context</param>
|
||||||
|
public MethodBodyReader(IInstructionOperandResolver opResolver, DataReader codeReader, DataReader? ehReader, IList<Parameter> parameters, GenericParamContext gpContext, ModuleContext context)
|
||||||
|
: base(codeReader, parameters, context) {
|
||||||
|
this.opResolver = opResolver;
|
||||||
|
exceptionsReader = ehReader;
|
||||||
|
this.gpContext = gpContext;
|
||||||
|
startOfHeader = uint.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the method header
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="flags">Header flags, eg. 2 if it's a tiny method</param>
|
||||||
|
/// <param name="maxStack">Max stack</param>
|
||||||
|
/// <param name="codeSize">Code size</param>
|
||||||
|
/// <param name="localVarSigTok">Local variable signature token</param>
|
||||||
|
void SetHeader(ushort flags, ushort maxStack, uint codeSize, uint localVarSigTok) {
|
||||||
|
hasReadHeader = true;
|
||||||
|
this.flags = flags;
|
||||||
|
this.maxStack = maxStack;
|
||||||
|
this.codeSize = codeSize;
|
||||||
|
this.localVarSigTok = localVarSigTok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the method body header, locals, all instructions, and the exception handlers (if any)
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><c>true</c> if it worked, and <c>false</c> if something failed</returns>
|
||||||
|
public bool Read() {
|
||||||
|
try {
|
||||||
|
if (!ReadHeader())
|
||||||
|
return false;
|
||||||
|
SetLocals(ReadLocals());
|
||||||
|
ReadInstructions();
|
||||||
|
ReadExceptionHandlers(out totalBodySize);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (InvalidMethodException) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (IOException) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the method header
|
||||||
|
/// </summary>
|
||||||
|
bool ReadHeader() {
|
||||||
|
if (hasReadHeader)
|
||||||
|
return true;
|
||||||
|
hasReadHeader = true;
|
||||||
|
|
||||||
|
startOfHeader = reader.Position;
|
||||||
|
byte b = reader.ReadByte();
|
||||||
|
switch (b & 7) {
|
||||||
|
case 2:
|
||||||
|
case 6:
|
||||||
|
// Tiny header. [7:2] = code size, max stack is 8, no locals or exception handlers
|
||||||
|
flags = 2;
|
||||||
|
maxStack = 8;
|
||||||
|
codeSize = (uint)(b >> 2);
|
||||||
|
localVarSigTok = 0;
|
||||||
|
headerSize = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
// Fat header. Can have locals and exception handlers
|
||||||
|
flags = (ushort)((reader.ReadByte() << 8) | b);
|
||||||
|
headerSize = (byte)(flags >> 12);
|
||||||
|
maxStack = reader.ReadUInt16();
|
||||||
|
codeSize = reader.ReadUInt32();
|
||||||
|
localVarSigTok = reader.ReadUInt32();
|
||||||
|
|
||||||
|
// The CLR allows the code to start inside the method header. But if it does,
|
||||||
|
// the CLR doesn't read any exceptions.
|
||||||
|
reader.Position = reader.Position - 12 + headerSize * 4U;
|
||||||
|
if (headerSize < 3)
|
||||||
|
flags &= 0xFFF7;
|
||||||
|
headerSize *= 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ulong)reader.Position + codeSize > reader.Length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the locals
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>All locals or <c>null</c> if there are none</returns>
|
||||||
|
IList<TypeSig> ReadLocals() {
|
||||||
|
var standAloneSig = opResolver.ResolveToken(localVarSigTok, gpContext) as StandAloneSig;
|
||||||
|
if (standAloneSig is null)
|
||||||
|
return null;
|
||||||
|
var localSig = standAloneSig.LocalSig;
|
||||||
|
if (localSig is null)
|
||||||
|
return null;
|
||||||
|
return localSig.Locals;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads all instructions
|
||||||
|
/// </summary>
|
||||||
|
void ReadInstructions() => ReadInstructionsNumBytes(codeSize);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override IField ReadInlineField(Instruction instr) => opResolver.ResolveToken(reader.ReadUInt32(), gpContext) as IField;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override IMethod ReadInlineMethod(Instruction instr) => opResolver.ResolveToken(reader.ReadUInt32(), gpContext) as IMethod;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override MethodSig ReadInlineSig(Instruction instr) {
|
||||||
|
var standAloneSig = opResolver.ResolveToken(reader.ReadUInt32(), gpContext) as StandAloneSig;
|
||||||
|
if (standAloneSig is null)
|
||||||
|
return null;
|
||||||
|
var sig = standAloneSig.MethodSig;
|
||||||
|
if (sig is not null)
|
||||||
|
sig.OriginalToken = standAloneSig.MDToken.Raw;
|
||||||
|
return sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override string ReadInlineString(Instruction instr) => opResolver.ReadUserString(reader.ReadUInt32()) ?? string.Empty;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override ITokenOperand ReadInlineTok(Instruction instr) => opResolver.ResolveToken(reader.ReadUInt32(), gpContext) as ITokenOperand;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override ITypeDefOrRef ReadInlineType(Instruction instr) => opResolver.ResolveToken(reader.ReadUInt32(), gpContext) as ITypeDefOrRef;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads all exception handlers
|
||||||
|
/// </summary>
|
||||||
|
void ReadExceptionHandlers(out uint totalBodySize) {
|
||||||
|
if ((flags & 8) == 0) {
|
||||||
|
totalBodySize = startOfHeader == uint.MaxValue ? 0 : reader.Position - startOfHeader;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool canSaveTotalBodySize;
|
||||||
|
DataReader ehReader;
|
||||||
|
if (exceptionsReader is not null) {
|
||||||
|
canSaveTotalBodySize = false;
|
||||||
|
ehReader = exceptionsReader.Value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
canSaveTotalBodySize = true;
|
||||||
|
ehReader = reader;
|
||||||
|
ehReader.Position = (ehReader.Position + 3) & ~3U;
|
||||||
|
}
|
||||||
|
// Only read the first one. Any others aren't used.
|
||||||
|
byte b = ehReader.ReadByte();
|
||||||
|
if ((b & 0x3F) != 1) {
|
||||||
|
totalBodySize = startOfHeader == uint.MaxValue ? 0 : reader.Position - startOfHeader;
|
||||||
|
return; // Not exception handler clauses
|
||||||
|
}
|
||||||
|
if ((b & 0x40) != 0)
|
||||||
|
ReadFatExceptionHandlers(ref ehReader);
|
||||||
|
else
|
||||||
|
ReadSmallExceptionHandlers(ref ehReader);
|
||||||
|
if (canSaveTotalBodySize)
|
||||||
|
totalBodySize = startOfHeader == uint.MaxValue ? 0 : ehReader.Position - startOfHeader;
|
||||||
|
else
|
||||||
|
totalBodySize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadFatExceptionHandlers(ref DataReader ehReader) {
|
||||||
|
ehReader.Position--;
|
||||||
|
int num = (int)((ehReader.ReadUInt32() >> 8) / 24);
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
var eh = new ExceptionHandler((ExceptionHandlerType)ehReader.ReadUInt32());
|
||||||
|
uint offs = ehReader.ReadUInt32();
|
||||||
|
eh.TryStart = GetInstruction(offs);
|
||||||
|
eh.TryEnd = GetInstruction(offs + ehReader.ReadUInt32());
|
||||||
|
offs = ehReader.ReadUInt32();
|
||||||
|
eh.HandlerStart = GetInstruction(offs);
|
||||||
|
eh.HandlerEnd = GetInstruction(offs + ehReader.ReadUInt32());
|
||||||
|
if (eh.IsCatch)
|
||||||
|
eh.CatchType = opResolver.ResolveToken(ehReader.ReadUInt32(), gpContext) as ITypeDefOrRef;
|
||||||
|
else if (eh.IsFilter)
|
||||||
|
eh.FilterStart = GetInstruction(ehReader.ReadUInt32());
|
||||||
|
else
|
||||||
|
ehReader.ReadUInt32();
|
||||||
|
Add(eh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadSmallExceptionHandlers(ref DataReader ehReader) {
|
||||||
|
int num = (int)((uint)ehReader.ReadByte() / 12);
|
||||||
|
ehReader.Position += 2;
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
var eh = new ExceptionHandler((ExceptionHandlerType)ehReader.ReadUInt16());
|
||||||
|
uint offs = ehReader.ReadUInt16();
|
||||||
|
eh.TryStart = GetInstruction(offs);
|
||||||
|
eh.TryEnd = GetInstruction(offs + ehReader.ReadByte());
|
||||||
|
offs = ehReader.ReadUInt16();
|
||||||
|
eh.HandlerStart = GetInstruction(offs);
|
||||||
|
eh.HandlerEnd = GetInstruction(offs + ehReader.ReadByte());
|
||||||
|
if (eh.IsCatch)
|
||||||
|
eh.CatchType = opResolver.ResolveToken(ehReader.ReadUInt32(), gpContext) as ITypeDefOrRef;
|
||||||
|
else if (eh.IsFilter)
|
||||||
|
eh.FilterStart = GetInstruction(ehReader.ReadUInt32());
|
||||||
|
else
|
||||||
|
ehReader.ReadUInt32();
|
||||||
|
Add(eh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a CIL body. Must be called after <see cref="Read()"/>, and can only be
|
||||||
|
/// called once.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A new <see cref="CilBody"/> instance</returns>
|
||||||
|
public CilBody CreateCilBody() {
|
||||||
|
// Set init locals if it's a tiny method or if the init locals bit is set (fat header)
|
||||||
|
bool initLocals = flags == 2 || (flags & 0x10) != 0;
|
||||||
|
var cilBody = new CilBody(initLocals, instructions, exceptionHandlers, locals);
|
||||||
|
cilBody.HeaderSize = headerSize;
|
||||||
|
cilBody.MaxStack = maxStack;
|
||||||
|
cilBody.LocalVarSigTok = localVarSigTok;
|
||||||
|
cilBody.MetadataBodySize = totalBodySize;
|
||||||
|
instructions = null;
|
||||||
|
exceptionHandlers = null;
|
||||||
|
locals = null;
|
||||||
|
return cilBody;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,587 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using dnlib.IO;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// Method body reader base class
|
||||||
|
/// </summary>
|
||||||
|
public abstract class MethodBodyReaderBase {
|
||||||
|
/// <summary>The method reader</summary>
|
||||||
|
protected DataReader reader;
|
||||||
|
/// <summary>All parameters</summary>
|
||||||
|
protected IList<Parameter> parameters;
|
||||||
|
/// <summary>All locals</summary>
|
||||||
|
protected IList<Local> locals = new List<Local>();
|
||||||
|
/// <summary>All instructions</summary>
|
||||||
|
protected IList<Instruction> instructions;
|
||||||
|
/// <summary>All exception handlers</summary>
|
||||||
|
protected IList<ExceptionHandler> exceptionHandlers = new List<ExceptionHandler>();
|
||||||
|
uint currentOffset;
|
||||||
|
/// <summary>First byte after the end of the code</summary>
|
||||||
|
protected uint codeEndOffs;
|
||||||
|
/// <summary>Start offset of method</summary>
|
||||||
|
protected uint codeStartOffs;
|
||||||
|
readonly ModuleContext context;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all parameters
|
||||||
|
/// </summary>
|
||||||
|
public IList<Parameter> Parameters => parameters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all locals
|
||||||
|
/// </summary>
|
||||||
|
public IList<Local> Locals => locals;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all instructions
|
||||||
|
/// </summary>
|
||||||
|
public IList<Instruction> Instructions => instructions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all exception handlers
|
||||||
|
/// </summary>
|
||||||
|
public IList<ExceptionHandler> ExceptionHandlers => exceptionHandlers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
protected MethodBodyReaderBase() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The module context</param>
|
||||||
|
protected MethodBodyReaderBase(ModuleContext context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The reader</param>
|
||||||
|
protected MethodBodyReaderBase(DataReader reader)
|
||||||
|
: this(reader, null) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The reader</param>
|
||||||
|
/// <param name="parameters">Method parameters or <c>null</c> if they're not known yet</param>
|
||||||
|
protected MethodBodyReaderBase(DataReader reader, IList<Parameter> parameters)
|
||||||
|
: this(reader, parameters, null) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The reader</param>
|
||||||
|
/// <param name="parameters">Method parameters or <c>null</c> if they're not known yet</param>
|
||||||
|
/// <param name="context">The module context</param>
|
||||||
|
protected MethodBodyReaderBase(DataReader reader, IList<Parameter> parameters, ModuleContext context) {
|
||||||
|
this.reader = reader;
|
||||||
|
this.parameters = parameters;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets new locals
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="newLocals">A list of types of all locals or <c>null</c> if none</param>
|
||||||
|
protected void SetLocals(IList<TypeSig> newLocals) {
|
||||||
|
var locals = this.locals;
|
||||||
|
locals.Clear();
|
||||||
|
if (newLocals is null)
|
||||||
|
return;
|
||||||
|
int count = newLocals.Count;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
locals.Add(new Local(newLocals[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets new locals
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="newLocals">A list of types of all locals or <c>null</c> if none</param>
|
||||||
|
protected void SetLocals(IList<Local> newLocals) {
|
||||||
|
var locals = this.locals;
|
||||||
|
locals.Clear();
|
||||||
|
if (newLocals is null)
|
||||||
|
return;
|
||||||
|
int count = newLocals.Count;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
locals.Add(new Local(newLocals[i].Type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads all instructions
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="numInstrs">Number of instructions to read</param>
|
||||||
|
protected void ReadInstructions(int numInstrs) {
|
||||||
|
codeStartOffs = reader.Position;
|
||||||
|
codeEndOffs = reader.Length; // We don't know the end pos so use the last one
|
||||||
|
this.instructions = new List<Instruction>(numInstrs);
|
||||||
|
currentOffset = 0;
|
||||||
|
var instructions = this.instructions;
|
||||||
|
for (int i = 0; i < numInstrs && reader.Position < codeEndOffs; i++)
|
||||||
|
instructions.Add(ReadOneInstruction());
|
||||||
|
FixBranches();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads all instructions
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="codeSize">Size of code</param>
|
||||||
|
protected void ReadInstructionsNumBytes(uint codeSize) {
|
||||||
|
codeStartOffs = reader.Position;
|
||||||
|
codeEndOffs = reader.Position + codeSize;
|
||||||
|
if (codeEndOffs < codeStartOffs || codeEndOffs > reader.Length)
|
||||||
|
throw new InvalidMethodException("Invalid code size");
|
||||||
|
|
||||||
|
this.instructions = new List<Instruction>(); //TODO: Estimate number of instructions based on codeSize
|
||||||
|
currentOffset = 0;
|
||||||
|
var instructions = this.instructions;
|
||||||
|
while (reader.Position < codeEndOffs)
|
||||||
|
instructions.Add(ReadOneInstruction());
|
||||||
|
reader.Position = codeEndOffs;
|
||||||
|
FixBranches();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fixes all branch instructions so their operands are set to an <see cref="Instruction"/>
|
||||||
|
/// instead of an offset.
|
||||||
|
/// </summary>
|
||||||
|
void FixBranches() {
|
||||||
|
var instructions = this.instructions;
|
||||||
|
int count = instructions.Count;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
var instr = instructions[i];
|
||||||
|
switch (instr.OpCode.OperandType) {
|
||||||
|
case OperandType.InlineBrTarget:
|
||||||
|
case OperandType.ShortInlineBrTarget:
|
||||||
|
instr.Operand = GetInstruction((uint)instr.Operand);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OperandType.InlineSwitch:
|
||||||
|
var uintTargets = (IList<uint>)instr.Operand;
|
||||||
|
var targets = new Instruction[uintTargets.Count];
|
||||||
|
for (int j = 0; j < uintTargets.Count; j++)
|
||||||
|
targets[j] = GetInstruction(uintTargets[j]);
|
||||||
|
instr.Operand = targets;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds an instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">Offset of instruction</param>
|
||||||
|
/// <returns>The instruction or <c>null</c> if there's no instruction at <paramref name="offset"/>.</returns>
|
||||||
|
protected Instruction GetInstruction(uint offset) {
|
||||||
|
// The instructions are sorted and all Offset fields are correct. Do a binary search.
|
||||||
|
var instructions = this.instructions;
|
||||||
|
int lo = 0, hi = instructions.Count - 1;
|
||||||
|
while (lo <= hi && hi != -1) {
|
||||||
|
int i = (lo + hi) / 2;
|
||||||
|
var instr = instructions[i];
|
||||||
|
if (instr.Offset == offset)
|
||||||
|
return instr;
|
||||||
|
if (offset < instr.Offset)
|
||||||
|
hi = i - 1;
|
||||||
|
else
|
||||||
|
lo = i + 1;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds an instruction and throws if it's not present
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">Offset of instruction</param>
|
||||||
|
/// <returns>The instruction</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">There's no instruction at
|
||||||
|
/// <paramref name="offset"/></exception>
|
||||||
|
protected Instruction GetInstructionThrow(uint offset) {
|
||||||
|
var instr = GetInstruction(offset);
|
||||||
|
if (instr is not null)
|
||||||
|
return instr;
|
||||||
|
throw new InvalidOperationException($"There's no instruction @ {offset:X4}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the next instruction
|
||||||
|
/// </summary>
|
||||||
|
Instruction ReadOneInstruction() {
|
||||||
|
var instr = new Instruction();
|
||||||
|
instr.Offset = currentOffset;
|
||||||
|
instr.OpCode = ReadOpCode();
|
||||||
|
instr.Operand = ReadOperand(instr);
|
||||||
|
|
||||||
|
if (instr.OpCode.Code == Code.Switch) {
|
||||||
|
var targets = (IList<uint>)instr.Operand;
|
||||||
|
currentOffset += (uint)(instr.OpCode.Size + 4 + 4 * targets.Count);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
currentOffset += (uint)instr.GetSize();
|
||||||
|
if (currentOffset < instr.Offset)
|
||||||
|
reader.Position = codeEndOffs;
|
||||||
|
return instr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the next OpCode from the current position
|
||||||
|
/// </summary>
|
||||||
|
OpCode ReadOpCode() {
|
||||||
|
var op = reader.ReadByte();
|
||||||
|
if (op == 0xFE)
|
||||||
|
return OpCodes.TwoByteOpCodes[reader.ReadByte()];
|
||||||
|
if (op >= 0xF0 && op <= 0xFB && context is not null && reader.BytesLeft >= 1) {
|
||||||
|
if (context.GetExperimentalOpCode(op, reader.ReadByte()) is OpCode opCode)
|
||||||
|
return opCode;
|
||||||
|
else
|
||||||
|
reader.Position--;
|
||||||
|
}
|
||||||
|
return OpCodes.OneByteOpCodes[op];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the instruction operand (if any)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The instruction</param>
|
||||||
|
object ReadOperand(Instruction instr) =>
|
||||||
|
instr.OpCode.OperandType switch {
|
||||||
|
OperandType.InlineBrTarget => ReadInlineBrTarget(instr),
|
||||||
|
OperandType.InlineField => ReadInlineField(instr),
|
||||||
|
OperandType.InlineI => ReadInlineI(instr),
|
||||||
|
OperandType.InlineI8 => ReadInlineI8(instr),
|
||||||
|
OperandType.InlineMethod => ReadInlineMethod(instr),
|
||||||
|
OperandType.InlineNone => ReadInlineNone(instr),
|
||||||
|
OperandType.InlinePhi => ReadInlinePhi(instr),
|
||||||
|
OperandType.InlineR => ReadInlineR(instr),
|
||||||
|
OperandType.InlineSig => ReadInlineSig(instr),
|
||||||
|
OperandType.InlineString => ReadInlineString(instr),
|
||||||
|
OperandType.InlineSwitch => ReadInlineSwitch(instr),
|
||||||
|
OperandType.InlineTok => ReadInlineTok(instr),
|
||||||
|
OperandType.InlineType => ReadInlineType(instr),
|
||||||
|
OperandType.InlineVar => ReadInlineVar(instr),
|
||||||
|
OperandType.ShortInlineBrTarget => ReadShortInlineBrTarget(instr),
|
||||||
|
OperandType.ShortInlineI => ReadShortInlineI(instr),
|
||||||
|
OperandType.ShortInlineR => ReadShortInlineR(instr),
|
||||||
|
OperandType.ShortInlineVar => ReadShortInlineVar(instr),
|
||||||
|
_ => throw new InvalidOperationException("Invalid OpCode.OperandType"),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.InlineBrTarget"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected virtual uint ReadInlineBrTarget(Instruction instr) => instr.Offset + (uint)instr.GetSize() + reader.ReadUInt32();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.InlineField"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected abstract IField ReadInlineField(Instruction instr);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.InlineI"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected virtual int ReadInlineI(Instruction instr) => reader.ReadInt32();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.InlineI8"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected virtual long ReadInlineI8(Instruction instr) => reader.ReadInt64();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.InlineMethod"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected abstract IMethod ReadInlineMethod(Instruction instr);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.InlineNone"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected virtual object ReadInlineNone(Instruction instr) => null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.InlinePhi"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected virtual object ReadInlinePhi(Instruction instr) => null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.InlineR"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected virtual double ReadInlineR(Instruction instr) => reader.ReadDouble();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.InlineSig"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected abstract MethodSig ReadInlineSig(Instruction instr);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.InlineString"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected abstract string ReadInlineString(Instruction instr);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.InlineSwitch"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected virtual IList<uint> ReadInlineSwitch(Instruction instr) {
|
||||||
|
var num = reader.ReadUInt32();
|
||||||
|
long offsetAfterInstr = (long)instr.Offset + (long)instr.OpCode.Size + 4L + (long)num * 4;
|
||||||
|
if (offsetAfterInstr > uint.MaxValue || codeStartOffs + offsetAfterInstr > codeEndOffs) {
|
||||||
|
reader.Position = codeEndOffs;
|
||||||
|
return Array2.Empty<uint>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var targets = new uint[num];
|
||||||
|
uint offset = (uint)offsetAfterInstr;
|
||||||
|
for (int i = 0; i < targets.Length; i++)
|
||||||
|
targets[i] = offset + reader.ReadUInt32();
|
||||||
|
return targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.InlineTok"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected abstract ITokenOperand ReadInlineTok(Instruction instr);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.InlineType"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected abstract ITypeDefOrRef ReadInlineType(Instruction instr);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.InlineVar"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected virtual IVariable ReadInlineVar(Instruction instr) {
|
||||||
|
if (IsArgOperandInstruction(instr))
|
||||||
|
return ReadInlineVarArg(instr);
|
||||||
|
return ReadInlineVarLocal(instr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.InlineVar"/> (a parameter) operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected virtual Parameter ReadInlineVarArg(Instruction instr) => GetParameter(reader.ReadUInt16());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.InlineVar"/> (a local) operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected virtual Local ReadInlineVarLocal(Instruction instr) => GetLocal(reader.ReadUInt16());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.ShortInlineBrTarget"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected virtual uint ReadShortInlineBrTarget(Instruction instr) => instr.Offset + (uint)instr.GetSize() + (uint)reader.ReadSByte();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.ShortInlineI"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected virtual object ReadShortInlineI(Instruction instr) {
|
||||||
|
if (instr.OpCode.Code == Code.Ldc_I4_S)
|
||||||
|
return reader.ReadSByte();
|
||||||
|
return reader.ReadByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.ShortInlineR"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected virtual float ReadShortInlineR(Instruction instr) => reader.ReadSingle();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.ShortInlineVar"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected virtual IVariable ReadShortInlineVar(Instruction instr) {
|
||||||
|
if (IsArgOperandInstruction(instr))
|
||||||
|
return ReadShortInlineVarArg(instr);
|
||||||
|
return ReadShortInlineVarLocal(instr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.ShortInlineVar"/> (a parameter) operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected virtual Parameter ReadShortInlineVarArg(Instruction instr) => GetParameter(reader.ReadByte());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="OperandType.ShortInlineVar"/> (a local) operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The current instruction</param>
|
||||||
|
/// <returns>The operand</returns>
|
||||||
|
protected virtual Local ReadShortInlineVarLocal(Instruction instr) => GetLocal(reader.ReadByte());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns <c>true</c> if it's one of the ldarg/starg instructions that have an operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The instruction to check</param>
|
||||||
|
protected static bool IsArgOperandInstruction(Instruction instr) {
|
||||||
|
switch (instr.OpCode.Code) {
|
||||||
|
case Code.Ldarg:
|
||||||
|
case Code.Ldarg_S:
|
||||||
|
case Code.Ldarga:
|
||||||
|
case Code.Ldarga_S:
|
||||||
|
case Code.Starg:
|
||||||
|
case Code.Starg_S:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a parameter
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">A parameter index</param>
|
||||||
|
/// <returns>A <see cref="Parameter"/> or <c>null</c> if <paramref name="index"/> is invalid</returns>
|
||||||
|
protected Parameter GetParameter(int index) {
|
||||||
|
var parameters = this.parameters;
|
||||||
|
if ((uint)index < (uint)parameters.Count)
|
||||||
|
return parameters[index];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a local
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">A local index</param>
|
||||||
|
/// <returns>A <see cref="Local"/> or <c>null</c> if <paramref name="index"/> is invalid</returns>
|
||||||
|
protected Local GetLocal(int index) {
|
||||||
|
var locals = this.locals;
|
||||||
|
if ((uint)index < (uint)locals.Count)
|
||||||
|
return locals[index];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add an exception handler if it appears valid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eh">The exception handler</param>
|
||||||
|
/// <returns><c>true</c> if it was added, <c>false</c> otherwise</returns>
|
||||||
|
protected bool Add(ExceptionHandler eh) {
|
||||||
|
uint tryStart = GetOffset(eh.TryStart);
|
||||||
|
uint tryEnd = GetOffset(eh.TryEnd);
|
||||||
|
if (tryEnd <= tryStart)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint handlerStart = GetOffset(eh.HandlerStart);
|
||||||
|
uint handlerEnd = GetOffset(eh.HandlerEnd);
|
||||||
|
if (handlerEnd <= handlerStart)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (eh.IsFilter) {
|
||||||
|
if (eh.FilterStart is null)
|
||||||
|
return false;
|
||||||
|
if (eh.FilterStart.Offset >= handlerStart)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handlerStart <= tryStart && tryStart < handlerEnd)
|
||||||
|
return false;
|
||||||
|
if (handlerStart < tryEnd && tryEnd <= handlerEnd)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (tryStart <= handlerStart && handlerStart < tryEnd)
|
||||||
|
return false;
|
||||||
|
if (tryStart < handlerEnd && handlerEnd <= tryEnd)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// It's probably valid, so let's add it.
|
||||||
|
exceptionHandlers.Add(eh);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the offset of an instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instr">The instruction or <c>null</c> if the offset is the first offset
|
||||||
|
/// at the end of the method.</param>
|
||||||
|
/// <returns>The instruction offset</returns>
|
||||||
|
uint GetOffset(Instruction instr) {
|
||||||
|
if (instr is not null)
|
||||||
|
return instr.Offset;
|
||||||
|
var instructions = this.instructions;
|
||||||
|
if (instructions.Count == 0)
|
||||||
|
return 0;
|
||||||
|
return instructions[instructions.Count - 1].Offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restores a <see cref="MethodDef"/>'s body with the parsed method instructions
|
||||||
|
/// and exception handlers
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method">The method that gets updated with the instructions, locals, and
|
||||||
|
/// exception handlers.</param>
|
||||||
|
public virtual void RestoreMethod(MethodDef method) {
|
||||||
|
var body = method.Body;
|
||||||
|
|
||||||
|
body.Variables.Clear();
|
||||||
|
var locals = this.locals;
|
||||||
|
if (locals is not null) {
|
||||||
|
int count = locals.Count;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
body.Variables.Add(locals[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.Instructions.Clear();
|
||||||
|
var instructions = this.instructions;
|
||||||
|
if (instructions is not null) {
|
||||||
|
int count = instructions.Count;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
body.Instructions.Add(instructions[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ExceptionHandlers.Clear();
|
||||||
|
var exceptionHandlers = this.exceptionHandlers;
|
||||||
|
if (exceptionHandlers is not null) {
|
||||||
|
int count = exceptionHandlers.Count;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
body.ExceptionHandlers.Add(exceptionHandlers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
using SR = System.Reflection;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a type address to a <see cref="Type"/>. The address can be found in
|
||||||
|
/// <c>RuntimeTypeHandle.Value</c> and it's the same address you use with the WinDbg SOS command
|
||||||
|
/// !dumpmt.
|
||||||
|
/// </summary>
|
||||||
|
static class MethodTableToTypeConverter {
|
||||||
|
const string METHOD_NAME = "m";
|
||||||
|
static readonly MethodInfo setMethodBodyMethodInfo = typeof(MethodBuilder).GetMethod("SetMethodBody", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
static readonly FieldInfo localSignatureFieldInfo = typeof(ILGenerator).GetField("m_localSignature", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
static readonly FieldInfo sigDoneFieldInfo = typeof(SignatureHelper).GetField("m_sigDone", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
static readonly FieldInfo currSigFieldInfo = typeof(SignatureHelper).GetField("m_currSig", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
static readonly FieldInfo signatureFieldInfo = typeof(SignatureHelper).GetField("m_signature", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
static readonly FieldInfo ptrFieldInfo = typeof(RuntimeTypeHandle).GetField("m_ptr", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||||
|
static readonly Dictionary<IntPtr, Type> addrToType = new Dictionary<IntPtr, Type>();
|
||||||
|
static ModuleBuilder moduleBuilder;
|
||||||
|
static int numNewTypes;
|
||||||
|
static object lockObj = new object();
|
||||||
|
|
||||||
|
static MethodTableToTypeConverter() {
|
||||||
|
if (ptrFieldInfo is null) {
|
||||||
|
#if NETSTANDARD || NETCOREAPP
|
||||||
|
var asmb = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("DynAsm"), AssemblyBuilderAccess.Run);
|
||||||
|
#else
|
||||||
|
var asmb = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("DynAsm"), AssemblyBuilderAccess.Run);
|
||||||
|
#endif
|
||||||
|
moduleBuilder = asmb.DefineDynamicModule("DynMod");
|
||||||
|
}
|
||||||
|
|
||||||
|
// In .NET 8+, ILGenerator is an abstract class and the actual ILGenerator implementation is found in RuntimeILGenerator.
|
||||||
|
localSignatureFieldInfo ??= Type.GetType("System.Reflection.Emit.RuntimeILGenerator")?.GetField("m_localSignature", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts <paramref name="address"/> to a <see cref="Type"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">Address of type</param>
|
||||||
|
/// <returns>The <see cref="Type"/> or <c>null</c></returns>
|
||||||
|
public static Type Convert(IntPtr address) {
|
||||||
|
lock (lockObj) {
|
||||||
|
if (addrToType.TryGetValue(address, out var type))
|
||||||
|
return type;
|
||||||
|
|
||||||
|
type = GetTypeNET20(address) ?? GetTypeUsingTypeBuilder(address);
|
||||||
|
addrToType[address] = type;
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Type GetTypeUsingTypeBuilder(IntPtr address) {
|
||||||
|
if (moduleBuilder is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var tb = moduleBuilder.DefineType(GetNextTypeName());
|
||||||
|
var mb = tb.DefineMethod(METHOD_NAME, SR.MethodAttributes.Static, typeof(void), Array2.Empty<Type>());
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (setMethodBodyMethodInfo is not null)
|
||||||
|
return GetTypeNET45(tb, mb, address);
|
||||||
|
else
|
||||||
|
return GetTypeNET40(tb, mb, address);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
moduleBuilder = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// .NET Framework 4.5 and later have the documented SetMethodBody() method.
|
||||||
|
static Type GetTypeNET45(TypeBuilder tb, MethodBuilder mb, IntPtr address) {
|
||||||
|
var code = new byte[1] { 0x2A };
|
||||||
|
int maxStack = 8;
|
||||||
|
var locals = GetLocalSignature(address);
|
||||||
|
setMethodBodyMethodInfo.Invoke(mb, new object[5] { code, maxStack, locals, null, null });
|
||||||
|
#if NETSTANDARD
|
||||||
|
var type = tb.CreateTypeInfo();
|
||||||
|
#else
|
||||||
|
var type = tb.CreateType();
|
||||||
|
#endif
|
||||||
|
var createdMethod = type.GetMethod(METHOD_NAME, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
|
||||||
|
return createdMethod.GetMethodBody().LocalVariables[0].LocalType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This code works with .NET Framework 4.0+ but will throw an exception if .NET Framework 2.0 is used
|
||||||
|
// ("operation could destabilize the runtime")
|
||||||
|
static Type GetTypeNET40(TypeBuilder tb, MethodBuilder mb, IntPtr address) {
|
||||||
|
var ilg = mb.GetILGenerator();
|
||||||
|
ilg.Emit(SR.Emit.OpCodes.Ret);
|
||||||
|
|
||||||
|
// We need at least one local to make sure the SignatureHelper from ILGenerator is used.
|
||||||
|
ilg.DeclareLocal(typeof(int));
|
||||||
|
|
||||||
|
var locals = GetLocalSignature(address);
|
||||||
|
var sigHelper = (SignatureHelper)localSignatureFieldInfo.GetValue(ilg);
|
||||||
|
sigDoneFieldInfo.SetValue(sigHelper, true);
|
||||||
|
currSigFieldInfo.SetValue(sigHelper, locals.Length);
|
||||||
|
signatureFieldInfo.SetValue(sigHelper, locals);
|
||||||
|
#if NETSTANDARD
|
||||||
|
var type = tb.CreateTypeInfo();
|
||||||
|
#else
|
||||||
|
var type = tb.CreateType();
|
||||||
|
#endif
|
||||||
|
var createdMethod = type.GetMethod(METHOD_NAME, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
|
||||||
|
return createdMethod.GetMethodBody().LocalVariables[0].LocalType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .NET Framework 2.0 - 3.5
|
||||||
|
static Type GetTypeNET20(IntPtr address) {
|
||||||
|
if (ptrFieldInfo is null)
|
||||||
|
return null;
|
||||||
|
object th = new RuntimeTypeHandle();
|
||||||
|
ptrFieldInfo.SetValue(th, address);
|
||||||
|
return Type.GetTypeFromHandle((RuntimeTypeHandle)th);
|
||||||
|
}
|
||||||
|
|
||||||
|
static string GetNextTypeName() => $"Type{numNewTypes++}";
|
||||||
|
|
||||||
|
static byte[] GetLocalSignature(IntPtr mtAddr) {
|
||||||
|
ulong mtValue = (ulong)mtAddr.ToInt64();
|
||||||
|
if (IntPtr.Size == 4) {
|
||||||
|
return new byte[] {
|
||||||
|
0x07,
|
||||||
|
0x01,
|
||||||
|
(byte)ElementType.Internal,
|
||||||
|
(byte)mtValue,
|
||||||
|
(byte)(mtValue >> 8),
|
||||||
|
(byte)(mtValue >> 16),
|
||||||
|
(byte)(mtValue >> 24),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new byte[] {
|
||||||
|
0x07,
|
||||||
|
0x01,
|
||||||
|
(byte)ElementType.Internal,
|
||||||
|
(byte)mtValue,
|
||||||
|
(byte)(mtValue >> 8),
|
||||||
|
(byte)(mtValue >> 16),
|
||||||
|
(byte)(mtValue >> 24),
|
||||||
|
(byte)(mtValue >> 32),
|
||||||
|
(byte)(mtValue >> 40),
|
||||||
|
(byte)(mtValue >> 48),
|
||||||
|
(byte)(mtValue >> 56),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,516 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// Instruction utility methods
|
||||||
|
/// </summary>
|
||||||
|
public static class MethodUtils {
|
||||||
|
/// <summary>
|
||||||
|
/// Shorter instructions are converted to the longer form, eg. <c>Ldc_I4_1</c> is
|
||||||
|
/// converted to <c>Ldc_I4</c> with a <c>1</c> as the operand.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instructions">All instructions</param>
|
||||||
|
/// <param name="locals">All locals</param>
|
||||||
|
/// <param name="parameters">All method parameters, including the hidden 'this' parameter
|
||||||
|
/// if it's an instance method. Use <see cref="MethodDef.Parameters"/>.</param>
|
||||||
|
public static void SimplifyMacros(this IList<Instruction> instructions, IList<Local> locals, IList<Parameter> parameters) {
|
||||||
|
int count = instructions.Count;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
var instr = instructions[i];
|
||||||
|
switch (instr.OpCode.Code) {
|
||||||
|
case Code.Beq_S:
|
||||||
|
instr.OpCode = OpCodes.Beq;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Bge_S:
|
||||||
|
instr.OpCode = OpCodes.Bge;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Bge_Un_S:
|
||||||
|
instr.OpCode = OpCodes.Bge_Un;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Bgt_S:
|
||||||
|
instr.OpCode = OpCodes.Bgt;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Bgt_Un_S:
|
||||||
|
instr.OpCode = OpCodes.Bgt_Un;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ble_S:
|
||||||
|
instr.OpCode = OpCodes.Ble;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ble_Un_S:
|
||||||
|
instr.OpCode = OpCodes.Ble_Un;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Blt_S:
|
||||||
|
instr.OpCode = OpCodes.Blt;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Blt_Un_S:
|
||||||
|
instr.OpCode = OpCodes.Blt_Un;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Bne_Un_S:
|
||||||
|
instr.OpCode = OpCodes.Bne_Un;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Br_S:
|
||||||
|
instr.OpCode = OpCodes.Br;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Brfalse_S:
|
||||||
|
instr.OpCode = OpCodes.Brfalse;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Brtrue_S:
|
||||||
|
instr.OpCode = OpCodes.Brtrue;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldarg_0:
|
||||||
|
instr.OpCode = OpCodes.Ldarg;
|
||||||
|
instr.Operand = ReadList(parameters, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldarg_1:
|
||||||
|
instr.OpCode = OpCodes.Ldarg;
|
||||||
|
instr.Operand = ReadList(parameters, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldarg_2:
|
||||||
|
instr.OpCode = OpCodes.Ldarg;
|
||||||
|
instr.Operand = ReadList(parameters, 2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldarg_3:
|
||||||
|
instr.OpCode = OpCodes.Ldarg;
|
||||||
|
instr.Operand = ReadList(parameters, 3);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldarg_S:
|
||||||
|
instr.OpCode = OpCodes.Ldarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldarga_S:
|
||||||
|
instr.OpCode = OpCodes.Ldarga;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldc_I4_0:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4;
|
||||||
|
instr.Operand = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldc_I4_1:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4;
|
||||||
|
instr.Operand = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldc_I4_2:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4;
|
||||||
|
instr.Operand = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldc_I4_3:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4;
|
||||||
|
instr.Operand = 3;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldc_I4_4:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4;
|
||||||
|
instr.Operand = 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldc_I4_5:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4;
|
||||||
|
instr.Operand = 5;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldc_I4_6:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4;
|
||||||
|
instr.Operand = 6;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldc_I4_7:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4;
|
||||||
|
instr.Operand = 7;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldc_I4_8:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4;
|
||||||
|
instr.Operand = 8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldc_I4_M1:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4;
|
||||||
|
instr.Operand = -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldc_I4_S:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4;
|
||||||
|
instr.Operand = (int)(sbyte)instr.Operand;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldloc_0:
|
||||||
|
instr.OpCode = OpCodes.Ldloc;
|
||||||
|
instr.Operand = ReadList(locals, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldloc_1:
|
||||||
|
instr.OpCode = OpCodes.Ldloc;
|
||||||
|
instr.Operand = ReadList(locals, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldloc_2:
|
||||||
|
instr.OpCode = OpCodes.Ldloc;
|
||||||
|
instr.Operand = ReadList(locals, 2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldloc_3:
|
||||||
|
instr.OpCode = OpCodes.Ldloc;
|
||||||
|
instr.Operand = ReadList(locals, 3);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldloc_S:
|
||||||
|
instr.OpCode = OpCodes.Ldloc;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldloca_S:
|
||||||
|
instr.OpCode = OpCodes.Ldloca;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Leave_S:
|
||||||
|
instr.OpCode = OpCodes.Leave;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Starg_S:
|
||||||
|
instr.OpCode = OpCodes.Starg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Stloc_0:
|
||||||
|
instr.OpCode = OpCodes.Stloc;
|
||||||
|
instr.Operand = ReadList(locals, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Stloc_1:
|
||||||
|
instr.OpCode = OpCodes.Stloc;
|
||||||
|
instr.Operand = ReadList(locals, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Stloc_2:
|
||||||
|
instr.OpCode = OpCodes.Stloc;
|
||||||
|
instr.Operand = ReadList(locals, 2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Stloc_3:
|
||||||
|
instr.OpCode = OpCodes.Stloc;
|
||||||
|
instr.Operand = ReadList(locals, 3);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Stloc_S:
|
||||||
|
instr.OpCode = OpCodes.Stloc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static T ReadList<T>(IList<T> list, int index) {
|
||||||
|
if (list is null)
|
||||||
|
return default;
|
||||||
|
if ((uint)index < (uint)list.Count)
|
||||||
|
return list[index];
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optimizes instructions by using the shorter form if possible. Eg. <c>Ldc_I4</c> <c>1</c>
|
||||||
|
/// will be replaced with <c>Ldc_I4_1</c>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instructions">All instructions</param>
|
||||||
|
public static void OptimizeMacros(this IList<Instruction> instructions) {
|
||||||
|
int count = instructions.Count;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
var instr = instructions[i];
|
||||||
|
Parameter arg;
|
||||||
|
Local local;
|
||||||
|
switch (instr.OpCode.Code) {
|
||||||
|
case Code.Ldarg:
|
||||||
|
case Code.Ldarg_S:
|
||||||
|
arg = instr.Operand as Parameter;
|
||||||
|
if (arg is null)
|
||||||
|
break;
|
||||||
|
if (arg.Index == 0) {
|
||||||
|
instr.OpCode = OpCodes.Ldarg_0;
|
||||||
|
instr.Operand = null;
|
||||||
|
}
|
||||||
|
else if (arg.Index == 1) {
|
||||||
|
instr.OpCode = OpCodes.Ldarg_1;
|
||||||
|
instr.Operand = null;
|
||||||
|
}
|
||||||
|
else if (arg.Index == 2) {
|
||||||
|
instr.OpCode = OpCodes.Ldarg_2;
|
||||||
|
instr.Operand = null;
|
||||||
|
}
|
||||||
|
else if (arg.Index == 3) {
|
||||||
|
instr.OpCode = OpCodes.Ldarg_3;
|
||||||
|
instr.Operand = null;
|
||||||
|
}
|
||||||
|
else if (byte.MinValue <= arg.Index && arg.Index <= byte.MaxValue)
|
||||||
|
instr.OpCode = OpCodes.Ldarg_S;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldarga:
|
||||||
|
arg = instr.Operand as Parameter;
|
||||||
|
if (arg is null)
|
||||||
|
break;
|
||||||
|
if (byte.MinValue <= arg.Index && arg.Index <= byte.MaxValue)
|
||||||
|
instr.OpCode = OpCodes.Ldarga_S;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldc_I4:
|
||||||
|
case Code.Ldc_I4_S:
|
||||||
|
int i4;
|
||||||
|
if (instr.Operand is int)
|
||||||
|
i4 = (int)instr.Operand;
|
||||||
|
else if (instr.Operand is sbyte)
|
||||||
|
i4 = (sbyte)instr.Operand;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
switch (i4) {
|
||||||
|
case 0:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4_0;
|
||||||
|
instr.Operand = null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4_1;
|
||||||
|
instr.Operand = null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4_2;
|
||||||
|
instr.Operand = null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4_3;
|
||||||
|
instr.Operand = null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4_4;
|
||||||
|
instr.Operand = null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4_5;
|
||||||
|
instr.Operand = null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4_6;
|
||||||
|
instr.Operand = null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 7:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4_7;
|
||||||
|
instr.Operand = null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 8:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4_8;
|
||||||
|
instr.Operand = null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case -1:
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4_M1;
|
||||||
|
instr.Operand = null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (sbyte.MinValue <= i4 && i4 <= sbyte.MaxValue) {
|
||||||
|
instr.OpCode = OpCodes.Ldc_I4_S;
|
||||||
|
instr.Operand = (sbyte)i4;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldloc:
|
||||||
|
case Code.Ldloc_S:
|
||||||
|
local = instr.Operand as Local;
|
||||||
|
if (local is null)
|
||||||
|
break;
|
||||||
|
if (local.Index == 0) {
|
||||||
|
instr.OpCode = OpCodes.Ldloc_0;
|
||||||
|
instr.Operand = null;
|
||||||
|
}
|
||||||
|
else if (local.Index == 1) {
|
||||||
|
instr.OpCode = OpCodes.Ldloc_1;
|
||||||
|
instr.Operand = null;
|
||||||
|
}
|
||||||
|
else if (local.Index == 2) {
|
||||||
|
instr.OpCode = OpCodes.Ldloc_2;
|
||||||
|
instr.Operand = null;
|
||||||
|
}
|
||||||
|
else if (local.Index == 3) {
|
||||||
|
instr.OpCode = OpCodes.Ldloc_3;
|
||||||
|
instr.Operand = null;
|
||||||
|
}
|
||||||
|
else if (byte.MinValue <= local.Index && local.Index <= byte.MaxValue)
|
||||||
|
instr.OpCode = OpCodes.Ldloc_S;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldloca:
|
||||||
|
local = instr.Operand as Local;
|
||||||
|
if (local is null)
|
||||||
|
break;
|
||||||
|
if (byte.MinValue <= local.Index && local.Index <= byte.MaxValue)
|
||||||
|
instr.OpCode = OpCodes.Ldloca_S;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Starg:
|
||||||
|
arg = instr.Operand as Parameter;
|
||||||
|
if (arg is null)
|
||||||
|
break;
|
||||||
|
if (byte.MinValue <= arg.Index && arg.Index <= byte.MaxValue)
|
||||||
|
instr.OpCode = OpCodes.Starg_S;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Stloc:
|
||||||
|
case Code.Stloc_S:
|
||||||
|
local = instr.Operand as Local;
|
||||||
|
if (local is null)
|
||||||
|
break;
|
||||||
|
if (local.Index == 0) {
|
||||||
|
instr.OpCode = OpCodes.Stloc_0;
|
||||||
|
instr.Operand = null;
|
||||||
|
}
|
||||||
|
else if (local.Index == 1) {
|
||||||
|
instr.OpCode = OpCodes.Stloc_1;
|
||||||
|
instr.Operand = null;
|
||||||
|
}
|
||||||
|
else if (local.Index == 2) {
|
||||||
|
instr.OpCode = OpCodes.Stloc_2;
|
||||||
|
instr.Operand = null;
|
||||||
|
}
|
||||||
|
else if (local.Index == 3) {
|
||||||
|
instr.OpCode = OpCodes.Stloc_3;
|
||||||
|
instr.Operand = null;
|
||||||
|
}
|
||||||
|
else if (byte.MinValue <= local.Index && local.Index <= byte.MaxValue)
|
||||||
|
instr.OpCode = OpCodes.Stloc_S;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OptimizeBranches(instructions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Short branch instructions are converted to the long form, eg. <c>Beq_S</c> is
|
||||||
|
/// converted to <c>Beq</c>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instructions">All instructions</param>
|
||||||
|
public static void SimplifyBranches(this IList<Instruction> instructions) {
|
||||||
|
int count = instructions.Count;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
var instr = instructions[i];
|
||||||
|
switch (instr.OpCode.Code) {
|
||||||
|
case Code.Beq_S: instr.OpCode = OpCodes.Beq; break;
|
||||||
|
case Code.Bge_S: instr.OpCode = OpCodes.Bge; break;
|
||||||
|
case Code.Bgt_S: instr.OpCode = OpCodes.Bgt; break;
|
||||||
|
case Code.Ble_S: instr.OpCode = OpCodes.Ble; break;
|
||||||
|
case Code.Blt_S: instr.OpCode = OpCodes.Blt; break;
|
||||||
|
case Code.Bne_Un_S: instr.OpCode = OpCodes.Bne_Un; break;
|
||||||
|
case Code.Bge_Un_S: instr.OpCode = OpCodes.Bge_Un; break;
|
||||||
|
case Code.Bgt_Un_S: instr.OpCode = OpCodes.Bgt_Un; break;
|
||||||
|
case Code.Ble_Un_S: instr.OpCode = OpCodes.Ble_Un; break;
|
||||||
|
case Code.Blt_Un_S: instr.OpCode = OpCodes.Blt_Un; break;
|
||||||
|
case Code.Br_S: instr.OpCode = OpCodes.Br; break;
|
||||||
|
case Code.Brfalse_S:instr.OpCode = OpCodes.Brfalse; break;
|
||||||
|
case Code.Brtrue_S: instr.OpCode = OpCodes.Brtrue; break;
|
||||||
|
case Code.Leave_S: instr.OpCode = OpCodes.Leave; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optimizes branches by using the smallest possible branch
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instructions">All instructions</param>
|
||||||
|
public static void OptimizeBranches(this IList<Instruction> instructions) {
|
||||||
|
while (true) {
|
||||||
|
UpdateInstructionOffsets(instructions);
|
||||||
|
|
||||||
|
bool modified = false;
|
||||||
|
int count = instructions.Count;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
var instr = instructions[i];
|
||||||
|
OpCode shortOpCode;
|
||||||
|
switch (instr.OpCode.Code) {
|
||||||
|
case Code.Beq: shortOpCode = OpCodes.Beq_S; break;
|
||||||
|
case Code.Bge: shortOpCode = OpCodes.Bge_S; break;
|
||||||
|
case Code.Bge_Un: shortOpCode = OpCodes.Bge_Un_S; break;
|
||||||
|
case Code.Bgt: shortOpCode = OpCodes.Bgt_S; break;
|
||||||
|
case Code.Bgt_Un: shortOpCode = OpCodes.Bgt_Un_S; break;
|
||||||
|
case Code.Ble: shortOpCode = OpCodes.Ble_S; break;
|
||||||
|
case Code.Ble_Un: shortOpCode = OpCodes.Ble_Un_S; break;
|
||||||
|
case Code.Blt: shortOpCode = OpCodes.Blt_S; break;
|
||||||
|
case Code.Blt_Un: shortOpCode = OpCodes.Blt_Un_S; break;
|
||||||
|
case Code.Bne_Un: shortOpCode = OpCodes.Bne_Un_S; break;
|
||||||
|
case Code.Br: shortOpCode = OpCodes.Br_S; break;
|
||||||
|
case Code.Brfalse: shortOpCode = OpCodes.Brfalse_S; break;
|
||||||
|
case Code.Brtrue: shortOpCode = OpCodes.Brtrue_S; break;
|
||||||
|
case Code.Leave: shortOpCode = OpCodes.Leave_S; break;
|
||||||
|
default: continue;
|
||||||
|
}
|
||||||
|
var targetInstr = instr.Operand as Instruction;
|
||||||
|
if (targetInstr is null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int afterShortInstr;
|
||||||
|
if (targetInstr.Offset >= instr.Offset) {
|
||||||
|
// Target is >= this instruction so use the offset after
|
||||||
|
// current instruction
|
||||||
|
afterShortInstr = (int)instr.Offset + instr.GetSize();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Target is < this instruction so use the offset after
|
||||||
|
// the short instruction
|
||||||
|
const int operandSize = 1;
|
||||||
|
afterShortInstr = (int)instr.Offset + shortOpCode.Size + operandSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
int displ = (int)targetInstr.Offset - afterShortInstr;
|
||||||
|
if (sbyte.MinValue <= displ && displ <= sbyte.MaxValue) {
|
||||||
|
instr.OpCode = shortOpCode;
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!modified)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates each instruction's offset
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instructions">All instructions</param>
|
||||||
|
/// <returns>Total size in bytes of all instructions</returns>
|
||||||
|
public static uint UpdateInstructionOffsets(this IList<Instruction> instructions) {
|
||||||
|
uint offset = 0;
|
||||||
|
int count = instructions.Count;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
var instr = instructions[i];
|
||||||
|
instr.Offset = offset;
|
||||||
|
offset += (uint)instr.GetSize();
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// A CIL opcode
|
||||||
|
/// </summary>
|
||||||
|
public sealed class OpCode {
|
||||||
|
/// <summary>
|
||||||
|
/// The opcode name
|
||||||
|
/// </summary>
|
||||||
|
public readonly string Name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The opcode as a <see cref="Code"/> enum
|
||||||
|
/// </summary>
|
||||||
|
public readonly Code Code;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Operand type
|
||||||
|
/// </summary>
|
||||||
|
public readonly OperandType OperandType;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flow control info
|
||||||
|
/// </summary>
|
||||||
|
public readonly FlowControl FlowControl;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opcode type
|
||||||
|
/// </summary>
|
||||||
|
public readonly OpCodeType OpCodeType;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Push stack behavior
|
||||||
|
/// </summary>
|
||||||
|
public readonly StackBehaviour StackBehaviourPush;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pop stack behavior
|
||||||
|
/// </summary>
|
||||||
|
public readonly StackBehaviour StackBehaviourPop;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value which is compatible with <see cref="System.Reflection.Emit.OpCode.Value"/>
|
||||||
|
/// </summary>
|
||||||
|
public short Value => (short)Code;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size of the opcode. It's either 1 or 2 bytes.
|
||||||
|
/// </summary>
|
||||||
|
public int Size => Code < (Code)0x100 || Code == Code.UNKNOWN1 ? 1 : 2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs an experimental opcode.
|
||||||
|
/// </summary>
|
||||||
|
public OpCode(string name, byte first, byte second, OperandType operandType, FlowControl flowControl, StackBehaviour push, StackBehaviour pop)
|
||||||
|
: this(name, (Code)((first << 8) | second), operandType, flowControl, OpCodeType.Experimental, push, pop, true) {
|
||||||
|
}
|
||||||
|
|
||||||
|
internal OpCode(string name, Code code, OperandType operandType, FlowControl flowControl, OpCodeType opCodeType, StackBehaviour push, StackBehaviour pop, bool experimental = false) {
|
||||||
|
Name = name;
|
||||||
|
Code = code;
|
||||||
|
OperandType = operandType;
|
||||||
|
FlowControl = flowControl;
|
||||||
|
OpCodeType = opCodeType;
|
||||||
|
StackBehaviourPush = push;
|
||||||
|
StackBehaviourPop = pop;
|
||||||
|
if (!experimental) {
|
||||||
|
if (((ushort)code >> 8) == 0)
|
||||||
|
OpCodes.OneByteOpCodes[(byte)code] = this;
|
||||||
|
else if (((ushort)code >> 8) == 0xFE)
|
||||||
|
OpCodes.TwoByteOpCodes[(byte)code] = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with no operand
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction() => Instruction.Create(this);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a <see cref="byte"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(byte value) => Instruction.Create(this, value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a <see cref="sbyte"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(sbyte value) => Instruction.Create(this, value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with an <see cref="int"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(int value) => Instruction.Create(this, value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a <see cref="long"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(long value) => Instruction.Create(this, value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a <see cref="float"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(float value) => Instruction.Create(this, value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a <see cref="double"/> operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(double value) => Instruction.Create(this, value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a string operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="s">The string</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(string s) => Instruction.Create(this, s);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with an instruction target operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">Target instruction</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(Instruction target) => Instruction.Create(this, target);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with an instruction target list operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="targets">The targets</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(IList<Instruction> targets) => Instruction.Create(this, targets);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a type operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(ITypeDefOrRef type) => Instruction.Create(this, type);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a type operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(CorLibTypeSig type) => Instruction.Create(this, type.TypeDefOrRef);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a method/field operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mr">The method/field</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(MemberRef mr) => Instruction.Create(this, mr);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a field operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="field">The field</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(IField field) => Instruction.Create(this, field);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a method operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method">The method</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(IMethod method) => Instruction.Create(this, method);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a token operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">The token</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(ITokenOperand token) => Instruction.Create(this, token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a method signature operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="methodSig">The method signature</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(MethodSig methodSig) => Instruction.Create(this, methodSig);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a method parameter operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameter">The method parameter</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(Parameter parameter) => Instruction.Create(this, parameter);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instruction with a method local operand
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="local">The method local</param>
|
||||||
|
/// <returns>A new <see cref="Instruction"/> instance</returns>
|
||||||
|
public Instruction ToInstruction(Local local) => Instruction.Create(this, local);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString() => Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// CIL opcode type
|
||||||
|
/// </summary>
|
||||||
|
public enum OpCodeType : byte {
|
||||||
|
/// <summary/>
|
||||||
|
Annotation,
|
||||||
|
/// <summary/>
|
||||||
|
Macro,
|
||||||
|
/// <summary/>
|
||||||
|
Nternal,
|
||||||
|
/// <summary/>
|
||||||
|
Objmodel,
|
||||||
|
/// <summary/>
|
||||||
|
Prefix,
|
||||||
|
/// <summary/>
|
||||||
|
Primitive,
|
||||||
|
/// <summary/>
|
||||||
|
Experimental,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,263 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// Contains all valid CIL opcodes
|
||||||
|
/// </summary>
|
||||||
|
public static class OpCodes {
|
||||||
|
/// <summary>
|
||||||
|
/// All one-byte opcodes
|
||||||
|
/// </summary>
|
||||||
|
public static readonly OpCode[] OneByteOpCodes = new OpCode[0x100];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All two-byte opcodes (first byte is <c>0xFE</c>)
|
||||||
|
/// </summary>
|
||||||
|
public static readonly OpCode[] TwoByteOpCodes = new OpCode[0x100];
|
||||||
|
|
||||||
|
#pragma warning disable 1591 // disable XML doc warning
|
||||||
|
public static readonly OpCode UNKNOWN1 = new OpCode("UNKNOWN1", Code.UNKNOWN1, OperandType.InlineNone, FlowControl.Meta, OpCodeType.Nternal, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode UNKNOWN2 = new OpCode("UNKNOWN2", Code.UNKNOWN2, OperandType.InlineNone, FlowControl.Meta, OpCodeType.Nternal, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Nop = new OpCode("nop", Code.Nop, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Break = new OpCode("break", Code.Break, OperandType.InlineNone, FlowControl.Break, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldarg_0 = new OpCode("ldarg.0", Code.Ldarg_0, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Push1, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldarg_1 = new OpCode("ldarg.1", Code.Ldarg_1, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Push1, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldarg_2 = new OpCode("ldarg.2", Code.Ldarg_2, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Push1, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldarg_3 = new OpCode("ldarg.3", Code.Ldarg_3, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Push1, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldloc_0 = new OpCode("ldloc.0", Code.Ldloc_0, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Push1, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldloc_1 = new OpCode("ldloc.1", Code.Ldloc_1, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Push1, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldloc_2 = new OpCode("ldloc.2", Code.Ldloc_2, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Push1, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldloc_3 = new OpCode("ldloc.3", Code.Ldloc_3, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Push1, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Stloc_0 = new OpCode("stloc.0", Code.Stloc_0, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Stloc_1 = new OpCode("stloc.1", Code.Stloc_1, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Stloc_2 = new OpCode("stloc.2", Code.Stloc_2, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Stloc_3 = new OpCode("stloc.3", Code.Stloc_3, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Ldarg_S = new OpCode("ldarg.s", Code.Ldarg_S, OperandType.ShortInlineVar, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Push1, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldarga_S = new OpCode("ldarga.s", Code.Ldarga_S, OperandType.ShortInlineVar, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Starg_S = new OpCode("starg.s", Code.Starg_S, OperandType.ShortInlineVar, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Ldloc_S = new OpCode("ldloc.s", Code.Ldloc_S, OperandType.ShortInlineVar, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Push1, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldloca_S = new OpCode("ldloca.s", Code.Ldloca_S, OperandType.ShortInlineVar, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Stloc_S = new OpCode("stloc.s", Code.Stloc_S, OperandType.ShortInlineVar, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Ldnull = new OpCode("ldnull", Code.Ldnull, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushref, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldc_I4_M1 = new OpCode("ldc.i4.m1", Code.Ldc_I4_M1, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldc_I4_0 = new OpCode("ldc.i4.0", Code.Ldc_I4_0, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldc_I4_1 = new OpCode("ldc.i4.1", Code.Ldc_I4_1, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldc_I4_2 = new OpCode("ldc.i4.2", Code.Ldc_I4_2, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldc_I4_3 = new OpCode("ldc.i4.3", Code.Ldc_I4_3, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldc_I4_4 = new OpCode("ldc.i4.4", Code.Ldc_I4_4, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldc_I4_5 = new OpCode("ldc.i4.5", Code.Ldc_I4_5, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldc_I4_6 = new OpCode("ldc.i4.6", Code.Ldc_I4_6, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldc_I4_7 = new OpCode("ldc.i4.7", Code.Ldc_I4_7, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldc_I4_8 = new OpCode("ldc.i4.8", Code.Ldc_I4_8, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldc_I4_S = new OpCode("ldc.i4.s", Code.Ldc_I4_S, OperandType.ShortInlineI, FlowControl.Next, OpCodeType.Macro, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldc_I4 = new OpCode("ldc.i4", Code.Ldc_I4, OperandType.InlineI, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldc_I8 = new OpCode("ldc.i8", Code.Ldc_I8, OperandType.InlineI8, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi8, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldc_R4 = new OpCode("ldc.r4", Code.Ldc_R4, OperandType.ShortInlineR, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushr4, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldc_R8 = new OpCode("ldc.r8", Code.Ldc_R8, OperandType.InlineR, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushr8, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Dup = new OpCode("dup", Code.Dup, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1_push1, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Pop = new OpCode("pop", Code.Pop, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Jmp = new OpCode("jmp", Code.Jmp, OperandType.InlineMethod, FlowControl.Call, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Call = new OpCode("call", Code.Call, OperandType.InlineMethod, FlowControl.Call, OpCodeType.Primitive, StackBehaviour.Varpush, StackBehaviour.Varpop);
|
||||||
|
public static readonly OpCode Calli = new OpCode("calli", Code.Calli, OperandType.InlineSig, FlowControl.Call, OpCodeType.Primitive, StackBehaviour.Varpush, StackBehaviour.Varpop);
|
||||||
|
public static readonly OpCode Ret = new OpCode("ret", Code.Ret, OperandType.InlineNone, FlowControl.Return, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Varpop);
|
||||||
|
public static readonly OpCode Br_S = new OpCode("br.s", Code.Br_S, OperandType.ShortInlineBrTarget, FlowControl.Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Brfalse_S = new OpCode("brfalse.s", Code.Brfalse_S, OperandType.ShortInlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Brtrue_S = new OpCode("brtrue.s", Code.Brtrue_S, OperandType.ShortInlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Beq_S = new OpCode("beq.s", Code.Beq_S, OperandType.ShortInlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Bge_S = new OpCode("bge.s", Code.Bge_S, OperandType.ShortInlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Bgt_S = new OpCode("bgt.s", Code.Bgt_S, OperandType.ShortInlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Ble_S = new OpCode("ble.s", Code.Ble_S, OperandType.ShortInlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Blt_S = new OpCode("blt.s", Code.Blt_S, OperandType.ShortInlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Bne_Un_S = new OpCode("bne.un.s", Code.Bne_Un_S, OperandType.ShortInlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Bge_Un_S = new OpCode("bge.un.s", Code.Bge_Un_S, OperandType.ShortInlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Bgt_Un_S = new OpCode("bgt.un.s", Code.Bgt_Un_S, OperandType.ShortInlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Ble_Un_S = new OpCode("ble.un.s", Code.Ble_Un_S, OperandType.ShortInlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Blt_Un_S = new OpCode("blt.un.s", Code.Blt_Un_S, OperandType.ShortInlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Br = new OpCode("br", Code.Br, OperandType.InlineBrTarget, FlowControl.Branch, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Brfalse = new OpCode("brfalse", Code.Brfalse, OperandType.InlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Brtrue = new OpCode("brtrue", Code.Brtrue, OperandType.InlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Beq = new OpCode("beq", Code.Beq, OperandType.InlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Bge = new OpCode("bge", Code.Bge, OperandType.InlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Bgt = new OpCode("bgt", Code.Bgt, OperandType.InlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Ble = new OpCode("ble", Code.Ble, OperandType.InlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Blt = new OpCode("blt", Code.Blt, OperandType.InlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Bne_Un = new OpCode("bne.un", Code.Bne_Un, OperandType.InlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Bge_Un = new OpCode("bge.un", Code.Bge_Un, OperandType.InlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Bgt_Un = new OpCode("bgt.un", Code.Bgt_Un, OperandType.InlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Ble_Un = new OpCode("ble.un", Code.Ble_Un, OperandType.InlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Blt_Un = new OpCode("blt.un", Code.Blt_Un, OperandType.InlineBrTarget, FlowControl.Cond_Branch, OpCodeType.Macro, StackBehaviour.Push0, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Switch = new OpCode("switch", Code.Switch, OperandType.InlineSwitch, FlowControl.Cond_Branch, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Ldind_I1 = new OpCode("ldind.i1", Code.Ldind_I1, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Ldind_U1 = new OpCode("ldind.u1", Code.Ldind_U1, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Ldind_I2 = new OpCode("ldind.i2", Code.Ldind_I2, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Ldind_U2 = new OpCode("ldind.u2", Code.Ldind_U2, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Ldind_I4 = new OpCode("ldind.i4", Code.Ldind_I4, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Ldind_U4 = new OpCode("ldind.u4", Code.Ldind_U4, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Ldind_I8 = new OpCode("ldind.i8", Code.Ldind_I8, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi8, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Ldind_I = new OpCode("ldind.i", Code.Ldind_I, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Ldind_R4 = new OpCode("ldind.r4", Code.Ldind_R4, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushr4, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Ldind_R8 = new OpCode("ldind.r8", Code.Ldind_R8, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushr8, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Ldind_Ref = new OpCode("ldind.ref", Code.Ldind_Ref, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushref, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Stind_Ref = new OpCode("stind.ref", Code.Stind_Ref, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Popi_popi);
|
||||||
|
public static readonly OpCode Stind_I1 = new OpCode("stind.i1", Code.Stind_I1, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Popi_popi);
|
||||||
|
public static readonly OpCode Stind_I2 = new OpCode("stind.i2", Code.Stind_I2, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Popi_popi);
|
||||||
|
public static readonly OpCode Stind_I4 = new OpCode("stind.i4", Code.Stind_I4, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Popi_popi);
|
||||||
|
public static readonly OpCode Stind_I8 = new OpCode("stind.i8", Code.Stind_I8, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Popi_popi8);
|
||||||
|
public static readonly OpCode Stind_R4 = new OpCode("stind.r4", Code.Stind_R4, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Popi_popr4);
|
||||||
|
public static readonly OpCode Stind_R8 = new OpCode("stind.r8", Code.Stind_R8, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Popi_popr8);
|
||||||
|
public static readonly OpCode Add = new OpCode("add", Code.Add, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Sub = new OpCode("sub", Code.Sub, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Mul = new OpCode("mul", Code.Mul, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Div = new OpCode("div", Code.Div, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Div_Un = new OpCode("div.un", Code.Div_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Rem = new OpCode("rem", Code.Rem, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Rem_Un = new OpCode("rem.un", Code.Rem_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode And = new OpCode("and", Code.And, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Or = new OpCode("or", Code.Or, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Xor = new OpCode("xor", Code.Xor, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Shl = new OpCode("shl", Code.Shl, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Shr = new OpCode("shr", Code.Shr, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Shr_Un = new OpCode("shr.un", Code.Shr_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Neg = new OpCode("neg", Code.Neg, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Not = new OpCode("not", Code.Not, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_I1 = new OpCode("conv.i1", Code.Conv_I1, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_I2 = new OpCode("conv.i2", Code.Conv_I2, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_I4 = new OpCode("conv.i4", Code.Conv_I4, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_I8 = new OpCode("conv.i8", Code.Conv_I8, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi8, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_R4 = new OpCode("conv.r4", Code.Conv_R4, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushr4, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_R8 = new OpCode("conv.r8", Code.Conv_R8, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushr8, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_U4 = new OpCode("conv.u4", Code.Conv_U4, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_U8 = new OpCode("conv.u8", Code.Conv_U8, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi8, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Callvirt = new OpCode("callvirt", Code.Callvirt, OperandType.InlineMethod, FlowControl.Call, OpCodeType.Objmodel, StackBehaviour.Varpush, StackBehaviour.Varpop);
|
||||||
|
public static readonly OpCode Cpobj = new OpCode("cpobj", Code.Cpobj, OperandType.InlineType, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push0, StackBehaviour.Popi_popi);
|
||||||
|
public static readonly OpCode Ldobj = new OpCode("ldobj", Code.Ldobj, OperandType.InlineType, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push1, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Ldstr = new OpCode("ldstr", Code.Ldstr, OperandType.InlineString, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushref, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Newobj = new OpCode("newobj", Code.Newobj, OperandType.InlineMethod, FlowControl.Call, OpCodeType.Objmodel, StackBehaviour.Pushref, StackBehaviour.Varpop);
|
||||||
|
public static readonly OpCode Castclass = new OpCode("castclass", Code.Castclass, OperandType.InlineType, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushref, StackBehaviour.Popref);
|
||||||
|
public static readonly OpCode Isinst = new OpCode("isinst", Code.Isinst, OperandType.InlineType, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushi, StackBehaviour.Popref);
|
||||||
|
public static readonly OpCode Conv_R_Un = new OpCode("conv.r.un", Code.Conv_R_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushr8, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Unbox = new OpCode("unbox", Code.Unbox, OperandType.InlineType, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Popref);
|
||||||
|
public static readonly OpCode Throw = new OpCode("throw", Code.Throw, OperandType.InlineNone, FlowControl.Throw, OpCodeType.Objmodel, StackBehaviour.Push0, StackBehaviour.Popref);
|
||||||
|
public static readonly OpCode Ldfld = new OpCode("ldfld", Code.Ldfld, OperandType.InlineField, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push1, StackBehaviour.Popref);
|
||||||
|
public static readonly OpCode Ldflda = new OpCode("ldflda", Code.Ldflda, OperandType.InlineField, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushi, StackBehaviour.Popref);
|
||||||
|
public static readonly OpCode Stfld = new OpCode("stfld", Code.Stfld, OperandType.InlineField, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push0, StackBehaviour.Popref_pop1);
|
||||||
|
public static readonly OpCode Ldsfld = new OpCode("ldsfld", Code.Ldsfld, OperandType.InlineField, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push1, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldsflda = new OpCode("ldsflda", Code.Ldsflda, OperandType.InlineField, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Stsfld = new OpCode("stsfld", Code.Stsfld, OperandType.InlineField, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push0, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Stobj = new OpCode("stobj", Code.Stobj, OperandType.InlineType, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Popi_pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_I1_Un= new OpCode("conv.ovf.i1.un", Code.Conv_Ovf_I1_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_I2_Un= new OpCode("conv.ovf.i2.un", Code.Conv_Ovf_I2_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_I4_Un= new OpCode("conv.ovf.i4.un", Code.Conv_Ovf_I4_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_I8_Un= new OpCode("conv.ovf.i8.un", Code.Conv_Ovf_I8_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi8, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_U1_Un= new OpCode("conv.ovf.u1.un", Code.Conv_Ovf_U1_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_U2_Un= new OpCode("conv.ovf.u2.un", Code.Conv_Ovf_U2_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_U4_Un= new OpCode("conv.ovf.u4.un", Code.Conv_Ovf_U4_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_U8_Un= new OpCode("conv.ovf.u8.un", Code.Conv_Ovf_U8_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi8, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_I_Un = new OpCode("conv.ovf.i.un", Code.Conv_Ovf_I_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_U_Un = new OpCode("conv.ovf.u.un", Code.Conv_Ovf_U_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Box = new OpCode("box", Code.Box, OperandType.InlineType, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushref, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Newarr = new OpCode("newarr", Code.Newarr, OperandType.InlineType, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushref, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Ldlen = new OpCode("ldlen", Code.Ldlen, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushi, StackBehaviour.Popref);
|
||||||
|
public static readonly OpCode Ldelema = new OpCode("ldelema", Code.Ldelema, OperandType.InlineType, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushi, StackBehaviour.Popref_popi);
|
||||||
|
public static readonly OpCode Ldelem_I1 = new OpCode("ldelem.i1", Code.Ldelem_I1, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushi, StackBehaviour.Popref_popi);
|
||||||
|
public static readonly OpCode Ldelem_U1 = new OpCode("ldelem.u1", Code.Ldelem_U1, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushi, StackBehaviour.Popref_popi);
|
||||||
|
public static readonly OpCode Ldelem_I2 = new OpCode("ldelem.i2", Code.Ldelem_I2, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushi, StackBehaviour.Popref_popi);
|
||||||
|
public static readonly OpCode Ldelem_U2 = new OpCode("ldelem.u2", Code.Ldelem_U2, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushi, StackBehaviour.Popref_popi);
|
||||||
|
public static readonly OpCode Ldelem_I4 = new OpCode("ldelem.i4", Code.Ldelem_I4, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushi, StackBehaviour.Popref_popi);
|
||||||
|
public static readonly OpCode Ldelem_U4 = new OpCode("ldelem.u4", Code.Ldelem_U4, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushi, StackBehaviour.Popref_popi);
|
||||||
|
public static readonly OpCode Ldelem_I8 = new OpCode("ldelem.i8", Code.Ldelem_I8, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushi8, StackBehaviour.Popref_popi);
|
||||||
|
public static readonly OpCode Ldelem_I = new OpCode("ldelem.i", Code.Ldelem_I, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushi, StackBehaviour.Popref_popi);
|
||||||
|
public static readonly OpCode Ldelem_R4 = new OpCode("ldelem.r4", Code.Ldelem_R4, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushr4, StackBehaviour.Popref_popi);
|
||||||
|
public static readonly OpCode Ldelem_R8 = new OpCode("ldelem.r8", Code.Ldelem_R8, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushr8, StackBehaviour.Popref_popi);
|
||||||
|
public static readonly OpCode Ldelem_Ref = new OpCode("ldelem.ref", Code.Ldelem_Ref, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Pushref, StackBehaviour.Popref_popi);
|
||||||
|
public static readonly OpCode Stelem_I = new OpCode("stelem.i", Code.Stelem_I, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push0, StackBehaviour.Popref_popi_popi);
|
||||||
|
public static readonly OpCode Stelem_I1 = new OpCode("stelem.i1", Code.Stelem_I1, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push0, StackBehaviour.Popref_popi_popi);
|
||||||
|
public static readonly OpCode Stelem_I2 = new OpCode("stelem.i2", Code.Stelem_I2, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push0, StackBehaviour.Popref_popi_popi);
|
||||||
|
public static readonly OpCode Stelem_I4 = new OpCode("stelem.i4", Code.Stelem_I4, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push0, StackBehaviour.Popref_popi_popi);
|
||||||
|
public static readonly OpCode Stelem_I8 = new OpCode("stelem.i8", Code.Stelem_I8, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push0, StackBehaviour.Popref_popi_popi8);
|
||||||
|
public static readonly OpCode Stelem_R4 = new OpCode("stelem.r4", Code.Stelem_R4, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push0, StackBehaviour.Popref_popi_popr4);
|
||||||
|
public static readonly OpCode Stelem_R8 = new OpCode("stelem.r8", Code.Stelem_R8, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push0, StackBehaviour.Popref_popi_popr8);
|
||||||
|
public static readonly OpCode Stelem_Ref = new OpCode("stelem.ref", Code.Stelem_Ref, OperandType.InlineNone, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push0, StackBehaviour.Popref_popi_popref);
|
||||||
|
public static readonly OpCode Ldelem = new OpCode("ldelem", Code.Ldelem, OperandType.InlineType, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push1, StackBehaviour.Popref_popi);
|
||||||
|
public static readonly OpCode Stelem = new OpCode("stelem", Code.Stelem, OperandType.InlineType, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push0, StackBehaviour.Popref_popi_pop1);
|
||||||
|
public static readonly OpCode Unbox_Any = new OpCode("unbox.any", Code.Unbox_Any, OperandType.InlineType, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push1, StackBehaviour.Popref);
|
||||||
|
public static readonly OpCode Conv_Ovf_I1 = new OpCode("conv.ovf.i1", Code.Conv_Ovf_I1, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_U1 = new OpCode("conv.ovf.u1", Code.Conv_Ovf_U1, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_I2 = new OpCode("conv.ovf.i2", Code.Conv_Ovf_I2, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_U2 = new OpCode("conv.ovf.u2", Code.Conv_Ovf_U2, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_I4 = new OpCode("conv.ovf.i4", Code.Conv_Ovf_I4, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_U4 = new OpCode("conv.ovf.u4", Code.Conv_Ovf_U4, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_I8 = new OpCode("conv.ovf.i8", Code.Conv_Ovf_I8, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi8, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_U8 = new OpCode("conv.ovf.u8", Code.Conv_Ovf_U8, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi8, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Refanyval = new OpCode("refanyval", Code.Refanyval, OperandType.InlineType, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Ckfinite = new OpCode("ckfinite", Code.Ckfinite, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushr8, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Mkrefany = new OpCode("mkrefany", Code.Mkrefany, OperandType.InlineType, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Ldtoken = new OpCode("ldtoken", Code.Ldtoken, OperandType.InlineTok, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Conv_U2 = new OpCode("conv.u2", Code.Conv_U2, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_U1 = new OpCode("conv.u1", Code.Conv_U1, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_I = new OpCode("conv.i", Code.Conv_I, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_I = new OpCode("conv.ovf.i", Code.Conv_Ovf_I, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Conv_Ovf_U = new OpCode("conv.ovf.u", Code.Conv_Ovf_U, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Add_Ovf = new OpCode("add.ovf", Code.Add_Ovf, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Add_Ovf_Un = new OpCode("add.ovf.un", Code.Add_Ovf_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Mul_Ovf = new OpCode("mul.ovf", Code.Mul_Ovf, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Mul_Ovf_Un = new OpCode("mul.ovf.un", Code.Mul_Ovf_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Sub_Ovf = new OpCode("sub.ovf", Code.Sub_Ovf, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Sub_Ovf_Un = new OpCode("sub.ovf.un", Code.Sub_Ovf_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Endfinally = new OpCode("endfinally", Code.Endfinally, OperandType.InlineNone, FlowControl.Return, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.PopAll);
|
||||||
|
public static readonly OpCode Leave = new OpCode("leave", Code.Leave, OperandType.InlineBrTarget, FlowControl.Branch, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.PopAll);
|
||||||
|
public static readonly OpCode Leave_S = new OpCode("leave.s", Code.Leave_S, OperandType.ShortInlineBrTarget, FlowControl.Branch, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.PopAll);
|
||||||
|
public static readonly OpCode Stind_I = new OpCode("stind.i", Code.Stind_I, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Popi_popi);
|
||||||
|
public static readonly OpCode Conv_U = new OpCode("conv.u", Code.Conv_U, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Prefix7 = new OpCode("prefix7", Code.Prefix7, OperandType.InlineNone, FlowControl.Meta, OpCodeType.Nternal, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Prefix6 = new OpCode("prefix6", Code.Prefix6, OperandType.InlineNone, FlowControl.Meta, OpCodeType.Nternal, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Prefix5 = new OpCode("prefix5", Code.Prefix5, OperandType.InlineNone, FlowControl.Meta, OpCodeType.Nternal, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Prefix4 = new OpCode("prefix4", Code.Prefix4, OperandType.InlineNone, FlowControl.Meta, OpCodeType.Nternal, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Prefix3 = new OpCode("prefix3", Code.Prefix3, OperandType.InlineNone, FlowControl.Meta, OpCodeType.Nternal, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Prefix2 = new OpCode("prefix2", Code.Prefix2, OperandType.InlineNone, FlowControl.Meta, OpCodeType.Nternal, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Prefix1 = new OpCode("prefix1", Code.Prefix1, OperandType.InlineNone, FlowControl.Meta, OpCodeType.Nternal, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Prefixref = new OpCode("prefixref", Code.Prefixref, OperandType.InlineNone, FlowControl.Meta, OpCodeType.Nternal, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Arglist = new OpCode("arglist", Code.Arglist, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ceq = new OpCode("ceq", Code.Ceq, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Cgt = new OpCode("cgt", Code.Cgt, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Cgt_Un = new OpCode("cgt.un", Code.Cgt_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Clt = new OpCode("clt", Code.Clt, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Clt_Un = new OpCode("clt.un", Code.Clt_Un, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1_pop1);
|
||||||
|
public static readonly OpCode Ldftn = new OpCode("ldftn", Code.Ldftn, OperandType.InlineMethod, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldvirtftn = new OpCode("ldvirtftn", Code.Ldvirtftn, OperandType.InlineMethod, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Popref);
|
||||||
|
public static readonly OpCode Ldarg = new OpCode("ldarg", Code.Ldarg, OperandType.InlineVar, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldarga = new OpCode("ldarga", Code.Ldarga, OperandType.InlineVar, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Starg = new OpCode("starg", Code.Starg, OperandType.InlineVar, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Ldloc = new OpCode("ldloc", Code.Ldloc, OperandType.InlineVar, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Ldloca = new OpCode("ldloca", Code.Ldloca, OperandType.InlineVar, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Stloc = new OpCode("stloc", Code.Stloc, OperandType.InlineVar, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Localloc = new OpCode("localloc", Code.Localloc, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Endfilter = new OpCode("endfilter", Code.Endfilter, OperandType.InlineNone, FlowControl.Return, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Unaligned = new OpCode("unaligned.", Code.Unaligned, OperandType.ShortInlineI, FlowControl.Meta, OpCodeType.Prefix, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Volatile = new OpCode("volatile.", Code.Volatile, OperandType.InlineNone, FlowControl.Meta, OpCodeType.Prefix, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Tailcall = new OpCode("tail.", Code.Tailcall, OperandType.InlineNone, FlowControl.Meta, OpCodeType.Prefix, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Initobj = new OpCode("initobj", Code.Initobj, OperandType.InlineType, FlowControl.Next, OpCodeType.Objmodel, StackBehaviour.Push0, StackBehaviour.Popi);
|
||||||
|
public static readonly OpCode Constrained = new OpCode("constrained.", Code.Constrained, OperandType.InlineType, FlowControl.Meta, OpCodeType.Prefix, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Cpblk = new OpCode("cpblk", Code.Cpblk, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Popi_popi_popi);
|
||||||
|
public static readonly OpCode Initblk = new OpCode("initblk", Code.Initblk, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Popi_popi_popi);
|
||||||
|
public static readonly OpCode No = new OpCode("no.", Code.No, OperandType.ShortInlineI, FlowControl.Meta, OpCodeType.Prefix, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Rethrow = new OpCode("rethrow", Code.Rethrow, OperandType.InlineNone, FlowControl.Throw, OpCodeType.Objmodel, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Sizeof = new OpCode("sizeof", Code.Sizeof, OperandType.InlineType, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop0);
|
||||||
|
public static readonly OpCode Refanytype = new OpCode("refanytype", Code.Refanytype, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1);
|
||||||
|
public static readonly OpCode Readonly = new OpCode("readonly.", Code.Readonly, OperandType.InlineNone, FlowControl.Meta, OpCodeType.Prefix, StackBehaviour.Push0, StackBehaviour.Pop0);
|
||||||
|
#pragma warning restore
|
||||||
|
|
||||||
|
static OpCodes() {
|
||||||
|
// The OpCode ctor copies itself to one of these arrays. Whatever are still null
|
||||||
|
// are unsupported opcodes. Set them all to UNKNOWN1 or UNKNOWN2.
|
||||||
|
for (int i = 0; i < OneByteOpCodes.Length; i++) {
|
||||||
|
if (OneByteOpCodes[i] is null)
|
||||||
|
OneByteOpCodes[i] = UNKNOWN1;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < TwoByteOpCodes.Length; i++) {
|
||||||
|
if (TwoByteOpCodes[i] is null)
|
||||||
|
TwoByteOpCodes[i] = UNKNOWN2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using dnlib.DotNet.MD;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// CIL opcode operand type
|
||||||
|
/// </summary>
|
||||||
|
public enum OperandType : byte {
|
||||||
|
/// <summary>4-byte relative instruction offset</summary>
|
||||||
|
InlineBrTarget,
|
||||||
|
/// <summary>4-byte field token (<see cref="Table.Field"/> or <see cref="Table.MemberRef"/>)</summary>
|
||||||
|
InlineField,
|
||||||
|
/// <summary>int32</summary>
|
||||||
|
InlineI,
|
||||||
|
/// <summary>int64</summary>
|
||||||
|
InlineI8,
|
||||||
|
/// <summary>4-byte method token (<see cref="Table.Method"/>, <see cref="Table.MemberRef"/>
|
||||||
|
/// or <see cref="Table.MethodSpec"/>)</summary>
|
||||||
|
InlineMethod,
|
||||||
|
/// <summary>No operand</summary>
|
||||||
|
InlineNone,
|
||||||
|
/// <summary>Never used</summary>
|
||||||
|
InlinePhi,
|
||||||
|
/// <summary>64-bit real</summary>
|
||||||
|
InlineR,
|
||||||
|
/// <summary/>
|
||||||
|
NOT_USED_8,
|
||||||
|
/// <summary>4-byte method sig token (<see cref="Table.StandAloneSig"/>)</summary>
|
||||||
|
InlineSig,
|
||||||
|
/// <summary>4-byte string token (<c>0x70xxxxxx</c>)</summary>
|
||||||
|
InlineString,
|
||||||
|
/// <summary>4-byte count N followed by N 4-byte relative instruction offsets</summary>
|
||||||
|
InlineSwitch,
|
||||||
|
/// <summary>4-byte token (<see cref="Table.Field"/>, <see cref="Table.MemberRef"/>,
|
||||||
|
/// <see cref="Table.Method"/>, <see cref="Table.MethodSpec"/>, <see cref="Table.TypeDef"/>,
|
||||||
|
/// <see cref="Table.TypeRef"/> or <see cref="Table.TypeSpec"/>)</summary>
|
||||||
|
InlineTok,
|
||||||
|
/// <summary>4-byte type token (<see cref="Table.TypeDef"/>, <see cref="Table.TypeRef"/> or
|
||||||
|
/// <see cref="Table.TypeSpec"/>)</summary>
|
||||||
|
InlineType,
|
||||||
|
/// <summary>2-byte param/local index</summary>
|
||||||
|
InlineVar,
|
||||||
|
/// <summary>1-byte relative instruction offset</summary>
|
||||||
|
ShortInlineBrTarget,
|
||||||
|
/// <summary>1-byte sbyte (<see cref="Code.Ldc_I4_S"/>) or byte (the rest)</summary>
|
||||||
|
ShortInlineI,
|
||||||
|
/// <summary>32-bit real</summary>
|
||||||
|
ShortInlineR,
|
||||||
|
/// <summary>1-byte param/local index</summary>
|
||||||
|
ShortInlineVar,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.Emit {
|
||||||
|
/// <summary>
|
||||||
|
/// CIL opcode stack behavior
|
||||||
|
/// </summary>
|
||||||
|
public enum StackBehaviour : byte {
|
||||||
|
/// <summary/>
|
||||||
|
Pop0,
|
||||||
|
/// <summary/>
|
||||||
|
Pop1,
|
||||||
|
/// <summary/>
|
||||||
|
Pop1_pop1,
|
||||||
|
/// <summary/>
|
||||||
|
Popi,
|
||||||
|
/// <summary/>
|
||||||
|
Popi_pop1,
|
||||||
|
/// <summary/>
|
||||||
|
Popi_popi,
|
||||||
|
/// <summary/>
|
||||||
|
Popi_popi8,
|
||||||
|
/// <summary/>
|
||||||
|
Popi_popi_popi,
|
||||||
|
/// <summary/>
|
||||||
|
Popi_popr4,
|
||||||
|
/// <summary/>
|
||||||
|
Popi_popr8,
|
||||||
|
/// <summary/>
|
||||||
|
Popref,
|
||||||
|
/// <summary/>
|
||||||
|
Popref_pop1,
|
||||||
|
/// <summary/>
|
||||||
|
Popref_popi,
|
||||||
|
/// <summary/>
|
||||||
|
Popref_popi_popi,
|
||||||
|
/// <summary/>
|
||||||
|
Popref_popi_popi8,
|
||||||
|
/// <summary/>
|
||||||
|
Popref_popi_popr4,
|
||||||
|
/// <summary/>
|
||||||
|
Popref_popi_popr8,
|
||||||
|
/// <summary/>
|
||||||
|
Popref_popi_popref,
|
||||||
|
/// <summary/>
|
||||||
|
Push0,
|
||||||
|
/// <summary/>
|
||||||
|
Push1,
|
||||||
|
/// <summary/>
|
||||||
|
Push1_push1,
|
||||||
|
/// <summary/>
|
||||||
|
Pushi,
|
||||||
|
/// <summary/>
|
||||||
|
Pushi8,
|
||||||
|
/// <summary/>
|
||||||
|
Pushr4,
|
||||||
|
/// <summary/>
|
||||||
|
Pushr8,
|
||||||
|
/// <summary/>
|
||||||
|
Pushref,
|
||||||
|
/// <summary/>
|
||||||
|
Varpop,
|
||||||
|
/// <summary/>
|
||||||
|
Varpush,
|
||||||
|
/// <summary/>
|
||||||
|
Popref_popi_pop1,
|
||||||
|
/// <summary/>
|
||||||
|
PopAll = 0xFF,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Event attributes, see CorHdr.h/CorEventAttr
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum EventAttributes : ushort {
|
||||||
|
/// <summary>event is special. Name describes how.</summary>
|
||||||
|
SpecialName = 0x0200,
|
||||||
|
/// <summary>Runtime(metadata internal APIs) should check name encoding.</summary>
|
||||||
|
RTSpecialName = 0x0400,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,416 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
using dnlib.DotNet.MD;
|
||||||
|
using dnlib.DotNet.Pdb;
|
||||||
|
using dnlib.Threading;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// A high-level representation of a row in the Event table
|
||||||
|
/// </summary>
|
||||||
|
public abstract class EventDef : IHasCustomAttribute, IHasSemantic, IHasCustomDebugInformation, IFullName, IMemberDef {
|
||||||
|
/// <summary>
|
||||||
|
/// The row id in its table
|
||||||
|
/// </summary>
|
||||||
|
protected uint rid;
|
||||||
|
|
||||||
|
#if THREAD_SAFE
|
||||||
|
readonly Lock theLock = Lock.Create();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public MDToken MDToken => new MDToken(Table.Event, rid);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint Rid {
|
||||||
|
get => rid;
|
||||||
|
set => rid = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomAttributeTag => 10;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasSemanticTag => 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column Event.EventFlags
|
||||||
|
/// </summary>
|
||||||
|
public EventAttributes Attributes {
|
||||||
|
get => (EventAttributes)attributes;
|
||||||
|
set => attributes = (int)value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected int attributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column Event.Name
|
||||||
|
/// </summary>
|
||||||
|
public UTF8String Name {
|
||||||
|
get => name;
|
||||||
|
set => name = value;
|
||||||
|
}
|
||||||
|
/// <summary>Name</summary>
|
||||||
|
protected UTF8String name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column Event.EventType
|
||||||
|
/// </summary>
|
||||||
|
public ITypeDefOrRef EventType {
|
||||||
|
get => eventType;
|
||||||
|
set => eventType = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected ITypeDefOrRef eventType;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom attributes
|
||||||
|
/// </summary>
|
||||||
|
public CustomAttributeCollection CustomAttributes {
|
||||||
|
get {
|
||||||
|
if (customAttributes is null)
|
||||||
|
InitializeCustomAttributes();
|
||||||
|
return customAttributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected CustomAttributeCollection customAttributes;
|
||||||
|
/// <summary>Initializes <see cref="customAttributes"/></summary>
|
||||||
|
protected virtual void InitializeCustomAttributes() =>
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomDebugInformationTag => 10;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom debug infos
|
||||||
|
/// </summary>
|
||||||
|
public IList<PdbCustomDebugInfo> CustomDebugInfos {
|
||||||
|
get {
|
||||||
|
if (customDebugInfos is null)
|
||||||
|
InitializeCustomDebugInfos();
|
||||||
|
return customDebugInfos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected IList<PdbCustomDebugInfo> customDebugInfos;
|
||||||
|
/// <summary>Initializes <see cref="customDebugInfos"/></summary>
|
||||||
|
protected virtual void InitializeCustomDebugInfos() =>
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, new List<PdbCustomDebugInfo>(), null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the adder method
|
||||||
|
/// </summary>
|
||||||
|
public MethodDef AddMethod {
|
||||||
|
get {
|
||||||
|
if (otherMethods is null)
|
||||||
|
InitializeEventMethods();
|
||||||
|
return addMethod;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if (otherMethods is null)
|
||||||
|
InitializeEventMethods();
|
||||||
|
addMethod = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the invoker method
|
||||||
|
/// </summary>
|
||||||
|
public MethodDef InvokeMethod {
|
||||||
|
get {
|
||||||
|
if (otherMethods is null)
|
||||||
|
InitializeEventMethods();
|
||||||
|
return invokeMethod;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if (otherMethods is null)
|
||||||
|
InitializeEventMethods();
|
||||||
|
invokeMethod = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the remover method
|
||||||
|
/// </summary>
|
||||||
|
public MethodDef RemoveMethod {
|
||||||
|
get {
|
||||||
|
if (otherMethods is null)
|
||||||
|
InitializeEventMethods();
|
||||||
|
return removeMethod;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if (otherMethods is null)
|
||||||
|
InitializeEventMethods();
|
||||||
|
removeMethod = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the other methods
|
||||||
|
/// </summary>
|
||||||
|
public IList<MethodDef> OtherMethods {
|
||||||
|
get {
|
||||||
|
if (otherMethods is null)
|
||||||
|
InitializeEventMethods();
|
||||||
|
return otherMethods;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeEventMethods() {
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
if (otherMethods is null)
|
||||||
|
InitializeEventMethods_NoLock();
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes <see cref="otherMethods"/>, <see cref="addMethod"/>,
|
||||||
|
/// <see cref="invokeMethod"/> and <see cref="removeMethod"/>.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void InitializeEventMethods_NoLock() =>
|
||||||
|
otherMethods = new List<MethodDef>();
|
||||||
|
|
||||||
|
/// <summary/>
|
||||||
|
protected MethodDef addMethod;
|
||||||
|
/// <summary/>
|
||||||
|
protected MethodDef invokeMethod;
|
||||||
|
/// <summary/>
|
||||||
|
protected MethodDef removeMethod;
|
||||||
|
/// <summary/>
|
||||||
|
protected IList<MethodDef> otherMethods;
|
||||||
|
|
||||||
|
/// <summary>Reset <see cref="AddMethod"/>, <see cref="InvokeMethod"/>, <see cref="RemoveMethod"/>, <see cref="OtherMethods"/></summary>
|
||||||
|
protected void ResetMethods() => otherMethods = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if there are no methods attached to this event
|
||||||
|
/// </summary>
|
||||||
|
public bool IsEmpty =>
|
||||||
|
// The first property access initializes the other fields we access here
|
||||||
|
AddMethod is null &&
|
||||||
|
removeMethod is null &&
|
||||||
|
invokeMethod is null &&
|
||||||
|
otherMethods.Count == 0;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomAttributes => CustomAttributes.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="OtherMethods"/> is not empty
|
||||||
|
/// </summary>
|
||||||
|
public bool HasOtherMethods => OtherMethods.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the declaring type (owner type)
|
||||||
|
/// </summary>
|
||||||
|
public TypeDef DeclaringType {
|
||||||
|
get => declaringType2;
|
||||||
|
set {
|
||||||
|
var currentDeclaringType = DeclaringType2;
|
||||||
|
if (currentDeclaringType == value)
|
||||||
|
return;
|
||||||
|
if (currentDeclaringType is not null)
|
||||||
|
currentDeclaringType.Events.Remove(this); // Will set DeclaringType2 = null
|
||||||
|
if (value is not null)
|
||||||
|
value.Events.Add(this); // Will set DeclaringType2 = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
ITypeDefOrRef IMemberRef.DeclaringType => declaringType2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called by <see cref="DeclaringType"/> and should normally not be called by any user
|
||||||
|
/// code. Use <see cref="DeclaringType"/> instead. Only call this if you must set the
|
||||||
|
/// declaring type without inserting it in the declaring type's method list.
|
||||||
|
/// </summary>
|
||||||
|
public TypeDef DeclaringType2 {
|
||||||
|
get => declaringType2;
|
||||||
|
set => declaringType2 = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected TypeDef declaringType2;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ModuleDef Module => declaringType2?.Module;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the full name of the event
|
||||||
|
/// </summary>
|
||||||
|
public string FullName => FullNameFactory.EventFullName(declaringType2?.FullName, name, eventType, null, null);
|
||||||
|
|
||||||
|
bool IIsTypeOrMethod.IsType => false;
|
||||||
|
bool IIsTypeOrMethod.IsMethod => false;
|
||||||
|
bool IMemberRef.IsField => false;
|
||||||
|
bool IMemberRef.IsTypeSpec => false;
|
||||||
|
bool IMemberRef.IsTypeRef => false;
|
||||||
|
bool IMemberRef.IsTypeDef => false;
|
||||||
|
bool IMemberRef.IsMethodSpec => false;
|
||||||
|
bool IMemberRef.IsMethodDef => false;
|
||||||
|
bool IMemberRef.IsMemberRef => false;
|
||||||
|
bool IMemberRef.IsFieldDef => false;
|
||||||
|
bool IMemberRef.IsPropertyDef => false;
|
||||||
|
bool IMemberRef.IsEventDef => true;
|
||||||
|
bool IMemberRef.IsGenericParam => false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set or clear flags in <see cref="attributes"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="set"><c>true</c> if flags should be set, <c>false</c> if flags should
|
||||||
|
/// be cleared</param>
|
||||||
|
/// <param name="flags">Flags to set or clear</param>
|
||||||
|
void ModifyAttributes(bool set, EventAttributes flags) {
|
||||||
|
if (set)
|
||||||
|
attributes |= (int)flags;
|
||||||
|
else
|
||||||
|
attributes &= ~(int)flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="EventAttributes.SpecialName"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSpecialName {
|
||||||
|
get => ((EventAttributes)attributes & EventAttributes.SpecialName) != 0;
|
||||||
|
set => ModifyAttributes(value, EventAttributes.SpecialName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="EventAttributes.RTSpecialName"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRuntimeSpecialName {
|
||||||
|
get => ((EventAttributes)attributes & EventAttributes.RTSpecialName) != 0;
|
||||||
|
set => ModifyAttributes(value, EventAttributes.RTSpecialName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString() => FullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An Event row created by the user and not present in the original .NET file
|
||||||
|
/// </summary>
|
||||||
|
public class EventDefUser : EventDef {
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public EventDefUser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name</param>
|
||||||
|
public EventDefUser(UTF8String name)
|
||||||
|
: this(name, null, 0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name</param>
|
||||||
|
/// <param name="type">Type</param>
|
||||||
|
public EventDefUser(UTF8String name, ITypeDefOrRef type)
|
||||||
|
: this(name, type, 0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name</param>
|
||||||
|
/// <param name="type">Type</param>
|
||||||
|
/// <param name="flags">Flags</param>
|
||||||
|
public EventDefUser(UTF8String name, ITypeDefOrRef type, EventAttributes flags) {
|
||||||
|
this.name = name;
|
||||||
|
eventType = type;
|
||||||
|
attributes = (int)flags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Created from a row in the Event table
|
||||||
|
/// </summary>
|
||||||
|
sealed class EventDefMD : EventDef, IMDTokenProviderMD {
|
||||||
|
/// <summary>The module where this instance is located</summary>
|
||||||
|
readonly ModuleDefMD readerModule;
|
||||||
|
|
||||||
|
readonly uint origRid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint OrigRid => origRid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomAttributes() {
|
||||||
|
var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Event, origRid);
|
||||||
|
var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index]));
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, tmp, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomDebugInfos() {
|
||||||
|
var list = new List<PdbCustomDebugInfo>();
|
||||||
|
readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(declaringType2), list);
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, list, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readerModule">The module which contains this <c>Event</c> row</param>
|
||||||
|
/// <param name="rid">Row ID</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If <paramref name="readerModule"/> is <c>null</c></exception>
|
||||||
|
/// <exception cref="ArgumentException">If <paramref name="rid"/> is invalid</exception>
|
||||||
|
public EventDefMD(ModuleDefMD readerModule, uint rid) {
|
||||||
|
#if DEBUG
|
||||||
|
if (readerModule is null)
|
||||||
|
throw new ArgumentNullException("readerModule");
|
||||||
|
if (readerModule.TablesStream.EventTable.IsInvalidRID(rid))
|
||||||
|
throw new BadImageFormatException($"Event rid {rid} does not exist");
|
||||||
|
#endif
|
||||||
|
origRid = rid;
|
||||||
|
this.rid = rid;
|
||||||
|
this.readerModule = readerModule;
|
||||||
|
bool b = readerModule.TablesStream.TryReadEventRow(origRid, out var row);
|
||||||
|
Debug.Assert(b);
|
||||||
|
attributes = row.EventFlags;
|
||||||
|
name = readerModule.StringsStream.ReadNoNull(row.Name);
|
||||||
|
declaringType2 = readerModule.GetOwnerType(this);
|
||||||
|
eventType = readerModule.ResolveTypeDefOrRef(row.EventType, new GenericParamContext(declaringType2));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal EventDefMD InitializeAll() {
|
||||||
|
MemberMDInitializer.Initialize(Attributes);
|
||||||
|
MemberMDInitializer.Initialize(Name);
|
||||||
|
MemberMDInitializer.Initialize(EventType);
|
||||||
|
MemberMDInitializer.Initialize(CustomAttributes);
|
||||||
|
MemberMDInitializer.Initialize(AddMethod);
|
||||||
|
MemberMDInitializer.Initialize(InvokeMethod);
|
||||||
|
MemberMDInitializer.Initialize(RemoveMethod);
|
||||||
|
MemberMDInitializer.Initialize(OtherMethods);
|
||||||
|
MemberMDInitializer.Initialize(DeclaringType);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeEventMethods_NoLock() {
|
||||||
|
IList<MethodDef> newOtherMethods;
|
||||||
|
var dt = declaringType2 as TypeDefMD;
|
||||||
|
if (dt is null)
|
||||||
|
newOtherMethods = new List<MethodDef>();
|
||||||
|
else
|
||||||
|
dt.InitializeEvent(this, out addMethod, out invokeMethod, out removeMethod, out newOtherMethods);
|
||||||
|
otherMethods = newOtherMethods;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,690 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using dnlib.DotNet.MD;
|
||||||
|
using dnlib.DotNet.Pdb;
|
||||||
|
using dnlib.Threading;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// A high-level representation of a row in the ExportedType table
|
||||||
|
/// </summary>
|
||||||
|
public abstract class ExportedType : IHasCustomAttribute, IImplementation, IHasCustomDebugInformation, IType {
|
||||||
|
/// <summary>
|
||||||
|
/// The row id in its table
|
||||||
|
/// </summary>
|
||||||
|
protected uint rid;
|
||||||
|
|
||||||
|
#if THREAD_SAFE
|
||||||
|
readonly Lock theLock = Lock.Create();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The owner module
|
||||||
|
/// </summary>
|
||||||
|
protected ModuleDef module;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public MDToken MDToken => new MDToken(Table.ExportedType, rid);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint Rid {
|
||||||
|
get => rid;
|
||||||
|
set => rid = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomAttributeTag => 17;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int ImplementationTag => 2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom attributes
|
||||||
|
/// </summary>
|
||||||
|
public CustomAttributeCollection CustomAttributes {
|
||||||
|
get {
|
||||||
|
if (customAttributes is null)
|
||||||
|
InitializeCustomAttributes();
|
||||||
|
return customAttributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected CustomAttributeCollection customAttributes;
|
||||||
|
/// <summary>Initializes <see cref="customAttributes"/></summary>
|
||||||
|
protected virtual void InitializeCustomAttributes() =>
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomAttributes => CustomAttributes.Count > 0;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomDebugInformationTag => 17;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom debug infos
|
||||||
|
/// </summary>
|
||||||
|
public IList<PdbCustomDebugInfo> CustomDebugInfos {
|
||||||
|
get {
|
||||||
|
if (customDebugInfos is null)
|
||||||
|
InitializeCustomDebugInfos();
|
||||||
|
return customDebugInfos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected IList<PdbCustomDebugInfo> customDebugInfos;
|
||||||
|
/// <summary>Initializes <see cref="customDebugInfos"/></summary>
|
||||||
|
protected virtual void InitializeCustomDebugInfos() =>
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, new List<PdbCustomDebugInfo>(), null);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool IsValueType {
|
||||||
|
get {
|
||||||
|
var td = Resolve();
|
||||||
|
return td is not null && td.IsValueType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool IsPrimitive => this.IsPrimitive();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
string IType.TypeName => TypeName;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public UTF8String Name {
|
||||||
|
get => typeName;
|
||||||
|
set => typeName = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public string ReflectionName => FullNameFactory.Name(this, true, null);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public string Namespace => TypeNamespace;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public string ReflectionNamespace => FullNameFactory.Namespace(this, true, null);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public string FullName => FullNameFactory.FullName(this, false, null, null);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public string ReflectionFullName => FullNameFactory.FullName(this, true, null, null);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public string AssemblyQualifiedName => FullNameFactory.AssemblyQualifiedName(this, null, null);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public IAssembly DefinitionAssembly => FullNameFactory.DefinitionAssembly(this);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public IScope Scope => FullNameFactory.Scope(this);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ITypeDefOrRef ScopeType => FullNameFactory.ScopeType(this);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Always returns <c>false</c> since a <see cref="ExportedType"/> does not contain any
|
||||||
|
/// <see cref="GenericVar"/> or <see cref="GenericMVar"/>.
|
||||||
|
/// </summary>
|
||||||
|
public bool ContainsGenericParameter => false;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ModuleDef Module => module;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
bool IIsTypeOrMethod.IsMethod => false;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
bool IIsTypeOrMethod.IsType => true;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
int IGenericParameterProvider.NumberOfGenericParameters => 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column ExportedType.Flags
|
||||||
|
/// </summary>
|
||||||
|
public TypeAttributes Attributes {
|
||||||
|
get => (TypeAttributes)attributes;
|
||||||
|
set => attributes = (int)value;
|
||||||
|
}
|
||||||
|
/// <summary>Attributes</summary>
|
||||||
|
protected int attributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column ExportedType.TypeDefId
|
||||||
|
/// </summary>
|
||||||
|
public uint TypeDefId {
|
||||||
|
get => typeDefId;
|
||||||
|
set => typeDefId = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected uint typeDefId;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column ExportedType.TypeName
|
||||||
|
/// </summary>
|
||||||
|
public UTF8String TypeName {
|
||||||
|
get => typeName;
|
||||||
|
set => typeName = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected UTF8String typeName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column ExportedType.TypeNamespace
|
||||||
|
/// </summary>
|
||||||
|
public UTF8String TypeNamespace {
|
||||||
|
get => typeNamespace;
|
||||||
|
set => typeNamespace = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected UTF8String typeNamespace;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column ExportedType.Implementation
|
||||||
|
/// </summary>
|
||||||
|
public IImplementation Implementation {
|
||||||
|
get {
|
||||||
|
if (!implementation_isInitialized)
|
||||||
|
InitializeImplementation();
|
||||||
|
return implementation;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
implementation = value;
|
||||||
|
implementation_isInitialized = true;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected IImplementation implementation;
|
||||||
|
/// <summary/>
|
||||||
|
protected bool implementation_isInitialized;
|
||||||
|
|
||||||
|
void InitializeImplementation() {
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
if (implementation_isInitialized)
|
||||||
|
return;
|
||||||
|
implementation = GetImplementation_NoLock();
|
||||||
|
implementation_isInitialized = true;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Called to initialize <see cref="implementation"/></summary>
|
||||||
|
protected virtual IImplementation GetImplementation_NoLock() => null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if it's nested within another <see cref="ExportedType"/>
|
||||||
|
/// </summary>
|
||||||
|
public bool IsNested => DeclaringType is not null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the declaring type, if any
|
||||||
|
/// </summary>
|
||||||
|
public ExportedType DeclaringType {
|
||||||
|
get {
|
||||||
|
if (!implementation_isInitialized)
|
||||||
|
InitializeImplementation();
|
||||||
|
return implementation as ExportedType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Modify <see cref="attributes"/> property: <see cref="attributes"/> =
|
||||||
|
/// (<see cref="attributes"/> & <paramref name="andMask"/>) | <paramref name="orMask"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="andMask">Value to <c>AND</c></param>
|
||||||
|
/// <param name="orMask">Value to OR</param>
|
||||||
|
void ModifyAttributes(TypeAttributes andMask, TypeAttributes orMask) =>
|
||||||
|
attributes = (attributes & (int)andMask) | (int)orMask;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set or clear flags in <see cref="attributes"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="set"><c>true</c> if flags should be set, <c>false</c> if flags should
|
||||||
|
/// be cleared</param>
|
||||||
|
/// <param name="flags">Flags to set or clear</param>
|
||||||
|
void ModifyAttributes(bool set, TypeAttributes flags) {
|
||||||
|
if (set)
|
||||||
|
attributes |= (int)flags;
|
||||||
|
else
|
||||||
|
attributes &= ~(int)flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the visibility
|
||||||
|
/// </summary>
|
||||||
|
public TypeAttributes Visibility {
|
||||||
|
get => (TypeAttributes)attributes & TypeAttributes.VisibilityMask;
|
||||||
|
set => ModifyAttributes(~TypeAttributes.VisibilityMask, value & TypeAttributes.VisibilityMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="TypeAttributes.NotPublic"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsNotPublic => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NotPublic;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="TypeAttributes.Public"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPublic => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="TypeAttributes.NestedPublic"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsNestedPublic => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="TypeAttributes.NestedPrivate"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsNestedPrivate => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPrivate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="TypeAttributes.NestedFamily"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsNestedFamily => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="TypeAttributes.NestedAssembly"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsNestedAssembly => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedAssembly;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="TypeAttributes.NestedFamANDAssem"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsNestedFamilyAndAssembly => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamANDAssem;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="TypeAttributes.NestedFamORAssem"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsNestedFamilyOrAssembly => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the layout
|
||||||
|
/// </summary>
|
||||||
|
public TypeAttributes Layout {
|
||||||
|
get => (TypeAttributes)attributes & TypeAttributes.LayoutMask;
|
||||||
|
set => ModifyAttributes(~TypeAttributes.LayoutMask, value & TypeAttributes.LayoutMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="TypeAttributes.AutoLayout"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsAutoLayout => ((TypeAttributes)attributes & TypeAttributes.LayoutMask) == TypeAttributes.AutoLayout;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="TypeAttributes.SequentialLayout"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSequentialLayout => ((TypeAttributes)attributes & TypeAttributes.LayoutMask) == TypeAttributes.SequentialLayout;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="TypeAttributes.ExplicitLayout"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsExplicitLayout => ((TypeAttributes)attributes & TypeAttributes.LayoutMask) == TypeAttributes.ExplicitLayout;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="TypeAttributes.Interface"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsInterface {
|
||||||
|
get => ((TypeAttributes)attributes & TypeAttributes.Interface) != 0;
|
||||||
|
set => ModifyAttributes(value, TypeAttributes.Interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="TypeAttributes.Class"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsClass {
|
||||||
|
get => ((TypeAttributes)attributes & TypeAttributes.Interface) == 0;
|
||||||
|
set => ModifyAttributes(!value, TypeAttributes.Interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="TypeAttributes.Abstract"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsAbstract {
|
||||||
|
get => ((TypeAttributes)attributes & TypeAttributes.Abstract) != 0;
|
||||||
|
set => ModifyAttributes(value, TypeAttributes.Abstract);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="TypeAttributes.Sealed"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSealed {
|
||||||
|
get => ((TypeAttributes)attributes & TypeAttributes.Sealed) != 0;
|
||||||
|
set => ModifyAttributes(value, TypeAttributes.Sealed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="TypeAttributes.SpecialName"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSpecialName {
|
||||||
|
get => ((TypeAttributes)attributes & TypeAttributes.SpecialName) != 0;
|
||||||
|
set => ModifyAttributes(value, TypeAttributes.SpecialName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="TypeAttributes.Import"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsImport {
|
||||||
|
get => ((TypeAttributes)attributes & TypeAttributes.Import) != 0;
|
||||||
|
set => ModifyAttributes(value, TypeAttributes.Import);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="TypeAttributes.Serializable"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSerializable {
|
||||||
|
get => ((TypeAttributes)attributes & TypeAttributes.Serializable) != 0;
|
||||||
|
set => ModifyAttributes(value, TypeAttributes.Serializable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="TypeAttributes.WindowsRuntime"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsWindowsRuntime {
|
||||||
|
get => ((TypeAttributes)attributes & TypeAttributes.WindowsRuntime) != 0;
|
||||||
|
set => ModifyAttributes(value, TypeAttributes.WindowsRuntime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the string format
|
||||||
|
/// </summary>
|
||||||
|
public TypeAttributes StringFormat {
|
||||||
|
get => (TypeAttributes)attributes & TypeAttributes.StringFormatMask;
|
||||||
|
set => ModifyAttributes(~TypeAttributes.StringFormatMask, value & TypeAttributes.StringFormatMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="TypeAttributes.AnsiClass"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsAnsiClass => ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.AnsiClass;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="TypeAttributes.UnicodeClass"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsUnicodeClass => ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.UnicodeClass;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="TypeAttributes.AutoClass"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsAutoClass => ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.AutoClass;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="TypeAttributes.CustomFormatClass"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCustomFormatClass => ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.CustomFormatClass;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="TypeAttributes.BeforeFieldInit"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsBeforeFieldInit {
|
||||||
|
get => ((TypeAttributes)attributes & TypeAttributes.BeforeFieldInit) != 0;
|
||||||
|
set => ModifyAttributes(value, TypeAttributes.BeforeFieldInit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="TypeAttributes.Forwarder"/> bit. See also <see cref="MovedToAnotherAssembly"/>
|
||||||
|
/// </summary>
|
||||||
|
public bool IsForwarder {
|
||||||
|
get => ((TypeAttributes)attributes & TypeAttributes.Forwarder) != 0;
|
||||||
|
set => ModifyAttributes(value, TypeAttributes.Forwarder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="TypeAttributes.RTSpecialName"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRuntimeSpecialName {
|
||||||
|
get => ((TypeAttributes)attributes & TypeAttributes.RTSpecialName) != 0;
|
||||||
|
set => ModifyAttributes(value, TypeAttributes.RTSpecialName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="TypeAttributes.HasSecurity"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool HasSecurity {
|
||||||
|
get => ((TypeAttributes)attributes & TypeAttributes.HasSecurity) != 0;
|
||||||
|
set => ModifyAttributes(value, TypeAttributes.HasSecurity);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int MAX_LOOP_ITERS = 50;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if this type has been moved to another assembly
|
||||||
|
/// </summary>
|
||||||
|
public bool MovedToAnotherAssembly {
|
||||||
|
get {
|
||||||
|
var et = this;
|
||||||
|
for (int i = 0; i < MAX_LOOP_ITERS; i++) {
|
||||||
|
var impl = et.Implementation;
|
||||||
|
if (impl is AssemblyRef)
|
||||||
|
return et.IsForwarder;
|
||||||
|
|
||||||
|
et = impl as ExportedType;
|
||||||
|
if (et is null)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves the type
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A <see cref="TypeDef"/> instance or <c>null</c> if it couldn't be resolved</returns>
|
||||||
|
public TypeDef Resolve() => Resolve(null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves the type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sourceModule">Source module or <c>null</c></param>
|
||||||
|
/// <returns>A <see cref="TypeDef"/> instance or <c>null</c> if it couldn't be resolved</returns>
|
||||||
|
public TypeDef Resolve(ModuleDef sourceModule) {
|
||||||
|
if (module is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return Resolve(sourceModule, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeDef Resolve(ModuleDef sourceModule, ExportedType et) {
|
||||||
|
for (int i = 0; i < MAX_LOOP_ITERS; i++) {
|
||||||
|
if (et is null || et.module is null)
|
||||||
|
break;
|
||||||
|
var resolver = et.module.Context.AssemblyResolver;
|
||||||
|
var etAsm = resolver.Resolve(et.DefinitionAssembly, sourceModule ?? et.module);
|
||||||
|
if (etAsm is null)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var td = etAsm.Find(et.FullName, false);
|
||||||
|
if (td is not null)
|
||||||
|
return td;
|
||||||
|
|
||||||
|
et = FindExportedType(etAsm, et);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ExportedType FindExportedType(AssemblyDef asm, ExportedType et) {
|
||||||
|
var modules = asm.Modules;
|
||||||
|
int count = modules.Count;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
var mod = modules[i];
|
||||||
|
var exportedTypes = mod.ExportedTypes;
|
||||||
|
int count2 = exportedTypes.Count;
|
||||||
|
for (int j = 0; j < count2; j++) {
|
||||||
|
var et2 = exportedTypes[j];
|
||||||
|
if (new SigComparer(SigComparerOptions.DontCompareTypeScope).Equals(et, et2))
|
||||||
|
return et2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves the type
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A <see cref="TypeDef"/> instance</returns>
|
||||||
|
/// <exception cref="TypeResolveException">If the type couldn't be resolved</exception>
|
||||||
|
public TypeDef ResolveThrow() {
|
||||||
|
var type = Resolve();
|
||||||
|
if (type is not null)
|
||||||
|
return type;
|
||||||
|
throw new TypeResolveException($"Could not resolve type: {this} ({DefinitionAssembly})");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts this instance to a <see cref="TypeRef"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A new <see cref="TypeRef"/> instance</returns>
|
||||||
|
public TypeRef ToTypeRef() {
|
||||||
|
TypeRef result = null, prev = null;
|
||||||
|
var mod = module;
|
||||||
|
IImplementation impl = this;
|
||||||
|
for (int i = 0; i < MAX_LOOP_ITERS && impl is not null; i++) {
|
||||||
|
if (impl is ExportedType et) {
|
||||||
|
var newTr = mod.UpdateRowId(new TypeRefUser(mod, et.TypeNamespace, et.TypeName));
|
||||||
|
if (result is null)
|
||||||
|
result = newTr;
|
||||||
|
if (prev is not null)
|
||||||
|
prev.ResolutionScope = newTr;
|
||||||
|
|
||||||
|
prev = newTr;
|
||||||
|
impl = et.Implementation;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (impl is AssemblyRef asmRef) {
|
||||||
|
// prev is never null when we're here
|
||||||
|
prev.ResolutionScope = asmRef;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (impl is FileDef file) {
|
||||||
|
// prev is never null when we're here
|
||||||
|
prev.ResolutionScope = FindModule(mod, file);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ModuleDef FindModule(ModuleDef module, FileDef file) {
|
||||||
|
if (module is null || file is null)
|
||||||
|
return null;
|
||||||
|
if (UTF8String.CaseInsensitiveEquals(module.Name, file.Name))
|
||||||
|
return module;
|
||||||
|
var asm = module.Assembly;
|
||||||
|
if (asm is null)
|
||||||
|
return null;
|
||||||
|
return asm.FindModule(file.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString() => FullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An ExportedType row created by the user and not present in the original .NET file
|
||||||
|
/// </summary>
|
||||||
|
public class ExportedTypeUser : ExportedType {
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Owner module</param>
|
||||||
|
public ExportedTypeUser(ModuleDef module) => this.module = module;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Owner module</param>
|
||||||
|
/// <param name="typeDefId">TypeDef ID</param>
|
||||||
|
/// <param name="typeName">Type name</param>
|
||||||
|
/// <param name="typeNamespace">Type namespace</param>
|
||||||
|
/// <param name="flags">Flags</param>
|
||||||
|
/// <param name="implementation">Implementation</param>
|
||||||
|
public ExportedTypeUser(ModuleDef module, uint typeDefId, UTF8String typeNamespace, UTF8String typeName, TypeAttributes flags, IImplementation implementation) {
|
||||||
|
this.module = module;
|
||||||
|
this.typeDefId = typeDefId;
|
||||||
|
this.typeName = typeName;
|
||||||
|
this.typeNamespace = typeNamespace;
|
||||||
|
attributes = (int)flags;
|
||||||
|
this.implementation = implementation;
|
||||||
|
implementation_isInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Created from a row in the ExportedType table
|
||||||
|
/// </summary>
|
||||||
|
sealed class ExportedTypeMD : ExportedType, IMDTokenProviderMD {
|
||||||
|
/// <summary>The module where this instance is located</summary>
|
||||||
|
readonly ModuleDefMD readerModule;
|
||||||
|
|
||||||
|
readonly uint origRid;
|
||||||
|
readonly uint implementationRid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint OrigRid => origRid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomAttributes() {
|
||||||
|
var list = readerModule.Metadata.GetCustomAttributeRidList(Table.ExportedType, origRid);
|
||||||
|
var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index]));
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, tmp, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomDebugInfos() {
|
||||||
|
var list = new List<PdbCustomDebugInfo>();
|
||||||
|
readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list);
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, list, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override IImplementation GetImplementation_NoLock() =>
|
||||||
|
readerModule.ResolveImplementation(implementationRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readerModule">The module which contains this <c>ExportedType</c> row</param>
|
||||||
|
/// <param name="rid">Row ID</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If <paramref name="readerModule"/> is <c>null</c></exception>
|
||||||
|
/// <exception cref="ArgumentException">If <paramref name="rid"/> is invalid</exception>
|
||||||
|
public ExportedTypeMD(ModuleDefMD readerModule, uint rid) {
|
||||||
|
#if DEBUG
|
||||||
|
if (readerModule is null)
|
||||||
|
throw new ArgumentNullException("readerModule");
|
||||||
|
if (readerModule.TablesStream.ExportedTypeTable.IsInvalidRID(rid))
|
||||||
|
throw new BadImageFormatException($"ExportedType rid {rid} does not exist");
|
||||||
|
#endif
|
||||||
|
origRid = rid;
|
||||||
|
this.rid = rid;
|
||||||
|
this.readerModule = readerModule;
|
||||||
|
module = readerModule;
|
||||||
|
bool b = readerModule.TablesStream.TryReadExportedTypeRow(origRid, out var row);
|
||||||
|
implementationRid = row.Implementation;
|
||||||
|
attributes = (int)row.Flags;
|
||||||
|
typeDefId = row.TypeDefId;
|
||||||
|
typeName = readerModule.StringsStream.ReadNoNull(row.TypeName);
|
||||||
|
typeNamespace = readerModule.StringsStream.ReadNoNull(row.TypeNamespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods
|
||||||
|
/// </summary>
|
||||||
|
public static partial class Extensions {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Field flags, see CorHdr.h/CorFieldAttr
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum FieldAttributes : ushort {
|
||||||
|
/// <summary>member access mask - Use this mask to retrieve accessibility information.</summary>
|
||||||
|
FieldAccessMask = 0x0007,
|
||||||
|
/// <summary>Member not referenceable.</summary>
|
||||||
|
PrivateScope = 0x0000,
|
||||||
|
/// <summary>Member not referenceable.</summary>
|
||||||
|
CompilerControlled = PrivateScope,
|
||||||
|
/// <summary>Accessible only by the parent type.</summary>
|
||||||
|
Private = 0x0001,
|
||||||
|
/// <summary>Accessible by sub-types only in this Assembly.</summary>
|
||||||
|
FamANDAssem = 0x0002,
|
||||||
|
/// <summary>Accessibly by anyone in the Assembly.</summary>
|
||||||
|
Assembly = 0x0003,
|
||||||
|
/// <summary>Accessible only by type and sub-types.</summary>
|
||||||
|
Family = 0x0004,
|
||||||
|
/// <summary>Accessibly by sub-types anywhere, plus anyone in assembly.</summary>
|
||||||
|
FamORAssem = 0x0005,
|
||||||
|
/// <summary>Accessibly by anyone who has visibility to this scope.</summary>
|
||||||
|
Public = 0x0006,
|
||||||
|
|
||||||
|
/// <summary>Defined on type, else per instance.</summary>
|
||||||
|
Static = 0x0010,
|
||||||
|
/// <summary>Field may only be initialized, not written to after init.</summary>
|
||||||
|
InitOnly = 0x0020,
|
||||||
|
/// <summary>Value is compile time constant.</summary>
|
||||||
|
Literal = 0x0040,
|
||||||
|
/// <summary>Field does not have to be serialized when type is remoted.</summary>
|
||||||
|
NotSerialized = 0x0080,
|
||||||
|
|
||||||
|
/// <summary>field is special. Name describes how.</summary>
|
||||||
|
SpecialName = 0x0200,
|
||||||
|
|
||||||
|
/// <summary>Implementation is forwarded through pinvoke.</summary>
|
||||||
|
PinvokeImpl = 0x2000,
|
||||||
|
|
||||||
|
/// <summary>Runtime(metadata internal APIs) should check name encoding.</summary>
|
||||||
|
RTSpecialName = 0x0400,
|
||||||
|
/// <summary>Field has marshalling information.</summary>
|
||||||
|
HasFieldMarshal = 0x1000,
|
||||||
|
/// <summary>Field has default.</summary>
|
||||||
|
HasDefault = 0x8000,
|
||||||
|
/// <summary>Field has RVA.</summary>
|
||||||
|
HasFieldRVA = 0x0100,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,864 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
using dnlib.DotNet.MD;
|
||||||
|
using dnlib.DotNet.Pdb;
|
||||||
|
using dnlib.PE;
|
||||||
|
using dnlib.Threading;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// A high-level representation of a row in the Field table
|
||||||
|
/// </summary>
|
||||||
|
public abstract class FieldDef : IHasConstant, IHasCustomAttribute, IHasFieldMarshal, IMemberForwarded, IHasCustomDebugInformation, IField, ITokenOperand, IMemberDef {
|
||||||
|
/// <summary>
|
||||||
|
/// The row id in its table
|
||||||
|
/// </summary>
|
||||||
|
protected uint rid;
|
||||||
|
|
||||||
|
#if THREAD_SAFE
|
||||||
|
readonly Lock theLock = Lock.Create();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public MDToken MDToken => new MDToken(Table.Field, rid);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint Rid {
|
||||||
|
get => rid;
|
||||||
|
set => rid = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasConstantTag => 0;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomAttributeTag => 1;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasFieldMarshalTag => 0;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int MemberForwardedTag => 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom attributes
|
||||||
|
/// </summary>
|
||||||
|
public CustomAttributeCollection CustomAttributes {
|
||||||
|
get {
|
||||||
|
if (customAttributes is null)
|
||||||
|
InitializeCustomAttributes();
|
||||||
|
return customAttributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected CustomAttributeCollection customAttributes;
|
||||||
|
/// <summary>Initializes <see cref="customAttributes"/></summary>
|
||||||
|
protected virtual void InitializeCustomAttributes() =>
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomDebugInformationTag => 1;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom debug infos
|
||||||
|
/// </summary>
|
||||||
|
public IList<PdbCustomDebugInfo> CustomDebugInfos {
|
||||||
|
get {
|
||||||
|
if (customDebugInfos is null)
|
||||||
|
InitializeCustomDebugInfos();
|
||||||
|
return customDebugInfos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected IList<PdbCustomDebugInfo> customDebugInfos;
|
||||||
|
/// <summary>Initializes <see cref="customDebugInfos"/></summary>
|
||||||
|
protected virtual void InitializeCustomDebugInfos() =>
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, new List<PdbCustomDebugInfo>(), null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column Field.Flags
|
||||||
|
/// </summary>
|
||||||
|
public FieldAttributes Attributes {
|
||||||
|
get => (FieldAttributes)attributes;
|
||||||
|
set => attributes = (int)value;
|
||||||
|
}
|
||||||
|
/// <summary>Attributes</summary>
|
||||||
|
protected int attributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column Field.Name
|
||||||
|
/// </summary>
|
||||||
|
public UTF8String Name {
|
||||||
|
get => name;
|
||||||
|
set => name = value;
|
||||||
|
}
|
||||||
|
/// <summary>Name</summary>
|
||||||
|
protected UTF8String name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column Field.Signature
|
||||||
|
/// </summary>
|
||||||
|
public CallingConventionSig Signature {
|
||||||
|
get => signature;
|
||||||
|
set => signature = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected CallingConventionSig signature;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the field layout offset
|
||||||
|
/// </summary>
|
||||||
|
public uint? FieldOffset {
|
||||||
|
get {
|
||||||
|
if (!fieldOffset_isInitialized)
|
||||||
|
InitializeFieldOffset();
|
||||||
|
return fieldOffset;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
fieldOffset = value;
|
||||||
|
fieldOffset_isInitialized = true;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected uint? fieldOffset;
|
||||||
|
/// <summary/>
|
||||||
|
protected bool fieldOffset_isInitialized;
|
||||||
|
|
||||||
|
void InitializeFieldOffset() {
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
if (fieldOffset_isInitialized)
|
||||||
|
return;
|
||||||
|
fieldOffset = GetFieldOffset_NoLock();
|
||||||
|
fieldOffset_isInitialized = true;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Called to initialize <see cref="fieldOffset"/></summary>
|
||||||
|
protected virtual uint? GetFieldOffset_NoLock() => null;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public MarshalType MarshalType {
|
||||||
|
get {
|
||||||
|
if (!marshalType_isInitialized)
|
||||||
|
InitializeMarshalType();
|
||||||
|
return marshalType;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
marshalType = value;
|
||||||
|
marshalType_isInitialized = true;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected MarshalType marshalType;
|
||||||
|
/// <summary/>
|
||||||
|
protected bool marshalType_isInitialized;
|
||||||
|
|
||||||
|
void InitializeMarshalType() {
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
if (marshalType_isInitialized)
|
||||||
|
return;
|
||||||
|
marshalType = GetMarshalType_NoLock();
|
||||||
|
marshalType_isInitialized = true;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Called to initialize <see cref="marshalType"/></summary>
|
||||||
|
protected virtual MarshalType GetMarshalType_NoLock() => null;
|
||||||
|
|
||||||
|
/// <summary>Reset <see cref="MarshalType"/></summary>
|
||||||
|
protected void ResetMarshalType() =>
|
||||||
|
marshalType_isInitialized = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the field RVA
|
||||||
|
/// </summary>
|
||||||
|
public RVA RVA {
|
||||||
|
get {
|
||||||
|
if (!rva_isInitialized)
|
||||||
|
InitializeRVA();
|
||||||
|
return rva;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
rva = value;
|
||||||
|
rva_isInitialized = true;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected RVA rva;
|
||||||
|
/// <summary/>
|
||||||
|
protected bool rva_isInitialized;
|
||||||
|
|
||||||
|
void InitializeRVA() {
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
if (rva_isInitialized)
|
||||||
|
return;
|
||||||
|
rva = GetRVA_NoLock();
|
||||||
|
rva_isInitialized = true;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Called to initialize <see cref="rva"/></summary>
|
||||||
|
protected virtual RVA GetRVA_NoLock() => 0;
|
||||||
|
|
||||||
|
/// <summary>Reset <see cref="RVA"/></summary>
|
||||||
|
protected void ResetRVA() => rva_isInitialized = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the initial value. Be sure to set <see cref="HasFieldRVA"/> to <c>true</c> if
|
||||||
|
/// you write to this field.
|
||||||
|
/// </summary>
|
||||||
|
public byte[] InitialValue {
|
||||||
|
get {
|
||||||
|
if (!initialValue_isInitialized)
|
||||||
|
InitializeInitialValue();
|
||||||
|
return initialValue;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
initialValue = value;
|
||||||
|
initialValue_isInitialized = true;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected byte[] initialValue;
|
||||||
|
/// <summary/>
|
||||||
|
protected bool initialValue_isInitialized;
|
||||||
|
|
||||||
|
void InitializeInitialValue() {
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
if (initialValue_isInitialized)
|
||||||
|
return;
|
||||||
|
initialValue = GetInitialValue_NoLock();
|
||||||
|
initialValue_isInitialized = true;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Called to initialize <see cref="initialValue"/></summary>
|
||||||
|
protected virtual byte[] GetInitialValue_NoLock() => null;
|
||||||
|
|
||||||
|
/// <summary>Reset <see cref="InitialValue"/></summary>
|
||||||
|
protected void ResetInitialValue() => initialValue_isInitialized = false;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ImplMap ImplMap {
|
||||||
|
get {
|
||||||
|
if (!implMap_isInitialized)
|
||||||
|
InitializeImplMap();
|
||||||
|
return implMap;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
implMap = value;
|
||||||
|
implMap_isInitialized = true;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected ImplMap implMap;
|
||||||
|
/// <summary/>
|
||||||
|
protected bool implMap_isInitialized;
|
||||||
|
|
||||||
|
void InitializeImplMap() {
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
if (implMap_isInitialized)
|
||||||
|
return;
|
||||||
|
implMap = GetImplMap_NoLock();
|
||||||
|
implMap_isInitialized = true;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Called to initialize <see cref="implMap"/></summary>
|
||||||
|
protected virtual ImplMap GetImplMap_NoLock() => null;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public Constant Constant {
|
||||||
|
get {
|
||||||
|
if (!constant_isInitialized)
|
||||||
|
InitializeConstant();
|
||||||
|
return constant;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
constant = value;
|
||||||
|
constant_isInitialized = true;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected Constant constant;
|
||||||
|
/// <summary/>
|
||||||
|
protected bool constant_isInitialized;
|
||||||
|
|
||||||
|
void InitializeConstant() {
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
if (constant_isInitialized)
|
||||||
|
return;
|
||||||
|
constant = GetConstant_NoLock();
|
||||||
|
constant_isInitialized = true;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Called to initialize <see cref="constant"/></summary>
|
||||||
|
protected virtual Constant GetConstant_NoLock() => null;
|
||||||
|
|
||||||
|
/// <summary>Reset <see cref="Constant"/></summary>
|
||||||
|
protected void ResetConstant() => constant_isInitialized = false;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomAttributes => CustomAttributes.Count > 0;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasImplMap => ImplMap is not null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the declaring type (owner type)
|
||||||
|
/// </summary>
|
||||||
|
public TypeDef DeclaringType {
|
||||||
|
get => declaringType2;
|
||||||
|
set {
|
||||||
|
var currentDeclaringType = DeclaringType2;
|
||||||
|
if (currentDeclaringType == value)
|
||||||
|
return;
|
||||||
|
if (currentDeclaringType is not null)
|
||||||
|
currentDeclaringType.Fields.Remove(this); // Will set DeclaringType2 = null
|
||||||
|
if (value is not null)
|
||||||
|
value.Fields.Add(this); // Will set DeclaringType2 = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
ITypeDefOrRef IMemberRef.DeclaringType => declaringType2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called by <see cref="DeclaringType"/> and should normally not be called by any user
|
||||||
|
/// code. Use <see cref="DeclaringType"/> instead. Only call this if you must set the
|
||||||
|
/// declaring type without inserting it in the declaring type's method list.
|
||||||
|
/// </summary>
|
||||||
|
public TypeDef DeclaringType2 {
|
||||||
|
get => declaringType2;
|
||||||
|
set => declaringType2 = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected TypeDef declaringType2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="FieldSig"/>
|
||||||
|
/// </summary>
|
||||||
|
public FieldSig FieldSig {
|
||||||
|
get => signature as FieldSig;
|
||||||
|
set => signature = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ModuleDef Module => declaringType2?.Module;
|
||||||
|
|
||||||
|
bool IIsTypeOrMethod.IsType => false;
|
||||||
|
bool IIsTypeOrMethod.IsMethod => false;
|
||||||
|
bool IMemberRef.IsField => true;
|
||||||
|
bool IMemberRef.IsTypeSpec => false;
|
||||||
|
bool IMemberRef.IsTypeRef => false;
|
||||||
|
bool IMemberRef.IsTypeDef => false;
|
||||||
|
bool IMemberRef.IsMethodSpec => false;
|
||||||
|
bool IMemberRef.IsMethodDef => false;
|
||||||
|
bool IMemberRef.IsMemberRef => false;
|
||||||
|
bool IMemberRef.IsFieldDef => true;
|
||||||
|
bool IMemberRef.IsPropertyDef => false;
|
||||||
|
bool IMemberRef.IsEventDef => false;
|
||||||
|
bool IMemberRef.IsGenericParam => false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="FieldOffset"/> is not <c>null</c>
|
||||||
|
/// </summary>
|
||||||
|
public bool HasLayoutInfo => FieldOffset is not null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="Constant"/> is not <c>null</c>
|
||||||
|
/// </summary>
|
||||||
|
public bool HasConstant => Constant is not null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the constant element type or <see cref="dnlib.DotNet.ElementType.End"/> if there's no constant
|
||||||
|
/// </summary>
|
||||||
|
public ElementType ElementType {
|
||||||
|
get {
|
||||||
|
var c = Constant;
|
||||||
|
return c is null ? ElementType.End : c.Type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="MarshalType"/> is not <c>null</c>
|
||||||
|
/// </summary>
|
||||||
|
public bool HasMarshalType => MarshalType is not null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the field type
|
||||||
|
/// </summary>
|
||||||
|
public TypeSig FieldType {
|
||||||
|
get => FieldSig.GetFieldType();
|
||||||
|
set {
|
||||||
|
var sig = FieldSig;
|
||||||
|
if (sig is not null)
|
||||||
|
sig.Type = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Modify <see cref="attributes"/> field: <see cref="attributes"/> =
|
||||||
|
/// (<see cref="attributes"/> & <paramref name="andMask"/>) | <paramref name="orMask"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="andMask">Value to <c>AND</c></param>
|
||||||
|
/// <param name="orMask">Value to OR</param>
|
||||||
|
void ModifyAttributes(FieldAttributes andMask, FieldAttributes orMask) =>
|
||||||
|
attributes = (attributes & (int)andMask) | (int)orMask;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set or clear flags in <see cref="attributes"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="set"><c>true</c> if flags should be set, <c>false</c> if flags should
|
||||||
|
/// be cleared</param>
|
||||||
|
/// <param name="flags">Flags to set or clear</param>
|
||||||
|
void ModifyAttributes(bool set, FieldAttributes flags) {
|
||||||
|
if (set)
|
||||||
|
attributes |= (int)flags;
|
||||||
|
else
|
||||||
|
attributes &= ~(int)flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the field access
|
||||||
|
/// </summary>
|
||||||
|
public FieldAttributes Access {
|
||||||
|
get => (FieldAttributes)attributes & FieldAttributes.FieldAccessMask;
|
||||||
|
set => ModifyAttributes(~FieldAttributes.FieldAccessMask, value & FieldAttributes.FieldAccessMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="FieldAttributes.PrivateScope"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCompilerControlled => IsPrivateScope;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="FieldAttributes.PrivateScope"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPrivateScope => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.PrivateScope;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="FieldAttributes.Private"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPrivate => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Private;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="FieldAttributes.FamANDAssem"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFamilyAndAssembly => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamANDAssem;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="FieldAttributes.Assembly"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsAssembly => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Assembly;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="FieldAttributes.Family"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFamily => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Family;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="FieldAttributes.FamORAssem"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFamilyOrAssembly => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamORAssem;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="FieldAttributes.Public"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPublic => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Public;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="FieldAttributes.Static"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsStatic {
|
||||||
|
get => ((FieldAttributes)attributes & FieldAttributes.Static) != 0;
|
||||||
|
set => ModifyAttributes(value, FieldAttributes.Static);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="FieldAttributes.InitOnly"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsInitOnly {
|
||||||
|
get => ((FieldAttributes)attributes & FieldAttributes.InitOnly) != 0;
|
||||||
|
set => ModifyAttributes(value, FieldAttributes.InitOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="FieldAttributes.Literal"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsLiteral {
|
||||||
|
get => ((FieldAttributes)attributes & FieldAttributes.Literal) != 0;
|
||||||
|
set => ModifyAttributes(value, FieldAttributes.Literal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="FieldAttributes.NotSerialized"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsNotSerialized {
|
||||||
|
get => ((FieldAttributes)attributes & FieldAttributes.NotSerialized) != 0;
|
||||||
|
set => ModifyAttributes(value, FieldAttributes.NotSerialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="FieldAttributes.SpecialName"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSpecialName {
|
||||||
|
get => ((FieldAttributes)attributes & FieldAttributes.SpecialName) != 0;
|
||||||
|
set => ModifyAttributes(value, FieldAttributes.SpecialName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="FieldAttributes.PinvokeImpl"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPinvokeImpl {
|
||||||
|
get => ((FieldAttributes)attributes & FieldAttributes.PinvokeImpl) != 0;
|
||||||
|
set => ModifyAttributes(value, FieldAttributes.PinvokeImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="FieldAttributes.RTSpecialName"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRuntimeSpecialName {
|
||||||
|
get => ((FieldAttributes)attributes & FieldAttributes.RTSpecialName) != 0;
|
||||||
|
set => ModifyAttributes(value, FieldAttributes.RTSpecialName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="FieldAttributes.HasFieldMarshal"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool HasFieldMarshal {
|
||||||
|
get => ((FieldAttributes)attributes & FieldAttributes.HasFieldMarshal) != 0;
|
||||||
|
set => ModifyAttributes(value, FieldAttributes.HasFieldMarshal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="FieldAttributes.HasDefault"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool HasDefault {
|
||||||
|
get => ((FieldAttributes)attributes & FieldAttributes.HasDefault) != 0;
|
||||||
|
set => ModifyAttributes(value, FieldAttributes.HasDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="FieldAttributes.HasFieldRVA"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool HasFieldRVA {
|
||||||
|
get => ((FieldAttributes)attributes & FieldAttributes.HasFieldRVA) != 0;
|
||||||
|
set => ModifyAttributes(value, FieldAttributes.HasFieldRVA);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the full name of this field
|
||||||
|
/// </summary>
|
||||||
|
public string FullName => FullNameFactory.FieldFullName(declaringType2?.FullName, name, FieldSig, null, null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size of this field in bytes or <c>0</c> if unknown.
|
||||||
|
/// </summary>
|
||||||
|
public uint GetFieldSize() {
|
||||||
|
if (!GetFieldSize(out uint size))
|
||||||
|
return 0;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size of this field in bytes or <c>0</c> if unknown.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="size">Updated with size</param>
|
||||||
|
/// <returns><c>true</c> if <paramref name="size"/> is valid, <c>false</c> otherwise</returns>
|
||||||
|
public bool GetFieldSize(out uint size) => GetFieldSize(declaringType2, FieldSig, out size);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size of this field in bytes or <c>0</c> if unknown.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="declaringType">The declaring type of <c>this</c></param>
|
||||||
|
/// <param name="fieldSig">The field signature of <c>this</c></param>
|
||||||
|
/// <param name="size">Updated with size</param>
|
||||||
|
/// <returns><c>true</c> if <paramref name="size"/> is valid, <c>false</c> otherwise</returns>
|
||||||
|
protected bool GetFieldSize(TypeDef declaringType, FieldSig fieldSig, out uint size) => GetFieldSize(declaringType, fieldSig, GetPointerSize(declaringType), out size);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size of this field in bytes or <c>0</c> if unknown.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="declaringType">The declaring type of <c>this</c></param>
|
||||||
|
/// <param name="fieldSig">The field signature of <c>this</c></param>
|
||||||
|
/// <param name="ptrSize">Size of a pointer</param>
|
||||||
|
/// <param name="size">Updated with size</param>
|
||||||
|
/// <returns><c>true</c> if <paramref name="size"/> is valid, <c>false</c> otherwise</returns>
|
||||||
|
protected bool GetFieldSize(TypeDef declaringType, FieldSig fieldSig, int ptrSize, out uint size) {
|
||||||
|
size = 0;
|
||||||
|
if (fieldSig is null)
|
||||||
|
return false;
|
||||||
|
return GetClassSize(declaringType, fieldSig.Type, ptrSize, out size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetClassSize(TypeDef declaringType, TypeSig ts, int ptrSize, out uint size) {
|
||||||
|
size = 0;
|
||||||
|
ts = ts.RemovePinnedAndModifiers();
|
||||||
|
if (ts is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int size2 = ts.ElementType.GetPrimitiveSize(ptrSize);
|
||||||
|
if (size2 >= 0) {
|
||||||
|
size = (uint)size2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tdrs = ts as TypeDefOrRefSig;
|
||||||
|
if (tdrs is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var td = tdrs.TypeDef;
|
||||||
|
if (td is not null)
|
||||||
|
return TypeDef.GetClassSize(td, out size);
|
||||||
|
|
||||||
|
var tr = tdrs.TypeRef;
|
||||||
|
if (tr is not null)
|
||||||
|
return TypeDef.GetClassSize(tr.Resolve(), out size);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetPointerSize(TypeDef declaringType) {
|
||||||
|
if (declaringType is null)
|
||||||
|
return 4;
|
||||||
|
var module = declaringType.Module;
|
||||||
|
if (module is null)
|
||||||
|
return 4;
|
||||||
|
return module.GetPointerSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString() => FullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A Field row created by the user and not present in the original .NET file
|
||||||
|
/// </summary>
|
||||||
|
public class FieldDefUser : FieldDef {
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public FieldDefUser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name</param>
|
||||||
|
public FieldDefUser(UTF8String name)
|
||||||
|
: this(name, null) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name</param>
|
||||||
|
/// <param name="signature">Signature</param>
|
||||||
|
public FieldDefUser(UTF8String name, FieldSig signature)
|
||||||
|
: this(name, signature, 0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name</param>
|
||||||
|
/// <param name="signature">Signature</param>
|
||||||
|
/// <param name="attributes">Flags</param>
|
||||||
|
public FieldDefUser(UTF8String name, FieldSig signature, FieldAttributes attributes) {
|
||||||
|
this.name = name;
|
||||||
|
this.signature = signature;
|
||||||
|
this.attributes = (int)attributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Created from a row in the Field table
|
||||||
|
/// </summary>
|
||||||
|
sealed class FieldDefMD : FieldDef, IMDTokenProviderMD {
|
||||||
|
/// <summary>The module where this instance is located</summary>
|
||||||
|
readonly ModuleDefMD readerModule;
|
||||||
|
|
||||||
|
readonly uint origRid;
|
||||||
|
readonly FieldAttributes origAttributes;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint OrigRid => origRid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomAttributes() {
|
||||||
|
var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Field, origRid);
|
||||||
|
var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index]));
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, tmp, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomDebugInfos() {
|
||||||
|
var list = new List<PdbCustomDebugInfo>();
|
||||||
|
readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(declaringType2), list);
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, list, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override uint? GetFieldOffset_NoLock() {
|
||||||
|
if (readerModule.TablesStream.TryReadFieldLayoutRow(readerModule.Metadata.GetFieldLayoutRid(origRid), out var row))
|
||||||
|
return row.OffSet;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override MarshalType GetMarshalType_NoLock() =>
|
||||||
|
readerModule.ReadMarshalType(Table.Field, origRid, new GenericParamContext(declaringType2));
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override RVA GetRVA_NoLock() {
|
||||||
|
GetFieldRVA_NoLock(out var rva2);
|
||||||
|
return rva2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override byte[] GetInitialValue_NoLock() {
|
||||||
|
if (!GetFieldRVA_NoLock(out var rva2))
|
||||||
|
return null;
|
||||||
|
return ReadInitialValue_NoLock(rva2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override ImplMap GetImplMap_NoLock() =>
|
||||||
|
readerModule.ResolveImplMap(readerModule.Metadata.GetImplMapRid(Table.Field, origRid));
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override Constant GetConstant_NoLock() =>
|
||||||
|
readerModule.ResolveConstant(readerModule.Metadata.GetConstantRid(Table.Field, origRid));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readerModule">The module which contains this <c>Field</c> row</param>
|
||||||
|
/// <param name="rid">Row ID</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If <paramref name="readerModule"/> is <c>null</c></exception>
|
||||||
|
/// <exception cref="ArgumentException">If <paramref name="rid"/> is invalid</exception>
|
||||||
|
public FieldDefMD(ModuleDefMD readerModule, uint rid) {
|
||||||
|
#if DEBUG
|
||||||
|
if (readerModule is null)
|
||||||
|
throw new ArgumentNullException("readerModule");
|
||||||
|
if (readerModule.TablesStream.FieldTable.IsInvalidRID(rid))
|
||||||
|
throw new BadImageFormatException($"Field rid {rid} does not exist");
|
||||||
|
#endif
|
||||||
|
origRid = rid;
|
||||||
|
this.rid = rid;
|
||||||
|
this.readerModule = readerModule;
|
||||||
|
bool b = readerModule.TablesStream.TryReadFieldRow(origRid, out var row);
|
||||||
|
Debug.Assert(b);
|
||||||
|
name = readerModule.StringsStream.ReadNoNull(row.Name);
|
||||||
|
attributes = row.Flags;
|
||||||
|
origAttributes = (FieldAttributes)attributes;
|
||||||
|
declaringType2 = readerModule.GetOwnerType(this);
|
||||||
|
signature = readerModule.ReadSignature(row.Signature, new GenericParamContext(declaringType2));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal FieldDefMD InitializeAll() {
|
||||||
|
MemberMDInitializer.Initialize(CustomAttributes);
|
||||||
|
MemberMDInitializer.Initialize(Attributes);
|
||||||
|
MemberMDInitializer.Initialize(Name);
|
||||||
|
MemberMDInitializer.Initialize(Signature);
|
||||||
|
MemberMDInitializer.Initialize(FieldOffset);
|
||||||
|
MemberMDInitializer.Initialize(MarshalType);
|
||||||
|
MemberMDInitializer.Initialize(RVA);
|
||||||
|
MemberMDInitializer.Initialize(InitialValue);
|
||||||
|
MemberMDInitializer.Initialize(ImplMap);
|
||||||
|
MemberMDInitializer.Initialize(Constant);
|
||||||
|
MemberMDInitializer.Initialize(DeclaringType);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetFieldRVA_NoLock(out RVA rva) {
|
||||||
|
if ((origAttributes & FieldAttributes.HasFieldRVA) == 0) {
|
||||||
|
rva = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!readerModule.TablesStream.TryReadFieldRVARow(readerModule.Metadata.GetFieldRVARid(origRid), out var row)) {
|
||||||
|
rva = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
rva = (RVA)row.RVA;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] ReadInitialValue_NoLock(RVA rva) {
|
||||||
|
if (!GetFieldSize(declaringType2, signature as FieldSig, out uint size))
|
||||||
|
return null;
|
||||||
|
if (size >= int.MaxValue)
|
||||||
|
return null;
|
||||||
|
return readerModule.ReadDataAt(rva, (int)size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// File row flags. See CorHdr.h/CorFileFlags
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum FileAttributes : uint {
|
||||||
|
/// <summary>This is not a resource file</summary>
|
||||||
|
ContainsMetadata = 0x0000,
|
||||||
|
/// <summary>This is a resource file or other non-metadata-containing file</summary>
|
||||||
|
ContainsNoMetadata = 0x0001,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,215 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
using dnlib.DotNet.MD;
|
||||||
|
using dnlib.DotNet.Pdb;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// A high-level representation of a row in the File table
|
||||||
|
/// </summary>
|
||||||
|
public abstract class FileDef : IHasCustomAttribute, IImplementation, IHasCustomDebugInformation, IManagedEntryPoint {
|
||||||
|
/// <summary>
|
||||||
|
/// The row id in its table
|
||||||
|
/// </summary>
|
||||||
|
protected uint rid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public MDToken MDToken => new MDToken(Table.File, rid);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint Rid {
|
||||||
|
get => rid;
|
||||||
|
set => rid = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomAttributeTag => 16;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int ImplementationTag => 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column File.Flags
|
||||||
|
/// </summary>
|
||||||
|
public FileAttributes Flags {
|
||||||
|
get => (FileAttributes)attributes;
|
||||||
|
set => attributes = (int)value;
|
||||||
|
}
|
||||||
|
/// <summary>Attributes</summary>
|
||||||
|
protected int attributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column File.Name
|
||||||
|
/// </summary>
|
||||||
|
public UTF8String Name {
|
||||||
|
get => name;
|
||||||
|
set => name = value;
|
||||||
|
}
|
||||||
|
/// <summary>Name</summary>
|
||||||
|
protected UTF8String name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column File.HashValue
|
||||||
|
/// </summary>
|
||||||
|
public byte[] HashValue {
|
||||||
|
get => hashValue;
|
||||||
|
set => hashValue = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected byte[] hashValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom attributes
|
||||||
|
/// </summary>
|
||||||
|
public CustomAttributeCollection CustomAttributes {
|
||||||
|
get {
|
||||||
|
if (customAttributes is null)
|
||||||
|
InitializeCustomAttributes();
|
||||||
|
return customAttributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected CustomAttributeCollection customAttributes;
|
||||||
|
/// <summary>Initializes <see cref="customAttributes"/></summary>
|
||||||
|
protected virtual void InitializeCustomAttributes() =>
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomAttributes => CustomAttributes.Count > 0;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomDebugInformationTag => 16;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom debug infos
|
||||||
|
/// </summary>
|
||||||
|
public IList<PdbCustomDebugInfo> CustomDebugInfos {
|
||||||
|
get {
|
||||||
|
if (customDebugInfos is null)
|
||||||
|
InitializeCustomDebugInfos();
|
||||||
|
return customDebugInfos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected IList<PdbCustomDebugInfo> customDebugInfos;
|
||||||
|
/// <summary>Initializes <see cref="customDebugInfos"/></summary>
|
||||||
|
protected virtual void InitializeCustomDebugInfos() =>
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, new List<PdbCustomDebugInfo>(), null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set or clear flags in <see cref="attributes"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="set"><c>true</c> if flags should be set, <c>false</c> if flags should
|
||||||
|
/// be cleared</param>
|
||||||
|
/// <param name="flags">Flags to set or clear</param>
|
||||||
|
void ModifyAttributes(bool set, FileAttributes flags) {
|
||||||
|
if (set)
|
||||||
|
attributes |= (int)flags;
|
||||||
|
else
|
||||||
|
attributes &= ~(int)flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="FileAttributes.ContainsMetadata"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool ContainsMetadata {
|
||||||
|
get => ((FileAttributes)attributes & FileAttributes.ContainsNoMetadata) == 0;
|
||||||
|
set => ModifyAttributes(!value, FileAttributes.ContainsNoMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="FileAttributes.ContainsNoMetadata"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool ContainsNoMetadata {
|
||||||
|
get => ((FileAttributes)attributes & FileAttributes.ContainsNoMetadata) != 0;
|
||||||
|
set => ModifyAttributes(value, FileAttributes.ContainsNoMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public string FullName => UTF8String.ToSystemStringOrEmpty(name);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString() => FullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A File row created by the user and not present in the original .NET file
|
||||||
|
/// </summary>
|
||||||
|
public class FileDefUser : FileDef {
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public FileDefUser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of file</param>
|
||||||
|
/// <param name="flags">Flags</param>
|
||||||
|
/// <param name="hashValue">File hash</param>
|
||||||
|
public FileDefUser(UTF8String name, FileAttributes flags, byte[] hashValue) {
|
||||||
|
this.name = name;
|
||||||
|
attributes = (int)flags;
|
||||||
|
this.hashValue = hashValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Created from a row in the File table
|
||||||
|
/// </summary>
|
||||||
|
sealed class FileDefMD : FileDef, IMDTokenProviderMD {
|
||||||
|
/// <summary>The module where this instance is located</summary>
|
||||||
|
readonly ModuleDefMD readerModule;
|
||||||
|
|
||||||
|
readonly uint origRid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint OrigRid => origRid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomAttributes() {
|
||||||
|
var list = readerModule.Metadata.GetCustomAttributeRidList(Table.File, origRid);
|
||||||
|
var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index]));
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, tmp, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomDebugInfos() {
|
||||||
|
var list = new List<PdbCustomDebugInfo>();
|
||||||
|
readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list);
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, list, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readerModule">The module which contains this <c>File</c> row</param>
|
||||||
|
/// <param name="rid">Row ID</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If <paramref name="readerModule"/> is <c>null</c></exception>
|
||||||
|
/// <exception cref="ArgumentException">If <paramref name="rid"/> is invalid</exception>
|
||||||
|
public FileDefMD(ModuleDefMD readerModule, uint rid) {
|
||||||
|
#if DEBUG
|
||||||
|
if (readerModule is null)
|
||||||
|
throw new ArgumentNullException("readerModule");
|
||||||
|
if (readerModule.TablesStream.FileTable.IsInvalidRID(rid))
|
||||||
|
throw new BadImageFormatException($"File rid {rid} does not exist");
|
||||||
|
#endif
|
||||||
|
origRid = rid;
|
||||||
|
this.rid = rid;
|
||||||
|
this.readerModule = readerModule;
|
||||||
|
bool b = readerModule.TablesStream.TryReadFileRow(origRid, out var row);
|
||||||
|
Debug.Assert(b);
|
||||||
|
attributes = (int)row.Flags;
|
||||||
|
name = readerModule.StringsStream.ReadNoNull(row.Name);
|
||||||
|
hashValue = readerModule.BlobStream.Read(row.HashValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,389 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Redirects .NET framework assembly references from older to newer versions
|
||||||
|
/// </summary>
|
||||||
|
public static class FrameworkRedirect {
|
||||||
|
static readonly Dictionary<string, FrameworkRedirectInfo> frmRedir2;
|
||||||
|
static readonly Dictionary<string, FrameworkRedirectInfo> frmRedir4;
|
||||||
|
|
||||||
|
readonly struct FrameworkRedirectInfo {
|
||||||
|
public readonly PublicKeyToken publicKeyToken;
|
||||||
|
public readonly Version redirectVersion;
|
||||||
|
|
||||||
|
public FrameworkRedirectInfo(string publicKeyToken, string redirectVersion) {
|
||||||
|
this.publicKeyToken = new PublicKeyToken(publicKeyToken);
|
||||||
|
this.redirectVersion = new Version(redirectVersion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static FrameworkRedirect() {
|
||||||
|
frmRedir2 = new Dictionary<string, FrameworkRedirectInfo>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
frmRedir4 = new Dictionary<string, FrameworkRedirectInfo>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
InitFrameworkRedirectV2();
|
||||||
|
InitFrameworkRedirectV4();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitFrameworkRedirectV2() {
|
||||||
|
frmRedir2["Accessibility"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["cscompmgd"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "8.0.0.0");
|
||||||
|
frmRedir2["CustomMarshalers"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["IEExecRemote"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["IEHost"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["IIEHost"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["ISymWrapper"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["Microsoft.JScript"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "8.0.0.0");
|
||||||
|
frmRedir2["Microsoft.VisualBasic"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "8.0.0.0");
|
||||||
|
frmRedir2["Microsoft.VisualBasic.Compatibility"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "8.0.0.0");
|
||||||
|
frmRedir2["Microsoft.VisualBasic.Compatibility.Data"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "8.0.0.0");
|
||||||
|
frmRedir2["Microsoft.VisualBasic.Vsa"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "8.0.0.0");
|
||||||
|
frmRedir2["Microsoft.VisualC"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "8.0.0.0");
|
||||||
|
frmRedir2["Microsoft.Vsa"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "8.0.0.0");
|
||||||
|
frmRedir2["Microsoft.Vsa.Vb.CodeDOMProcessor"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "8.0.0.0");
|
||||||
|
frmRedir2["Microsoft_VsaVb"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "8.0.0.0");
|
||||||
|
frmRedir2["mscorcfg"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["mscorlib"] = new FrameworkRedirectInfo("b77a5c561934e089", "2.0.0.0");
|
||||||
|
frmRedir2["System"] = new FrameworkRedirectInfo("b77a5c561934e089", "2.0.0.0");
|
||||||
|
frmRedir2["System.Configuration"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.Configuration.Install"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.Data"] = new FrameworkRedirectInfo("b77a5c561934e089", "2.0.0.0");
|
||||||
|
frmRedir2["System.Data.OracleClient"] = new FrameworkRedirectInfo("b77a5c561934e089", "2.0.0.0");
|
||||||
|
frmRedir2["System.Data.SqlXml"] = new FrameworkRedirectInfo("b77a5c561934e089", "2.0.0.0");
|
||||||
|
frmRedir2["System.Deployment"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.Design"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.DirectoryServices"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.DirectoryServices.Protocols"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.Drawing"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.Drawing.Design"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.EnterpriseServices"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.Management"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.Messaging"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.Runtime.Remoting"] = new FrameworkRedirectInfo("b77a5c561934e089", "2.0.0.0");
|
||||||
|
frmRedir2["System.Runtime.Serialization.Formatters.Soap"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.Security"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.ServiceProcess"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.Transactions"] = new FrameworkRedirectInfo("b77a5c561934e089", "2.0.0.0");
|
||||||
|
frmRedir2["System.Web"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.Web.Mobile"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.Web.RegularExpressions"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.Web.Services"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["System.Windows.Forms"] = new FrameworkRedirectInfo("b77a5c561934e089", "2.0.0.0");
|
||||||
|
frmRedir2["System.Xml"] = new FrameworkRedirectInfo("b77a5c561934e089", "2.0.0.0");
|
||||||
|
frmRedir2["vjscor"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["VJSharpCodeProvider"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["vjsJBC"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["vjslib"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["vjslibcw"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["Vjssupuilib"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["vjsvwaux"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["vjswfc"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["VJSWfcBrowserStubLib"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["vjswfccw"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir2["vjswfchtml"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitFrameworkRedirectV4() {
|
||||||
|
frmRedir4["Accessibility"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["CustomMarshalers"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["ISymWrapper"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["Microsoft.JScript"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "10.0.0.0");
|
||||||
|
frmRedir4["Microsoft.VisualBasic"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "10.0.0.0");
|
||||||
|
frmRedir4["Microsoft.VisualBasic.Compatibility"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "10.0.0.0");
|
||||||
|
frmRedir4["Microsoft.VisualBasic.Compatibility.Data"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "10.0.0.0");
|
||||||
|
frmRedir4["Microsoft.VisualC"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "10.0.0.0");
|
||||||
|
frmRedir4["mscorlib"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Configuration"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Configuration.Install"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Data"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Data.OracleClient"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Data.SqlXml"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Deployment"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Design"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.DirectoryServices"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.DirectoryServices.Protocols"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Drawing"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Drawing.Design"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.EnterpriseServices"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Management"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Messaging"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Runtime.Remoting"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Runtime.Serialization.Formatters.Soap"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Security"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.ServiceProcess"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Transactions"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Web"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Web.Mobile"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Web.RegularExpressions"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Web.Services"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Windows.Forms"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Xml"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["AspNetMMCExt"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["sysglobl"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["Microsoft.Build.Engine"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["Microsoft.Build.Framework"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["PresentationCFFRasterizer"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["PresentationCore"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["PresentationFramework"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["PresentationFramework.Aero"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["PresentationFramework.Classic"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["PresentationFramework.Luna"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["PresentationFramework.Royale"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["PresentationUI"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["ReachFramework"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Printing"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Speech"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["UIAutomationClient"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["UIAutomationClientsideProviders"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["UIAutomationProvider"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["UIAutomationTypes"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["WindowsBase"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["WindowsFormsIntegration"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["SMDiagnostics"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.IdentityModel"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.IdentityModel.Selectors"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.IO.Log"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Runtime.Serialization"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.ServiceModel"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.ServiceModel.Install"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.ServiceModel.WasHosting"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Workflow.Activities"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Workflow.ComponentModel"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Workflow.Runtime"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["Microsoft.Transactions.Bridge"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["Microsoft.Transactions.Bridge.Dtc"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.AddIn"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.AddIn.Contract"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.ComponentModel.Composition"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Core"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Data.DataSetExtensions"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Data.Linq"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Xml.Linq"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.DirectoryServices.AccountManagement"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Management.Instrumentation"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Net"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.ServiceModel.Web"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Web.Extensions"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Web.Extensions.Design"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Windows.Presentation"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.WorkflowServices"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.ComponentModel.DataAnnotations"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Data.Entity"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Data.Entity.Design"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Data.Services"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Data.Services.Client"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Data.Services.Design"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Web.Abstractions"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Web.DynamicData"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Web.DynamicData.Design"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Web.Entity"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Web.Entity.Design"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Web.Routing"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["Microsoft.Build"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["Microsoft.CSharp"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Dynamic"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Numerics"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Xaml"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["Microsoft.Workflow.Compiler"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["Microsoft.Activities.Build"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["Microsoft.Build.Conversion.v4.0"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["Microsoft.Build.Tasks.v4.0"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["Microsoft.Build.Utilities.v4.0"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["Microsoft.Internal.Tasks.Dataflow"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["Microsoft.VisualBasic.Activities.Compiler"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "10.0.0.0");
|
||||||
|
frmRedir4["Microsoft.VisualC.STLCLR"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "2.0.0.0");
|
||||||
|
frmRedir4["Microsoft.Windows.ApplicationServer.Applications"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["PresentationBuildTasks"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["PresentationFramework.Aero2"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["PresentationFramework.AeroLite"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["PresentationFramework-SystemCore"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["PresentationFramework-SystemData"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["PresentationFramework-SystemDrawing"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["PresentationFramework-SystemXml"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["PresentationFramework-SystemXmlLinq"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Activities"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Activities.Core.Presentation"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Activities.DurableInstancing"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Activities.Presentation"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.ComponentModel.Composition.Registration"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Device"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.IdentityModel.Services"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.IO.Compression"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.IO.Compression.FileSystem"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Net.Http"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Net.Http.WebRequest"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Reflection.Context"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Runtime.Caching"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Runtime.DurableInstancing"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Runtime.WindowsRuntime"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Runtime.WindowsRuntime.UI.Xaml"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.ServiceModel.Activation"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.ServiceModel.Activities"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.ServiceModel.Channels"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.ServiceModel.Discovery"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.ServiceModel.Internals"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.ServiceModel.Routing"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.ServiceModel.ServiceMoniker40"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Web.ApplicationServices"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Web.DataVisualization"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Web.DataVisualization.Design"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Windows.Controls.Ribbon"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Windows.Forms.DataVisualization"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Windows.Forms.DataVisualization.Design"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Windows.Input.Manipulations"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
frmRedir4["System.Xaml.Hosting"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["XamlBuildTask"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["XsdBuildTask"] = new FrameworkRedirectInfo("31bf3856ad364e35", "4.0.0.0");
|
||||||
|
frmRedir4["System.Collections"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Collections.Concurrent"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.ComponentModel"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.ComponentModel.Annotations"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.ComponentModel.EventBasedAsync"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Diagnostics.Contracts"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Diagnostics.Debug"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Diagnostics.Tools"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Diagnostics.Tracing"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Dynamic.Runtime"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Globalization"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.IO"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Linq"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Linq.Expressions"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Linq.Parallel"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Linq.Queryable"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Net.NetworkInformation"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Net.Primitives"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Net.Requests"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.ObjectModel"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Reflection"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Reflection.Emit"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Reflection.Emit.ILGeneration"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Reflection.Emit.Lightweight"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Reflection.Extensions"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Reflection.Primitives"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Resources.ResourceManager"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Runtime"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Runtime.Extensions"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Runtime.InteropServices"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Runtime.InteropServices.WindowsRuntime"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Runtime.Numerics"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Runtime.Serialization.Json"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Runtime.Serialization.Primitives"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Runtime.Serialization.Xml"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Security.Principal"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.ServiceModel.Duplex"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.ServiceModel.Http"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.ServiceModel.NetTcp"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.ServiceModel.Primitives"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.ServiceModel.Security"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Text.Encoding"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Text.Encoding.Extensions"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Text.RegularExpressions"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Threading"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Threading.Timer"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Threading.Tasks"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Threading.Tasks.Parallel"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Xml.ReaderWriter"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Xml.XDocument"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Xml.XmlSerializer"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Net.Http.Rtc"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Windows"] = new FrameworkRedirectInfo("b03f5f7f11d50a3a", "4.0.0.0");
|
||||||
|
frmRedir4["System.Xml.Serialization"] = new FrameworkRedirectInfo("b77a5c561934e089", "4.0.0.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Redirects a .NET Framework assembly from an older version to the correct version
|
||||||
|
/// loaded at runtime.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assembly">Current assembly reference that might get updated</param>
|
||||||
|
/// <param name="sourceModule">Module using the assembly reference</param>
|
||||||
|
public static void ApplyFrameworkRedirect(ref IAssembly assembly, ModuleDef sourceModule) {
|
||||||
|
if (TryApplyFrameworkRedirectCore(assembly, sourceModule, out var redirectedAssembly))
|
||||||
|
assembly = redirectedAssembly;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to redirect a .NET Framework assembly from an older version to the correct version
|
||||||
|
/// loaded at runtime.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assembly">Assembly reference</param>
|
||||||
|
/// <param name="sourceModule">Module using the assembly reference</param>
|
||||||
|
/// <param name="redirectedAssembly">Updated with the redirected assembly if successful</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool TryApplyFrameworkRedirect(IAssembly assembly, ModuleDef sourceModule, out IAssembly redirectedAssembly) =>
|
||||||
|
TryApplyFrameworkRedirectCore(assembly, sourceModule, out redirectedAssembly);
|
||||||
|
|
||||||
|
static bool TryApplyFrameworkRedirectCore(IAssembly assembly, ModuleDef sourceModule, out IAssembly redirectedAssembly) {
|
||||||
|
if (sourceModule is not null) {
|
||||||
|
if (sourceModule.IsClr40)
|
||||||
|
return TryApplyFrameworkRedirect(assembly, frmRedir4, out redirectedAssembly);
|
||||||
|
if (sourceModule.IsClr20)
|
||||||
|
return TryApplyFrameworkRedirect(assembly, frmRedir2, out redirectedAssembly);
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectedAssembly = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Redirects a .NET Framework 2.0-3.5 assembly from an older version to the correct version
|
||||||
|
/// loaded at runtime.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assembly">Current assembly reference that might get updated</param>
|
||||||
|
public static void ApplyFrameworkRedirectV2(ref IAssembly assembly) {
|
||||||
|
if (TryApplyFrameworkRedirect(assembly, frmRedir2, out var redirectedAssembly))
|
||||||
|
assembly = redirectedAssembly;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Redirects a .NET Framework 4.0+ assembly from an older version to the correct version
|
||||||
|
/// loaded at runtime.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assembly">Current assembly reference that might get updated</param>
|
||||||
|
public static void ApplyFrameworkRedirectV4(ref IAssembly assembly) {
|
||||||
|
if (TryApplyFrameworkRedirect(assembly, frmRedir4, out var redirectedAssembly))
|
||||||
|
assembly = redirectedAssembly;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to redirect a .NET Framework 2.0-3.5 assembly from an older version to the correct version
|
||||||
|
/// loaded at runtime.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assembly">Assembly reference</param>
|
||||||
|
/// <param name="redirectedAssembly">Updated with the redirected assembly if successful</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool TryApplyFrameworkRedirectV2(IAssembly assembly, out IAssembly redirectedAssembly) =>
|
||||||
|
TryApplyFrameworkRedirect(assembly, frmRedir2, out redirectedAssembly);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to redirect a .NET Framework 4.0+ assembly from an older version to the correct version
|
||||||
|
/// loaded at runtime.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assembly">Assembly reference</param>
|
||||||
|
/// <param name="redirectedAssembly">Updated with the redirected assembly if successful</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool TryApplyFrameworkRedirectV4(IAssembly assembly, out IAssembly redirectedAssembly) =>
|
||||||
|
TryApplyFrameworkRedirect(assembly, frmRedir4, out redirectedAssembly);
|
||||||
|
|
||||||
|
static bool TryApplyFrameworkRedirect(IAssembly assembly, Dictionary<string, FrameworkRedirectInfo> frmRedir, out IAssembly redirectedAssembly) {
|
||||||
|
redirectedAssembly = null;
|
||||||
|
if (!Utils.LocaleEquals(assembly.Culture, ""))
|
||||||
|
return false;
|
||||||
|
if (!frmRedir.TryGetValue(assembly.Name, out var redirect))
|
||||||
|
return false;
|
||||||
|
if (PublicKeyBase.TokenCompareTo(assembly.PublicKeyOrToken, redirect.publicKeyToken) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Utils.CompareTo(assembly.Version, redirect.redirectVersion) == 0)
|
||||||
|
redirectedAssembly = assembly;
|
||||||
|
else {
|
||||||
|
redirectedAssembly = new AssemblyNameInfo(assembly);
|
||||||
|
redirectedAssembly.Version = redirect.redirectVersion;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,120 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
readonly struct GenericArgumentsStack {
|
||||||
|
readonly List<IList<TypeSig>> argsStack;
|
||||||
|
readonly bool isTypeVar;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isTypeVar"><c>true</c> if it's for generic types, <c>false</c> if generic methods</param>
|
||||||
|
public GenericArgumentsStack(bool isTypeVar) {
|
||||||
|
argsStack = new List<IList<TypeSig>>();
|
||||||
|
this.isTypeVar = isTypeVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pushes generic arguments
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">The generic arguments</param>
|
||||||
|
public void Push(IList<TypeSig> args) => argsStack.Add(args);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pops generic arguments
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The popped generic arguments</returns>
|
||||||
|
public IList<TypeSig> Pop() {
|
||||||
|
int index = argsStack.Count - 1;
|
||||||
|
var result = argsStack[index];
|
||||||
|
argsStack.RemoveAt(index);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a generic argument
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="number">Generic variable number</param>
|
||||||
|
/// <returns>A <see cref="TypeSig"/> or <c>null</c> if none was found</returns>
|
||||||
|
public TypeSig Resolve(uint number) {
|
||||||
|
TypeSig result = null;
|
||||||
|
for (int i = argsStack.Count - 1; i >= 0; i--) {
|
||||||
|
var args = argsStack[i];
|
||||||
|
if (number >= args.Count)
|
||||||
|
return null;
|
||||||
|
var typeSig = args[(int)number];
|
||||||
|
var gvar = typeSig as GenericSig;
|
||||||
|
if (gvar is null || gvar.IsTypeVar != isTypeVar)
|
||||||
|
return typeSig;
|
||||||
|
result = gvar;
|
||||||
|
number = gvar.Number;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces generic type/method var with its generic argument
|
||||||
|
/// </summary>
|
||||||
|
sealed class GenericArguments {
|
||||||
|
GenericArgumentsStack typeArgsStack = new GenericArgumentsStack(true);
|
||||||
|
GenericArgumentsStack methodArgsStack = new GenericArgumentsStack(false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pushes generic arguments
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeArgs">The generic arguments</param>
|
||||||
|
public void PushTypeArgs(IList<TypeSig> typeArgs) => typeArgsStack.Push(typeArgs);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pops generic arguments
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The popped generic arguments</returns>
|
||||||
|
public IList<TypeSig> PopTypeArgs() => typeArgsStack.Pop();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pushes generic arguments
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="methodArgs">The generic arguments</param>
|
||||||
|
public void PushMethodArgs(IList<TypeSig> methodArgs) => methodArgsStack.Push(methodArgs);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pops generic arguments
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The popped generic arguments</returns>
|
||||||
|
public IList<TypeSig> PopMethodArgs() => methodArgsStack.Pop();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces a generic type/method var with its generic argument (if any). If
|
||||||
|
/// <paramref name="typeSig"/> isn't a generic type/method var or if it can't
|
||||||
|
/// be resolved, it itself is returned. Else the resolved type is returned.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeSig">Type signature</param>
|
||||||
|
/// <returns>New <see cref="TypeSig"/> which is never <c>null</c> unless
|
||||||
|
/// <paramref name="typeSig"/> is <c>null</c></returns>
|
||||||
|
public TypeSig Resolve(TypeSig typeSig) {
|
||||||
|
if (typeSig is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var sig = typeSig;
|
||||||
|
|
||||||
|
if (sig is GenericMVar genericMVar) {
|
||||||
|
var newSig = methodArgsStack.Resolve(genericMVar.Number);
|
||||||
|
if (newSig is null || newSig == sig)
|
||||||
|
return sig;
|
||||||
|
return newSig;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sig is GenericVar genericVar) {
|
||||||
|
var newSig = typeArgsStack.Resolve(genericVar.Number);
|
||||||
|
if (newSig is null || newSig == sig)
|
||||||
|
return sig;
|
||||||
|
return newSig;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,431 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
using dnlib.Utils;
|
||||||
|
using dnlib.DotNet.MD;
|
||||||
|
using dnlib.DotNet.Pdb;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// A high-level representation of a row in the GenericParam table
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("{Name.String}")]
|
||||||
|
public abstract class GenericParam : IHasCustomAttribute, IHasCustomDebugInformation, IMemberDef, IListListener<GenericParamConstraint> {
|
||||||
|
/// <summary>
|
||||||
|
/// The row id in its table
|
||||||
|
/// </summary>
|
||||||
|
protected uint rid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public MDToken MDToken => new MDToken(Table.GenericParam, rid);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint Rid {
|
||||||
|
get => rid;
|
||||||
|
set => rid = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomAttributeTag => 19;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the owner type/method
|
||||||
|
/// </summary>
|
||||||
|
public ITypeOrMethodDef Owner {
|
||||||
|
get => owner;
|
||||||
|
internal set => owner = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected ITypeOrMethodDef owner;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the declaring type or <c>null</c> if none or if <see cref="Owner"/> is
|
||||||
|
/// not a <see cref="TypeDef"/>
|
||||||
|
/// </summary>
|
||||||
|
public TypeDef DeclaringType => owner as TypeDef;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
ITypeDefOrRef IMemberRef.DeclaringType => owner as TypeDef;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the declaring method or <c>null</c> if none or if <see cref="Owner"/> is
|
||||||
|
/// not a <see cref="MethodDef"/>
|
||||||
|
/// </summary>
|
||||||
|
public MethodDef DeclaringMethod => owner as MethodDef;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column GenericParam.Number
|
||||||
|
/// </summary>
|
||||||
|
public ushort Number {
|
||||||
|
get => number;
|
||||||
|
set => number = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected ushort number;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column GenericParam.Flags
|
||||||
|
/// </summary>
|
||||||
|
public GenericParamAttributes Flags {
|
||||||
|
get => (GenericParamAttributes)attributes;
|
||||||
|
set => attributes = (int)value;
|
||||||
|
}
|
||||||
|
/// <summary>Attributes</summary>
|
||||||
|
protected int attributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column GenericParam.Name
|
||||||
|
/// </summary>
|
||||||
|
public UTF8String Name {
|
||||||
|
get => name;
|
||||||
|
set => name = value;
|
||||||
|
}
|
||||||
|
/// <summary>Name</summary>
|
||||||
|
protected UTF8String name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column GenericParam.Kind (v1.1 only)
|
||||||
|
/// </summary>
|
||||||
|
public ITypeDefOrRef Kind {
|
||||||
|
get => kind;
|
||||||
|
set => kind = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected ITypeDefOrRef kind;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the generic param constraints
|
||||||
|
/// </summary>
|
||||||
|
public IList<GenericParamConstraint> GenericParamConstraints {
|
||||||
|
get {
|
||||||
|
if (genericParamConstraints is null)
|
||||||
|
InitializeGenericParamConstraints();
|
||||||
|
return genericParamConstraints;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected LazyList<GenericParamConstraint> genericParamConstraints;
|
||||||
|
/// <summary>Initializes <see cref="genericParamConstraints"/></summary>
|
||||||
|
protected virtual void InitializeGenericParamConstraints() =>
|
||||||
|
Interlocked.CompareExchange(ref genericParamConstraints, new LazyList<GenericParamConstraint>(this), null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom attributes
|
||||||
|
/// </summary>
|
||||||
|
public CustomAttributeCollection CustomAttributes {
|
||||||
|
get {
|
||||||
|
if (customAttributes is null)
|
||||||
|
InitializeCustomAttributes();
|
||||||
|
return customAttributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected CustomAttributeCollection customAttributes;
|
||||||
|
/// <summary>Initializes <see cref="customAttributes"/></summary>
|
||||||
|
protected virtual void InitializeCustomAttributes() =>
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomAttributes => CustomAttributes.Count > 0;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomDebugInformationTag => 19;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom debug infos
|
||||||
|
/// </summary>
|
||||||
|
public IList<PdbCustomDebugInfo> CustomDebugInfos {
|
||||||
|
get {
|
||||||
|
if (customDebugInfos is null)
|
||||||
|
InitializeCustomDebugInfos();
|
||||||
|
return customDebugInfos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected IList<PdbCustomDebugInfo> customDebugInfos;
|
||||||
|
/// <summary>Initializes <see cref="customDebugInfos"/></summary>
|
||||||
|
protected virtual void InitializeCustomDebugInfos() =>
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, new List<PdbCustomDebugInfo>(), null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="GenericParamConstraints"/> is not empty
|
||||||
|
/// </summary>
|
||||||
|
public bool HasGenericParamConstraints => GenericParamConstraints.Count > 0;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ModuleDef Module => owner?.Module;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public string FullName => UTF8String.ToSystemStringOrEmpty(name);
|
||||||
|
|
||||||
|
bool IIsTypeOrMethod.IsType => false;
|
||||||
|
bool IIsTypeOrMethod.IsMethod => false;
|
||||||
|
bool IMemberRef.IsField => false;
|
||||||
|
bool IMemberRef.IsTypeSpec => false;
|
||||||
|
bool IMemberRef.IsTypeRef => false;
|
||||||
|
bool IMemberRef.IsTypeDef => false;
|
||||||
|
bool IMemberRef.IsMethodSpec => false;
|
||||||
|
bool IMemberRef.IsMethodDef => false;
|
||||||
|
bool IMemberRef.IsMemberRef => false;
|
||||||
|
bool IMemberRef.IsFieldDef => false;
|
||||||
|
bool IMemberRef.IsPropertyDef => false;
|
||||||
|
bool IMemberRef.IsEventDef => false;
|
||||||
|
bool IMemberRef.IsGenericParam => true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Modify <see cref="attributes"/> property: <see cref="attributes"/> =
|
||||||
|
/// (<see cref="attributes"/> & <paramref name="andMask"/>) | <paramref name="orMask"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="andMask">Value to <c>AND</c></param>
|
||||||
|
/// <param name="orMask">Value to OR</param>
|
||||||
|
void ModifyAttributes(GenericParamAttributes andMask, GenericParamAttributes orMask) =>
|
||||||
|
attributes = (attributes & (int)andMask) | (int)orMask;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set or clear flags in <see cref="attributes"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="set"><c>true</c> if flags should be set, <c>false</c> if flags should
|
||||||
|
/// be cleared</param>
|
||||||
|
/// <param name="flags">Flags to set or clear</param>
|
||||||
|
void ModifyAttributes(bool set, GenericParamAttributes flags) {
|
||||||
|
if (set)
|
||||||
|
attributes |= (int)flags;
|
||||||
|
else
|
||||||
|
attributes &= ~(int)flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets variance (non, contra, co)
|
||||||
|
/// </summary>
|
||||||
|
public GenericParamAttributes Variance {
|
||||||
|
get => (GenericParamAttributes)attributes & GenericParamAttributes.VarianceMask;
|
||||||
|
set => ModifyAttributes(~GenericParamAttributes.VarianceMask, value & GenericParamAttributes.VarianceMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="GenericParamAttributes.NonVariant"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsNonVariant => Variance == GenericParamAttributes.NonVariant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="GenericParamAttributes.Covariant"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCovariant => Variance == GenericParamAttributes.Covariant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="GenericParamAttributes.Contravariant"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsContravariant => Variance == GenericParamAttributes.Contravariant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the special constraint
|
||||||
|
/// </summary>
|
||||||
|
public GenericParamAttributes SpecialConstraint {
|
||||||
|
get => (GenericParamAttributes)attributes & GenericParamAttributes.SpecialConstraintMask;
|
||||||
|
set => ModifyAttributes(~GenericParamAttributes.SpecialConstraintMask, value & GenericParamAttributes.SpecialConstraintMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if there are no special constraints
|
||||||
|
/// </summary>
|
||||||
|
public bool HasNoSpecialConstraint => ((GenericParamAttributes)attributes & GenericParamAttributes.SpecialConstraintMask) == GenericParamAttributes.NoSpecialConstraint;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="GenericParamAttributes.ReferenceTypeConstraint"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool HasReferenceTypeConstraint {
|
||||||
|
get => ((GenericParamAttributes)attributes & GenericParamAttributes.ReferenceTypeConstraint) != 0;
|
||||||
|
set => ModifyAttributes(value, GenericParamAttributes.ReferenceTypeConstraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="GenericParamAttributes.NotNullableValueTypeConstraint"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool HasNotNullableValueTypeConstraint {
|
||||||
|
get => ((GenericParamAttributes)attributes & GenericParamAttributes.NotNullableValueTypeConstraint) != 0;
|
||||||
|
set => ModifyAttributes(value, GenericParamAttributes.NotNullableValueTypeConstraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="GenericParamAttributes.DefaultConstructorConstraint"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool HasDefaultConstructorConstraint {
|
||||||
|
get => ((GenericParamAttributes)attributes & GenericParamAttributes.DefaultConstructorConstraint) != 0;
|
||||||
|
set => ModifyAttributes(value, GenericParamAttributes.DefaultConstructorConstraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
void IListListener<GenericParamConstraint>.OnLazyAdd(int index, ref GenericParamConstraint value) => OnLazyAdd2(index, ref value);
|
||||||
|
|
||||||
|
internal virtual void OnLazyAdd2(int index, ref GenericParamConstraint value) {
|
||||||
|
#if DEBUG
|
||||||
|
if (value.Owner != this)
|
||||||
|
throw new InvalidOperationException("Added generic param constraint's Owner != this");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
void IListListener<GenericParamConstraint>.OnAdd(int index, GenericParamConstraint value) {
|
||||||
|
if (value.Owner is not null)
|
||||||
|
throw new InvalidOperationException("Generic param constraint is already owned by another generic param. Set Owner to null first.");
|
||||||
|
value.Owner = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
void IListListener<GenericParamConstraint>.OnRemove(int index, GenericParamConstraint value) => value.Owner = null;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
void IListListener<GenericParamConstraint>.OnResize(int index) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
void IListListener<GenericParamConstraint>.OnClear() {
|
||||||
|
foreach (var gpc in genericParamConstraints.GetEnumerable_NoLock())
|
||||||
|
gpc.Owner = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString() {
|
||||||
|
var o = owner;
|
||||||
|
if (o is TypeDef)
|
||||||
|
return $"!{number}";
|
||||||
|
if (o is MethodDef)
|
||||||
|
return $"!!{number}";
|
||||||
|
return $"??{number}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A GenericParam row created by the user and not present in the original .NET file
|
||||||
|
/// </summary>
|
||||||
|
public class GenericParamUser : GenericParam {
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public GenericParamUser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="number">The generic param number</param>
|
||||||
|
public GenericParamUser(ushort number)
|
||||||
|
: this(number, 0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="number">The generic param number</param>
|
||||||
|
/// <param name="flags">Flags</param>
|
||||||
|
public GenericParamUser(ushort number, GenericParamAttributes flags)
|
||||||
|
: this(number, flags, UTF8String.Empty) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="number">The generic param number</param>
|
||||||
|
/// <param name="flags">Flags</param>
|
||||||
|
/// <param name="name">Name</param>
|
||||||
|
public GenericParamUser(ushort number, GenericParamAttributes flags, UTF8String name) {
|
||||||
|
genericParamConstraints = new LazyList<GenericParamConstraint>(this);
|
||||||
|
this.number = number;
|
||||||
|
attributes = (int)flags;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Created from a row in the GenericParam table
|
||||||
|
/// </summary>
|
||||||
|
sealed class GenericParamMD : GenericParam, IMDTokenProviderMD {
|
||||||
|
/// <summary>The module where this instance is located</summary>
|
||||||
|
readonly ModuleDefMD readerModule;
|
||||||
|
|
||||||
|
readonly uint origRid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint OrigRid => origRid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomAttributes() {
|
||||||
|
var list = readerModule.Metadata.GetCustomAttributeRidList(Table.GenericParam, origRid);
|
||||||
|
var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index]));
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, tmp, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomDebugInfos() {
|
||||||
|
var list = new List<PdbCustomDebugInfo>();
|
||||||
|
readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), GetGenericParamContext(owner), list);
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, list, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeGenericParamConstraints() {
|
||||||
|
var list = readerModule.Metadata.GetGenericParamConstraintRidList(origRid);
|
||||||
|
var tmp = new LazyList<GenericParamConstraint, RidList>(list.Count, this, list, (list2, index) => readerModule.ResolveGenericParamConstraint(list2[index], GetGenericParamContext(owner)));
|
||||||
|
Interlocked.CompareExchange(ref genericParamConstraints, tmp, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GenericParamContext GetGenericParamContext(ITypeOrMethodDef tmOwner) {
|
||||||
|
if (tmOwner is MethodDef md)
|
||||||
|
return GenericParamContext.Create(md);
|
||||||
|
return new GenericParamContext(tmOwner as TypeDef);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readerModule">The module which contains this <c>GenericParam</c> row</param>
|
||||||
|
/// <param name="rid">Row ID</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If <paramref name="readerModule"/> is <c>null</c></exception>
|
||||||
|
/// <exception cref="ArgumentException">If <paramref name="rid"/> is invalid</exception>
|
||||||
|
public GenericParamMD(ModuleDefMD readerModule, uint rid) {
|
||||||
|
#if DEBUG
|
||||||
|
if (readerModule is null)
|
||||||
|
throw new ArgumentNullException("readerModule");
|
||||||
|
if (readerModule.TablesStream.GenericParamTable.IsInvalidRID(rid))
|
||||||
|
throw new BadImageFormatException($"GenericParam rid {rid} does not exist");
|
||||||
|
#endif
|
||||||
|
origRid = rid;
|
||||||
|
this.rid = rid;
|
||||||
|
this.readerModule = readerModule;
|
||||||
|
bool b = readerModule.TablesStream.TryReadGenericParamRow(origRid, out var row);
|
||||||
|
Debug.Assert(b);
|
||||||
|
number = row.Number;
|
||||||
|
attributes = row.Flags;
|
||||||
|
name = readerModule.StringsStream.ReadNoNull(row.Name);
|
||||||
|
owner = readerModule.GetOwner(this);
|
||||||
|
if (row.Kind != 0)
|
||||||
|
kind = readerModule.ResolveTypeDefOrRef(row.Kind, GetGenericParamContext(owner));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal GenericParamMD InitializeAll() {
|
||||||
|
MemberMDInitializer.Initialize(Owner);
|
||||||
|
MemberMDInitializer.Initialize(Number);
|
||||||
|
MemberMDInitializer.Initialize(Flags);
|
||||||
|
MemberMDInitializer.Initialize(Name);
|
||||||
|
MemberMDInitializer.Initialize(Kind);
|
||||||
|
MemberMDInitializer.Initialize(CustomAttributes);
|
||||||
|
MemberMDInitializer.Initialize(GenericParamConstraints);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
internal override void OnLazyAdd2(int index, ref GenericParamConstraint value) {
|
||||||
|
if (value.Owner != this) {
|
||||||
|
// More than one owner... This module has invalid metadata.
|
||||||
|
value = readerModule.ForceUpdateRowId(readerModule.ReadGenericParamConstraint(value.Rid, GetGenericParamContext(owner)).InitializeAll());
|
||||||
|
value.Owner = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Generic parameter flags. See CorHdr.h/CorGenericParamAttr
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum GenericParamAttributes : ushort {
|
||||||
|
/// <summary/>
|
||||||
|
VarianceMask = 0x0003,
|
||||||
|
/// <summary/>
|
||||||
|
NonVariant = 0x0000,
|
||||||
|
/// <summary/>
|
||||||
|
Covariant = 0x0001,
|
||||||
|
/// <summary/>
|
||||||
|
Contravariant = 0x0002,
|
||||||
|
|
||||||
|
/// <summary/>
|
||||||
|
SpecialConstraintMask = 0x001C,
|
||||||
|
/// <summary/>
|
||||||
|
NoSpecialConstraint = 0x0000,
|
||||||
|
/// <summary>type argument must be a reference type</summary>
|
||||||
|
ReferenceTypeConstraint = 0x0004,
|
||||||
|
/// <summary>type argument must be a value type but not Nullable</summary>
|
||||||
|
NotNullableValueTypeConstraint = 0x0008,
|
||||||
|
/// <summary>type argument must have a public default constructor</summary>
|
||||||
|
DefaultConstructorConstraint = 0x0010,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,174 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
using dnlib.DotNet.MD;
|
||||||
|
using dnlib.DotNet.Pdb;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// A high-level representation of a row in the GenericParamConstraint table
|
||||||
|
/// </summary>
|
||||||
|
public abstract class GenericParamConstraint : IHasCustomAttribute, IHasCustomDebugInformation, IContainsGenericParameter {
|
||||||
|
/// <summary>
|
||||||
|
/// The row id in its table
|
||||||
|
/// </summary>
|
||||||
|
protected uint rid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public MDToken MDToken => new MDToken(Table.GenericParamConstraint, rid);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint Rid {
|
||||||
|
get => rid;
|
||||||
|
set => rid = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomAttributeTag => 20;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the owner generic param
|
||||||
|
/// </summary>
|
||||||
|
public GenericParam Owner {
|
||||||
|
get => owner;
|
||||||
|
internal set => owner = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected GenericParam owner;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column GenericParamConstraint.Constraint
|
||||||
|
/// </summary>
|
||||||
|
public ITypeDefOrRef Constraint {
|
||||||
|
get => constraint;
|
||||||
|
set => constraint = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected ITypeDefOrRef constraint;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom attributes
|
||||||
|
/// </summary>
|
||||||
|
public CustomAttributeCollection CustomAttributes {
|
||||||
|
get {
|
||||||
|
if (customAttributes is null)
|
||||||
|
InitializeCustomAttributes();
|
||||||
|
return customAttributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected CustomAttributeCollection customAttributes;
|
||||||
|
/// <summary>Initializes <see cref="customAttributes"/></summary>
|
||||||
|
protected virtual void InitializeCustomAttributes() =>
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomAttributes => CustomAttributes.Count > 0;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomDebugInformationTag => 20;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom debug infos
|
||||||
|
/// </summary>
|
||||||
|
public IList<PdbCustomDebugInfo> CustomDebugInfos {
|
||||||
|
get {
|
||||||
|
if (customDebugInfos is null)
|
||||||
|
InitializeCustomDebugInfos();
|
||||||
|
return customDebugInfos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected IList<PdbCustomDebugInfo> customDebugInfos;
|
||||||
|
/// <summary>Initializes <see cref="customDebugInfos"/></summary>
|
||||||
|
protected virtual void InitializeCustomDebugInfos() =>
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, new List<PdbCustomDebugInfo>(), null);
|
||||||
|
|
||||||
|
bool IContainsGenericParameter.ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A GenericParamConstraintAssembly row created by the user and not present in the original .NET file
|
||||||
|
/// </summary>
|
||||||
|
public class GenericParamConstraintUser : GenericParamConstraint {
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public GenericParamConstraintUser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="constraint">The constraint</param>
|
||||||
|
public GenericParamConstraintUser(ITypeDefOrRef constraint) => this.constraint = constraint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Created from a row in the GenericParamConstraint table
|
||||||
|
/// </summary>
|
||||||
|
sealed class GenericParamConstraintMD : GenericParamConstraint, IMDTokenProviderMD, IContainsGenericParameter2 {
|
||||||
|
/// <summary>The module where this instance is located</summary>
|
||||||
|
readonly ModuleDefMD readerModule;
|
||||||
|
|
||||||
|
readonly uint origRid;
|
||||||
|
readonly GenericParamContext gpContext;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint OrigRid => origRid;
|
||||||
|
|
||||||
|
bool IContainsGenericParameter2.ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomAttributes() {
|
||||||
|
var list = readerModule.Metadata.GetCustomAttributeRidList(Table.GenericParamConstraint, origRid);
|
||||||
|
var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index]));
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, tmp, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomDebugInfos() {
|
||||||
|
var list = new List<PdbCustomDebugInfo>();
|
||||||
|
readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), gpContext, list);
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, list, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readerModule">The module which contains this <c>GenericParamConstraint</c> row</param>
|
||||||
|
/// <param name="rid">Row ID</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If <paramref name="readerModule"/> is <c>null</c></exception>
|
||||||
|
/// <exception cref="ArgumentException">If <paramref name="rid"/> is invalid</exception>
|
||||||
|
public GenericParamConstraintMD(ModuleDefMD readerModule, uint rid, GenericParamContext gpContext) {
|
||||||
|
#if DEBUG
|
||||||
|
if (readerModule is null)
|
||||||
|
throw new ArgumentNullException("readerModule");
|
||||||
|
if (readerModule.TablesStream.GenericParamConstraintTable.IsInvalidRID(rid))
|
||||||
|
throw new BadImageFormatException($"GenericParamConstraint rid {rid} does not exist");
|
||||||
|
#endif
|
||||||
|
origRid = rid;
|
||||||
|
this.rid = rid;
|
||||||
|
this.readerModule = readerModule;
|
||||||
|
this.gpContext = gpContext;
|
||||||
|
bool b = readerModule.TablesStream.TryReadGenericParamConstraintRow(origRid, out var row);
|
||||||
|
Debug.Assert(b);
|
||||||
|
constraint = readerModule.ResolveTypeDefOrRef(row.Constraint, gpContext);
|
||||||
|
owner = readerModule.GetOwner(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal GenericParamConstraintMD InitializeAll() {
|
||||||
|
MemberMDInitializer.Initialize(Owner);
|
||||||
|
MemberMDInitializer.Initialize(Constraint);
|
||||||
|
MemberMDInitializer.Initialize(CustomAttributes);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Generic parameter context
|
||||||
|
/// </summary>
|
||||||
|
public readonly struct GenericParamContext {
|
||||||
|
/// <summary>
|
||||||
|
/// Type context
|
||||||
|
/// </summary>
|
||||||
|
public readonly TypeDef Type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Method context
|
||||||
|
/// </summary>
|
||||||
|
public readonly MethodDef Method;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// true if <see cref="Type"/> and <see cref="Method"/> are both <c>null</c>
|
||||||
|
/// </summary>
|
||||||
|
public bool IsEmpty => Type is null && Method is null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="GenericParamContext"/> instance and initializes the
|
||||||
|
/// <see cref="Type"/> field to <paramref name="method"/>'s <see cref="MethodDef.DeclaringType"/>
|
||||||
|
/// and the <see cref="Method"/> field to <paramref name="method"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method">Method</param>
|
||||||
|
/// <returns>A new <see cref="GenericParamContext"/> instance</returns>
|
||||||
|
public static GenericParamContext Create(MethodDef method) {
|
||||||
|
if (method is null)
|
||||||
|
return new GenericParamContext();
|
||||||
|
return new GenericParamContext(method.DeclaringType, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="GenericParamContext"/> instance and initializes the
|
||||||
|
/// <see cref="Type"/> field to <paramref name="type"/> and the <see cref="Method"/> field
|
||||||
|
/// to <c>null</c>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Type</param>
|
||||||
|
/// <returns>A new <see cref="GenericParamContext"/> instance</returns>
|
||||||
|
public static GenericParamContext Create(TypeDef type) => new GenericParamContext(type);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Type context</param>
|
||||||
|
public GenericParamContext(TypeDef type) {
|
||||||
|
Type = type;
|
||||||
|
Method = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor. The <see cref="Type"/> field is set to <c>null</c> and <c>NOT</c> to
|
||||||
|
/// <paramref name="method"/>'s <see cref="MethodDef.DeclaringType"/>. Use
|
||||||
|
/// <see cref="Create(MethodDef)"/> if you want that behavior.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method">Method context</param>
|
||||||
|
public GenericParamContext(MethodDef method) {
|
||||||
|
Type = null;
|
||||||
|
Method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Type context</param>
|
||||||
|
/// <param name="method">Method context</param>
|
||||||
|
public GenericParamContext(TypeDef type, MethodDef method) {
|
||||||
|
Type = type;
|
||||||
|
Method = method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves assemblies
|
||||||
|
/// </summary>
|
||||||
|
public interface IAssemblyResolver {
|
||||||
|
/// <summary>
|
||||||
|
/// Finds and returns an <see cref="AssemblyDef"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assembly">The assembly to find</param>
|
||||||
|
/// <param name="sourceModule">The module that needs to resolve an assembly or <c>null</c></param>
|
||||||
|
/// <returns>An <see cref="AssemblyDef"/> instance owned by the assembly resolver or
|
||||||
|
/// <c>null</c> if the assembly couldn't be found.</returns>
|
||||||
|
AssemblyDef Resolve(IAssembly assembly, ModuleDef sourceModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static partial class Extensions {
|
||||||
|
/// <summary>
|
||||||
|
/// Finds and returns an <see cref="AssemblyDef"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="assembly">The assembly to find</param>
|
||||||
|
/// <param name="sourceModule">The module that needs to resolve an assembly or <c>null</c></param>
|
||||||
|
/// <returns>An <see cref="AssemblyDef"/> instance owned by the assembly resolver or
|
||||||
|
/// <c>null</c> if the assembly couldn't be found.</returns>
|
||||||
|
public static AssemblyDef Resolve(this IAssemblyResolver self, AssemblyName assembly, ModuleDef sourceModule) {
|
||||||
|
if (assembly is null)
|
||||||
|
return null;
|
||||||
|
return self.Resolve(new AssemblyNameInfo(assembly), sourceModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds and returns an <see cref="AssemblyDef"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="asmFullName">The assembly to find</param>
|
||||||
|
/// <param name="sourceModule">The module that needs to resolve an assembly or <c>null</c></param>
|
||||||
|
/// <returns>An <see cref="AssemblyDef"/> instance owned by the assembly resolver or
|
||||||
|
/// <c>null</c> if the assembly couldn't be found.</returns>
|
||||||
|
public static AssemblyDef Resolve(this IAssemblyResolver self, string asmFullName, ModuleDef sourceModule) {
|
||||||
|
if (asmFullName is null)
|
||||||
|
return null;
|
||||||
|
return self.Resolve(new AssemblyNameInfo(asmFullName), sourceModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds and returns an <see cref="AssemblyDef"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="assembly">The assembly to find</param>
|
||||||
|
/// <param name="sourceModule">The module that needs to resolve an assembly or <c>null</c></param>
|
||||||
|
/// <returns>An <see cref="AssemblyDef"/> instance owned by the assembly resolver</returns>
|
||||||
|
/// <exception cref="AssemblyResolveException">If the assembly couldn't be found.</exception>
|
||||||
|
public static AssemblyDef ResolveThrow(this IAssemblyResolver self, IAssembly assembly, ModuleDef sourceModule) {
|
||||||
|
if (assembly is null)
|
||||||
|
return null;
|
||||||
|
var asm = self.Resolve(assembly, sourceModule);
|
||||||
|
if (asm is not null)
|
||||||
|
return asm;
|
||||||
|
throw new AssemblyResolveException($"Could not resolve assembly: {assembly}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds and returns an <see cref="AssemblyDef"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="assembly">The assembly to find</param>
|
||||||
|
/// <param name="sourceModule">The module that needs to resolve an assembly or <c>null</c></param>
|
||||||
|
/// <returns>An <see cref="AssemblyDef"/> instance owned by the assembly resolver</returns>
|
||||||
|
/// <exception cref="AssemblyResolveException">If the assembly couldn't be found.</exception>
|
||||||
|
public static AssemblyDef ResolveThrow(this IAssemblyResolver self, AssemblyName assembly, ModuleDef sourceModule) {
|
||||||
|
if (assembly is null)
|
||||||
|
return null;
|
||||||
|
var asm = self.Resolve(new AssemblyNameInfo(assembly), sourceModule);
|
||||||
|
if (asm is not null)
|
||||||
|
return asm;
|
||||||
|
throw new AssemblyResolveException($"Could not resolve assembly: {assembly}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds and returns an <see cref="AssemblyDef"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="asmFullName">The assembly to find</param>
|
||||||
|
/// <param name="sourceModule">The module that needs to resolve an assembly or <c>null</c></param>
|
||||||
|
/// <returns>An <see cref="AssemblyDef"/> instance owned by the assembly resolver</returns>
|
||||||
|
/// <exception cref="AssemblyResolveException">If the assembly couldn't be found.</exception>
|
||||||
|
public static AssemblyDef ResolveThrow(this IAssemblyResolver self, string asmFullName, ModuleDef sourceModule) {
|
||||||
|
if (asmFullName is null)
|
||||||
|
return null;
|
||||||
|
var asm = self.Resolve(new AssemblyNameInfo(asmFullName), sourceModule);
|
||||||
|
if (asm is not null)
|
||||||
|
return asm;
|
||||||
|
throw new AssemblyResolveException($"Could not resolve assembly: {asmFullName}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,186 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Access to .NET core library's simple types
|
||||||
|
/// </summary>
|
||||||
|
public interface ICorLibTypes {
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.Void</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig Void { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.Boolean</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig Boolean { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.Char</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig Char { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.SByte</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig SByte { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.Byte</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig Byte { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.Int16</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig Int16 { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.UInt16</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig UInt16 { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.Int32</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig Int32 { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.UInt32</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig UInt32 { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.Int64</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig Int64 { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.UInt64</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig UInt64 { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.Single</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig Single { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.Double</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig Double { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.String</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig String { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.TypedReference</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig TypedReference { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.IntPtr</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig IntPtr { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.UIntPtr</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig UIntPtr { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <c>System.Object</c>
|
||||||
|
/// </summary>
|
||||||
|
CorLibTypeSig Object { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the assembly reference to the core library
|
||||||
|
/// </summary>
|
||||||
|
AssemblyRef AssemblyRef { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="TypeRef"/> that references a type in the core library assembly
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="namespace">Namespace of type (eg. "System")</param>
|
||||||
|
/// <param name="name">Name of type</param>
|
||||||
|
/// <returns>A <see cref="TypeRef"/> instance. This instance may be a cached instance.</returns>
|
||||||
|
TypeRef GetTypeRef(string @namespace, string name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static partial class Extensions {
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="CorLibTypeSig"/> if <paramref name="type"/> matches a primitive type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="type">The type</param>
|
||||||
|
/// <returns>A <see cref="CorLibTypeSig"/> or <c>null</c> if it didn't match any primitive type</returns>
|
||||||
|
public static CorLibTypeSig GetCorLibTypeSig(this ICorLibTypes self, ITypeDefOrRef type) {
|
||||||
|
CorLibTypeSig corLibType;
|
||||||
|
|
||||||
|
if (type is TypeDef td &&
|
||||||
|
td.DeclaringType is null &&
|
||||||
|
(corLibType = self.GetCorLibTypeSig(td.Namespace, td.Name, td.DefinitionAssembly)) is not null) {
|
||||||
|
return corLibType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type is TypeRef tr &&
|
||||||
|
!(tr.ResolutionScope is TypeRef) &&
|
||||||
|
(corLibType = self.GetCorLibTypeSig(tr.Namespace, tr.Name, tr.DefinitionAssembly)) is not null) {
|
||||||
|
return corLibType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="CorLibTypeSig"/> if <paramref name="namespace"/> and
|
||||||
|
/// <paramref name="name"/> matches a primitive type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="namespace">Namespace</param>
|
||||||
|
/// <param name="name">Name</param>
|
||||||
|
/// <param name="defAsm">Definition assembly</param>
|
||||||
|
/// <returns>A <see cref="CorLibTypeSig"/> or <c>null</c> if it didn't match any primitive type</returns>
|
||||||
|
public static CorLibTypeSig GetCorLibTypeSig(this ICorLibTypes self, UTF8String @namespace, UTF8String name, IAssembly defAsm) =>
|
||||||
|
self.GetCorLibTypeSig(UTF8String.ToSystemStringOrEmpty(@namespace), UTF8String.ToSystemStringOrEmpty(name), defAsm);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="CorLibTypeSig"/> if <paramref name="namespace"/> and
|
||||||
|
/// <paramref name="name"/> matches a primitive type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="namespace">Namespace</param>
|
||||||
|
/// <param name="name">Name</param>
|
||||||
|
/// <param name="defAsm">Definition assembly</param>
|
||||||
|
/// <returns>A <see cref="CorLibTypeSig"/> or <c>null</c> if it didn't match any primitive type</returns>
|
||||||
|
public static CorLibTypeSig GetCorLibTypeSig(this ICorLibTypes self, string @namespace, string name, IAssembly defAsm) {
|
||||||
|
if (@namespace != "System")
|
||||||
|
return null;
|
||||||
|
if (defAsm is null || !defAsm.IsCorLib())
|
||||||
|
return null;
|
||||||
|
return name switch {
|
||||||
|
"Void" => self.Void,
|
||||||
|
"Boolean" => self.Boolean,
|
||||||
|
"Char" => self.Char,
|
||||||
|
"SByte" => self.SByte,
|
||||||
|
"Byte" => self.Byte,
|
||||||
|
"Int16" => self.Int16,
|
||||||
|
"UInt16" => self.UInt16,
|
||||||
|
"Int32" => self.Int32,
|
||||||
|
"UInt32" => self.UInt32,
|
||||||
|
"Int64" => self.Int64,
|
||||||
|
"UInt64" => self.UInt64,
|
||||||
|
"Single" => self.Single,
|
||||||
|
"Double" => self.Double,
|
||||||
|
"String" => self.String,
|
||||||
|
"TypedReference" => self.TypedReference,
|
||||||
|
"IntPtr" => self.IntPtr,
|
||||||
|
"UIntPtr" => self.UIntPtr,
|
||||||
|
"Object" => self.Object,
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Custom attribute interface. Implemented by <see cref="SecurityAttribute"/> and
|
||||||
|
/// <see cref="CustomAttribute"/>
|
||||||
|
/// </summary>
|
||||||
|
public interface ICustomAttribute {
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the attribute type
|
||||||
|
/// </summary>
|
||||||
|
ITypeDefOrRef AttributeType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the full name of the attribute type
|
||||||
|
/// </summary>
|
||||||
|
string TypeFullName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all named arguments (field and property values)
|
||||||
|
/// </summary>
|
||||||
|
IList<CANamedArgument> NamedArguments { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="NamedArguments"/> is not empty
|
||||||
|
/// </summary>
|
||||||
|
bool HasNamedArguments { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all <see cref="CANamedArgument"/>s that are field arguments
|
||||||
|
/// </summary>
|
||||||
|
IEnumerable<CANamedArgument> Fields { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all <see cref="CANamedArgument"/>s that are property arguments
|
||||||
|
/// </summary>
|
||||||
|
IEnumerable<CANamedArgument> Properties { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using dnlib.PE;
|
||||||
|
using dnlib.DotNet.Emit;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Interface to decrypt methods
|
||||||
|
/// </summary>
|
||||||
|
public interface IMethodDecrypter {
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the method's body
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rid"><c>Method</c> rid</param>
|
||||||
|
/// <param name="rva">The <see cref="RVA"/> found in the method's <c>Method</c> row</param>
|
||||||
|
/// <param name="parameters">The method's parameters</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
/// <param name="methodBody">Updated with the method's <see cref="MethodBody"/> if this
|
||||||
|
/// method returns <c>true</c></param>
|
||||||
|
/// <returns><c>true</c> if the method body was decrypted, <c>false</c> if the method isn't
|
||||||
|
/// encrypted and the default <see cref="MethodDef"/> body reader code should be used.</returns>
|
||||||
|
bool GetMethodBody(uint rid, RVA rva, IList<Parameter> parameters, GenericParamContext gpContext, out MethodBody methodBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interface to decrypt strings
|
||||||
|
/// </summary>
|
||||||
|
public interface IStringDecrypter {
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a string
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">String token</param>
|
||||||
|
/// <returns>A string or <c>null</c> if we should read it from the #US heap</returns>
|
||||||
|
string ReadUserString(uint token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,435 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using dnlib.DotNet.Writer;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="ILogger"/> events
|
||||||
|
/// </summary>
|
||||||
|
public enum LoggerEvent {
|
||||||
|
/// <summary>
|
||||||
|
/// An error was detected. An exception should normally be thrown but the error
|
||||||
|
/// can be ignored.
|
||||||
|
/// </summary>
|
||||||
|
Error,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Just a warning and can be ignored.
|
||||||
|
/// </summary>
|
||||||
|
Warning,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A normal message
|
||||||
|
/// </summary>
|
||||||
|
Info,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A verbose message
|
||||||
|
/// </summary>
|
||||||
|
Verbose,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A very verbose message
|
||||||
|
/// </summary>
|
||||||
|
VeryVerbose,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simple logger
|
||||||
|
/// </summary>
|
||||||
|
public interface ILogger {
|
||||||
|
/// <summary>
|
||||||
|
/// Log something
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">Caller or <c>null</c></param>
|
||||||
|
/// <param name="loggerEvent">Logger event</param>
|
||||||
|
/// <param name="format">Format</param>
|
||||||
|
/// <param name="args">Arguments</param>
|
||||||
|
void Log(object sender, LoggerEvent loggerEvent, string format, params object[] args);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if this event is ignored. If the event is ignored, the caller can
|
||||||
|
/// choose not to call <see cref="Log"/>. This is useful if it can take time to
|
||||||
|
/// prepare the message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="loggerEvent">The logger event</param>
|
||||||
|
bool IgnoresEvent(LoggerEvent loggerEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static partial class Extensions {
|
||||||
|
/// <summary>
|
||||||
|
/// Log an error message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
public static void Error(this ILogger logger, object sender, string message) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Error, "{0}", message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log an error message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
public static void Error(this ILogger logger, object sender, string message, object arg1) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Error, message, arg1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log an error message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
/// <param name="arg2">Message arg #2</param>
|
||||||
|
public static void Error(this ILogger logger, object sender, string message, object arg1, object arg2) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Error, message, arg1, arg2);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log an error message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
/// <param name="arg2">Message arg #2</param>
|
||||||
|
/// <param name="arg3">Message arg #3</param>
|
||||||
|
public static void Error(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Error, message, arg1, arg2, arg3);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log an error message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
/// <param name="arg2">Message arg #2</param>
|
||||||
|
/// <param name="arg3">Message arg #3</param>
|
||||||
|
/// <param name="arg4">Message arg #4</param>
|
||||||
|
public static void Error(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3, object arg4) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Error, message, arg1, arg2, arg3, arg4);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log an error message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="args">Message arguments</param>
|
||||||
|
public static void Error(this ILogger logger, object sender, string message, params object[] args) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Error, message, args);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a warning message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
public static void Warning(this ILogger logger, object sender, string message) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Warning, "{0}", message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a warning message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
public static void Warning(this ILogger logger, object sender, string message, object arg1) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Warning, message, arg1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a warning message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
/// <param name="arg2">Message arg #2</param>
|
||||||
|
public static void Warning(this ILogger logger, object sender, string message, object arg1, object arg2) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Warning, message, arg1, arg2);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a warning message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
/// <param name="arg2">Message arg #2</param>
|
||||||
|
/// <param name="arg3">Message arg #3</param>
|
||||||
|
public static void Warning(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3) => logger.Log(sender, LoggerEvent.Warning, message, arg1, arg2, arg3);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a warning message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
/// <param name="arg2">Message arg #2</param>
|
||||||
|
/// <param name="arg3">Message arg #3</param>
|
||||||
|
/// <param name="arg4">Message arg #4</param>
|
||||||
|
public static void Warning(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3, object arg4) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Warning, message, arg1, arg2, arg3, arg4);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a warning message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="args">Message arguments</param>
|
||||||
|
public static void Warning(this ILogger logger, object sender, string message, params object[] args) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Warning, message, args);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log an info message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
public static void Info(this ILogger logger, object sender, string message) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Info, "{0}", message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log an info message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
public static void Info(this ILogger logger, object sender, string message, object arg1) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Info, message, arg1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log an info message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
/// <param name="arg2">Message arg #2</param>
|
||||||
|
public static void Info(this ILogger logger, object sender, string message, object arg1, object arg2) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Info, message, arg1, arg2);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log an info message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
/// <param name="arg2">Message arg #2</param>
|
||||||
|
/// <param name="arg3">Message arg #3</param>
|
||||||
|
public static void Info(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Info, message, arg1, arg2, arg3);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log an info message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
/// <param name="arg2">Message arg #2</param>
|
||||||
|
/// <param name="arg3">Message arg #3</param>
|
||||||
|
/// <param name="arg4">Message arg #4</param>
|
||||||
|
public static void Info(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3, object arg4) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Info, message, arg1, arg2, arg3, arg4);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log an info message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="args">Message arguments</param>
|
||||||
|
public static void Info(this ILogger logger, object sender, string message, params object[] args) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Info, message, args);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a verbose message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
public static void Verbose(this ILogger logger, object sender, string message) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Verbose, "{0}", message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a verbose message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
public static void Verbose(this ILogger logger, object sender, string message, object arg1) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Verbose, message, arg1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a verbose message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
/// <param name="arg2">Message arg #2</param>
|
||||||
|
public static void Verbose(this ILogger logger, object sender, string message, object arg1, object arg2) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Verbose, message, arg1, arg2);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a verbose message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
/// <param name="arg2">Message arg #2</param>
|
||||||
|
/// <param name="arg3">Message arg #3</param>
|
||||||
|
public static void Verbose(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Verbose, message, arg1, arg2, arg3);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a verbose message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
/// <param name="arg2">Message arg #2</param>
|
||||||
|
/// <param name="arg3">Message arg #3</param>
|
||||||
|
/// <param name="arg4">Message arg #4</param>
|
||||||
|
public static void Verbose(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3, object arg4) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Verbose, message, arg1, arg2, arg3, arg4);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a verbose message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="args">Message arguments</param>
|
||||||
|
public static void Verbose(this ILogger logger, object sender, string message, params object[] args) =>
|
||||||
|
logger.Log(sender, LoggerEvent.Verbose, message, args);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a very verbose message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
public static void VeryVerbose(this ILogger logger, object sender, string message) =>
|
||||||
|
logger.Log(sender, LoggerEvent.VeryVerbose, "{0}", message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a very verbose message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
public static void VeryVerbose(this ILogger logger, object sender, string message, object arg1) =>
|
||||||
|
logger.Log(sender, LoggerEvent.VeryVerbose, message, arg1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a very verbose message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
/// <param name="arg2">Message arg #2</param>
|
||||||
|
public static void VeryVerbose(this ILogger logger, object sender, string message, object arg1, object arg2) =>
|
||||||
|
logger.Log(sender, LoggerEvent.VeryVerbose, message, arg1, arg2);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a very verbose message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
/// <param name="arg2">Message arg #2</param>
|
||||||
|
/// <param name="arg3">Message arg #3</param>
|
||||||
|
public static void VeryVerbose(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3) =>
|
||||||
|
logger.Log(sender, LoggerEvent.VeryVerbose, message, arg1, arg2, arg3);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a very verbose message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="arg1">Message arg #1</param>
|
||||||
|
/// <param name="arg2">Message arg #2</param>
|
||||||
|
/// <param name="arg3">Message arg #3</param>
|
||||||
|
/// <param name="arg4">Message arg #4</param>
|
||||||
|
public static void VeryVerbose(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3, object arg4) =>
|
||||||
|
logger.Log(sender, LoggerEvent.VeryVerbose, message, arg1, arg2, arg3, arg4);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a very verbose message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">this</param>
|
||||||
|
/// <param name="sender">Sender or <c>null</c></param>
|
||||||
|
/// <param name="message">Message</param>
|
||||||
|
/// <param name="args">Message arguments</param>
|
||||||
|
public static void VeryVerbose(this ILogger logger, object sender, string message, params object[] args) =>
|
||||||
|
logger.Log(sender, LoggerEvent.VeryVerbose, message, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dummy logger which ignores all messages, but can optionally throw on errors.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class DummyLogger : ILogger {
|
||||||
|
ConstructorInfo ctor;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// It ignores everything and doesn't throw anything.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly DummyLogger NoThrowInstance = new DummyLogger();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Throws a <see cref="ModuleWriterException"/> on errors, but ignores anything else.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly DummyLogger ThrowModuleWriterExceptionOnErrorInstance = new DummyLogger(typeof(ModuleWriterException));
|
||||||
|
|
||||||
|
DummyLogger() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="exceptionToThrow">If non-<c>null</c>, this exception type is thrown on
|
||||||
|
/// errors. It must have a public constructor that takes a <see cref="string"/> as the only
|
||||||
|
/// argument.</param>
|
||||||
|
public DummyLogger(Type exceptionToThrow) {
|
||||||
|
if (exceptionToThrow is not null) {
|
||||||
|
if (!exceptionToThrow.IsSubclassOf(typeof(Exception)))
|
||||||
|
throw new ArgumentException($"Not a System.Exception sub class: {exceptionToThrow.GetType()}");
|
||||||
|
ctor = exceptionToThrow.GetConstructor(new Type[] { typeof(string) });
|
||||||
|
if (ctor is null)
|
||||||
|
throw new ArgumentException($"Exception type {exceptionToThrow.GetType()} doesn't have a public constructor that takes a string as the only argument");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Log(object sender, LoggerEvent loggerEvent, string format, params object[] args) {
|
||||||
|
if (loggerEvent == LoggerEvent.Error && ctor is not null)
|
||||||
|
throw (Exception)ctor.Invoke(new object[] { string.Format(format, args) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool IgnoresEvent(LoggerEvent loggerEvent) {
|
||||||
|
if (ctor is null)
|
||||||
|
return true;
|
||||||
|
return loggerEvent != LoggerEvent.Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves types, methods, fields
|
||||||
|
/// </summary>
|
||||||
|
public interface IResolver : ITypeResolver, IMemberRefResolver {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves types
|
||||||
|
/// </summary>
|
||||||
|
public interface ITypeResolver {
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeRef">The type</param>
|
||||||
|
/// <param name="sourceModule">The module that needs to resolve the type or <c>null</c></param>
|
||||||
|
/// <returns>A <see cref="TypeDef"/> instance or <c>null</c> if it couldn't be resolved</returns>
|
||||||
|
TypeDef Resolve(TypeRef typeRef, ModuleDef sourceModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves fields and methods
|
||||||
|
/// </summary>
|
||||||
|
public interface IMemberRefResolver {
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a method or a field
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="memberRef">A method/field reference</param>
|
||||||
|
/// <returns>A <see cref="MethodDef"/> or a <see cref="FieldDef"/> instance or <c>null</c>
|
||||||
|
/// if it couldn't be resolved.</returns>
|
||||||
|
IMemberForwarded Resolve(MemberRef memberRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static partial class Extensions {
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="typeRef">The type</param>
|
||||||
|
/// <returns>A <see cref="TypeDef"/> instance or <c>null</c> if it couldn't be resolved</returns>
|
||||||
|
public static TypeDef Resolve(this ITypeResolver self, TypeRef typeRef) => self.Resolve(typeRef, null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="typeRef">The type</param>
|
||||||
|
/// <returns>A <see cref="TypeDef"/> instance</returns>
|
||||||
|
/// <exception cref="TypeResolveException">If the type couldn't be resolved</exception>
|
||||||
|
public static TypeDef ResolveThrow(this ITypeResolver self, TypeRef typeRef) => self.ResolveThrow(typeRef, null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="typeRef">The type</param>
|
||||||
|
/// <param name="sourceModule">The module that needs to resolve the type or <c>null</c></param>
|
||||||
|
/// <returns>A <see cref="TypeDef"/> instance</returns>
|
||||||
|
/// <exception cref="TypeResolveException">If the type couldn't be resolved</exception>
|
||||||
|
public static TypeDef ResolveThrow(this ITypeResolver self, TypeRef typeRef, ModuleDef sourceModule) {
|
||||||
|
var type = self.Resolve(typeRef, sourceModule);
|
||||||
|
if (type is not null)
|
||||||
|
return type;
|
||||||
|
throw new TypeResolveException($"Could not resolve type: {typeRef} ({typeRef?.DefinitionAssembly})");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a method or a field
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="memberRef">A method/field reference</param>
|
||||||
|
/// <returns>A <see cref="MethodDef"/> or a <see cref="FieldDef"/> instance</returns>
|
||||||
|
/// <exception cref="MemberRefResolveException">If the method/field couldn't be resolved</exception>
|
||||||
|
public static IMemberForwarded ResolveThrow(this IMemberRefResolver self, MemberRef memberRef) {
|
||||||
|
var memberDef = self.Resolve(memberRef);
|
||||||
|
if (memberDef is not null)
|
||||||
|
return memberDef;
|
||||||
|
throw new MemberRefResolveException($"Could not resolve method/field: {memberRef} ({memberRef?.GetDefinitionAssembly()})");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a field
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="memberRef">A field reference</param>
|
||||||
|
/// <returns>A <see cref="FieldDef"/> instance or <c>null</c> if it couldn't be resolved.</returns>
|
||||||
|
public static FieldDef ResolveField(this IMemberRefResolver self, MemberRef memberRef) => self.Resolve(memberRef) as FieldDef;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a field
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="memberRef">A field reference</param>
|
||||||
|
/// <returns>A <see cref="FieldDef"/> instance or <c>null</c> if it couldn't be resolved.</returns>
|
||||||
|
/// <exception cref="MemberRefResolveException">If the field couldn't be resolved</exception>
|
||||||
|
public static FieldDef ResolveFieldThrow(this IMemberRefResolver self, MemberRef memberRef) {
|
||||||
|
if (self.Resolve(memberRef) is FieldDef field)
|
||||||
|
return field;
|
||||||
|
throw new MemberRefResolveException($"Could not resolve field: {memberRef} ({memberRef?.GetDefinitionAssembly()})");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a method
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="memberRef">A method reference</param>
|
||||||
|
/// <returns>A <see cref="MethodDef"/> instance or <c>null</c> if it couldn't be resolved.</returns>
|
||||||
|
public static MethodDef ResolveMethod(this IMemberRefResolver self, MemberRef memberRef) => self.Resolve(memberRef) as MethodDef;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a method
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="memberRef">A method reference</param>
|
||||||
|
/// <returns>A <see cref="MethodDef"/> instance or <c>null</c> if it couldn't be resolved.</returns>
|
||||||
|
/// <exception cref="MemberRefResolveException">If the method couldn't be resolved</exception>
|
||||||
|
public static MethodDef ResolveMethodThrow(this IMemberRefResolver self, MemberRef memberRef) {
|
||||||
|
if (self.Resolve(memberRef) is MethodDef method)
|
||||||
|
return method;
|
||||||
|
throw new MemberRefResolveException($"Could not resolve method: {memberRef} ({memberRef?.GetDefinitionAssembly()})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves tokens
|
||||||
|
/// </summary>
|
||||||
|
public interface ITokenResolver {
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">The metadata token</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
/// <returns>A <see cref="IMDTokenProvider"/> or <c>null</c> if <paramref name="token"/> is invalid</returns>
|
||||||
|
IMDTokenProvider ResolveToken(uint token, GenericParamContext gpContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static partial class Extensions {
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">This</param>
|
||||||
|
/// <param name="token">The metadata token</param>
|
||||||
|
/// <returns>A <see cref="IMDTokenProvider"/> or <c>null</c> if <paramref name="token"/> is invalid</returns>
|
||||||
|
public static IMDTokenProvider ResolveToken(this ITokenResolver self, uint token) => self.ResolveToken(token, new GenericParamContext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Interface to get the full name of a type
|
||||||
|
/// </summary>
|
||||||
|
public interface IType : IFullName, IOwnerModule, ICodedToken, IGenericParameterProvider, IContainsGenericParameter {
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if it's a value type
|
||||||
|
/// </summary>
|
||||||
|
bool IsValueType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the name of this type
|
||||||
|
/// </summary>
|
||||||
|
string TypeName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the reflection name of this type
|
||||||
|
/// </summary>
|
||||||
|
string ReflectionName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the namespace of this type
|
||||||
|
/// </summary>
|
||||||
|
string Namespace { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the reflection namespace of this type
|
||||||
|
/// </summary>
|
||||||
|
string ReflectionNamespace { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the reflection name of this type. See also <see cref="AssemblyQualifiedName"/>.
|
||||||
|
/// </summary>
|
||||||
|
string ReflectionFullName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the reflection name of this type, and includes the assembly name where the
|
||||||
|
/// type is located. It can be passed to <see cref="System.Type.GetType(string)"/> to
|
||||||
|
/// load the type.
|
||||||
|
/// </summary>
|
||||||
|
string AssemblyQualifiedName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the assembly where this type is defined
|
||||||
|
/// </summary>
|
||||||
|
IAssembly DefinitionAssembly { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the scope, which is different from <see cref="DefinitionAssembly"/> since it
|
||||||
|
/// can differentiate between modules within the same assembly.
|
||||||
|
/// </summary>
|
||||||
|
IScope Scope { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type whose scope is returned by <see cref="Scope"/> and whose assembly
|
||||||
|
/// is returned by <see cref="DefinitionAssembly"/>. This is always a
|
||||||
|
/// <see cref="TypeDef"/>, <see cref="TypeRef"/> or <c>null</c>. It can also be a
|
||||||
|
/// nested <see cref="TypeRef"/>.
|
||||||
|
/// For example, if this type is a System.String&, then this value is a System.String.
|
||||||
|
/// If it's a generic instance type (eg. List<int>), then the generic type is
|
||||||
|
/// returned (eg. List<T>). In other words, the first <see cref="TypeDef"/> or
|
||||||
|
/// <see cref="TypeRef"/> that is found (without searching generic arguments) is returned.
|
||||||
|
/// </summary>
|
||||||
|
ITypeDefOrRef ScopeType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if it's an integer or a floating point type
|
||||||
|
/// </summary>
|
||||||
|
bool IsPrimitive { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implemented by types and calling convention signatures.
|
||||||
|
/// </summary>
|
||||||
|
public interface IContainsGenericParameter {
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if this contains a <see cref="GenericVar"/> or a <see cref="GenericMVar"/>.
|
||||||
|
/// </summary>
|
||||||
|
bool ContainsGenericParameter { get; }
|
||||||
|
}
|
||||||
|
interface IContainsGenericParameter2 {
|
||||||
|
bool ContainsGenericParameter { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static partial class Extensions {
|
||||||
|
/// <summary>
|
||||||
|
/// Returns <see cref="IType.ScopeType"/>, but if it's a nested <see cref="TypeRef"/>,
|
||||||
|
/// return the non-nested <see cref="TypeRef"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">this</param>
|
||||||
|
/// <returns>The scope type</returns>
|
||||||
|
public static ITypeDefOrRef GetNonNestedTypeRefScope(this IType type) {
|
||||||
|
if (type is null)
|
||||||
|
return null;
|
||||||
|
var scopeType = type.ScopeType;
|
||||||
|
var tr = scopeType as TypeRef;
|
||||||
|
if (tr is null)
|
||||||
|
return scopeType;
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
var dt = tr.ResolutionScope as TypeRef;
|
||||||
|
if (dt is null)
|
||||||
|
return tr;
|
||||||
|
tr = dt;
|
||||||
|
}
|
||||||
|
return tr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,140 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Finds <see cref="TypeDef"/>s
|
||||||
|
/// </summary>
|
||||||
|
public interface ITypeDefFinder {
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a <see cref="TypeDef"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fullName">Full name of the type (no assembly information)</param>
|
||||||
|
/// <param name="isReflectionName"><c>true</c> if it's a reflection name, and nested
|
||||||
|
/// type names are separated by a <c>+</c> character. If <c>false</c>, nested type names
|
||||||
|
/// are separated by a <c>/</c> character.</param>
|
||||||
|
/// <returns>An existing <see cref="TypeDef"/> or <c>null</c> if it wasn't found.</returns>
|
||||||
|
TypeDef Find(string fullName, bool isReflectionName);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a <see cref="TypeDef"/>. <paramref name="typeRef"/>'s scope (i.e., module or
|
||||||
|
/// assembly) is ignored when looking up the type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeRef">The type ref</param>
|
||||||
|
/// <returns>An existing <see cref="TypeDef"/> or <c>null</c> if it wasn't found.</returns>
|
||||||
|
TypeDef Find(TypeRef typeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static partial class Extensions {
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a <see cref="TypeDef"/>. Its scope (i.e., module or assembly) is ignored when
|
||||||
|
/// looking up the type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="typeRef">The type ref</param>
|
||||||
|
/// <returns>An existing <see cref="TypeDef"/> or <c>null</c> if it wasn't found.</returns>
|
||||||
|
/// <exception cref="TypeResolveException">If type couldn't be found</exception>
|
||||||
|
public static TypeDef FindThrow(this ITypeDefFinder self, TypeRef typeRef) {
|
||||||
|
var type = self.Find(typeRef);
|
||||||
|
if (type is not null)
|
||||||
|
return type;
|
||||||
|
throw new TypeResolveException($"Could not find type: {typeRef}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a <see cref="TypeDef"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="fullName">Full name of the type (no assembly information)</param>
|
||||||
|
/// <param name="isReflectionName"><c>true</c> if it's a reflection name, and nested
|
||||||
|
/// type names are separated by a <c>+</c> character. If <c>false</c>, nested type names
|
||||||
|
/// are separated by a <c>/</c> character.</param>
|
||||||
|
/// <returns>An existing <see cref="TypeDef"/></returns>
|
||||||
|
/// <exception cref="TypeResolveException">If type couldn't be found</exception>
|
||||||
|
public static TypeDef FindThrow(this ITypeDefFinder self, string fullName, bool isReflectionName) {
|
||||||
|
var type = self.Find(fullName, isReflectionName);
|
||||||
|
if (type is not null)
|
||||||
|
return type;
|
||||||
|
throw new TypeResolveException($"Could not find type: {fullName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a <see cref="TypeDef"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="fullName">Full name of the type (no assembly information). Nested types are separated by <c>/</c></param>
|
||||||
|
/// <returns>An existing <see cref="TypeDef"/> or <c>null</c> if it wasn't found.</returns>
|
||||||
|
public static TypeDef FindNormal(this ITypeDefFinder self, string fullName) => self.Find(fullName, false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a <see cref="TypeDef"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="fullName">Full name of the type (no assembly information). Nested types are separated by <c>/</c></param>
|
||||||
|
/// <returns>An existing <see cref="TypeDef"/></returns>
|
||||||
|
/// <exception cref="TypeResolveException">If type couldn't be found</exception>
|
||||||
|
public static TypeDef FindNormalThrow(this ITypeDefFinder self, string fullName) {
|
||||||
|
var type = self.Find(fullName, false);
|
||||||
|
if (type is not null)
|
||||||
|
return type;
|
||||||
|
throw new TypeResolveException($"Could not find type: {fullName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a <see cref="TypeDef"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="fullName">Full name of the type (no assembly information). Nested types are separated by <c>+</c></param>
|
||||||
|
/// <returns>An existing <see cref="TypeDef"/> or <c>null</c> if it wasn't found.</returns>
|
||||||
|
public static TypeDef FindReflection(this ITypeDefFinder self, string fullName) => self.Find(fullName, true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a <see cref="TypeDef"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="fullName">Full name of the type (no assembly information). Nested types are separated by <c>+</c></param>
|
||||||
|
/// <returns>An existing <see cref="TypeDef"/></returns>
|
||||||
|
/// <exception cref="TypeResolveException">If type couldn't be found</exception>
|
||||||
|
public static TypeDef FindReflectionThrow(this ITypeDefFinder self, string fullName) {
|
||||||
|
var type = self.Find(fullName, true);
|
||||||
|
if (type is not null)
|
||||||
|
return type;
|
||||||
|
throw new TypeResolveException($"Could not find type: {fullName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether a <see cref="TypeDef"/> exists. <paramref name="typeRef"/>'s scope (i.e.,
|
||||||
|
/// module or assembly) is ignored when looking up the type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="typeRef">The type ref</param>
|
||||||
|
/// <returns><c>true</c> if the <see cref="TypeDef"/> exists, <c>false</c> otherwise</returns>
|
||||||
|
public static bool TypeExists(this ITypeDefFinder self, TypeRef typeRef) => self.Find(typeRef) is not null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether a <see cref="TypeDef"/> exists
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="fullName">Full name of the type (no assembly information)</param>
|
||||||
|
/// <param name="isReflectionName"><c>true</c> if it's a reflection name, and nested
|
||||||
|
/// type names are separated by a <c>+</c> character. If <c>false</c>, nested type names
|
||||||
|
/// are separated by a <c>/</c> character.</param>
|
||||||
|
/// <returns><c>true</c> if the <see cref="TypeDef"/> exists, <c>false</c> otherwise</returns>
|
||||||
|
public static bool TypeExists(this ITypeDefFinder self, string fullName, bool isReflectionName) => self.Find(fullName, isReflectionName) is not null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether a <see cref="TypeDef"/> exists
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="fullName">Full name of the type (no assembly information). Nested types are separated by <c>/</c></param>
|
||||||
|
/// <returns><c>true</c> if the <see cref="TypeDef"/> exists, <c>false</c> otherwise</returns>
|
||||||
|
public static bool TypeExistsNormal(this ITypeDefFinder self, string fullName) => self.Find(fullName, false) is not null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether a <see cref="TypeDef"/> exists
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">this</param>
|
||||||
|
/// <param name="fullName">Full name of the type (no assembly information). Nested types are separated by <c>+</c></param>
|
||||||
|
/// <returns><c>true</c> if the <see cref="TypeDef"/> exists, <c>false</c> otherwise</returns>
|
||||||
|
public static bool TypeExistsReflection(this ITypeDefFinder self, string fullName) => self.Find(fullName, true) is not null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// Interface to access a local or a parameter
|
||||||
|
/// </summary>
|
||||||
|
public interface IVariable {
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the variable type
|
||||||
|
/// </summary>
|
||||||
|
TypeSig Type { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the 0-based position
|
||||||
|
/// </summary>
|
||||||
|
int Index { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the variable name
|
||||||
|
/// </summary>
|
||||||
|
string Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,301 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using dnlib.DotNet.MD;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// A high-level representation of a row in the ImplMap table
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("{Module} {Name}")]
|
||||||
|
public abstract class ImplMap : IMDTokenProvider {
|
||||||
|
/// <summary>
|
||||||
|
/// The row id in its table
|
||||||
|
/// </summary>
|
||||||
|
protected uint rid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public MDToken MDToken => new MDToken(Table.ImplMap, rid);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint Rid {
|
||||||
|
get => rid;
|
||||||
|
set => rid = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column ImplMap.MappingFlags
|
||||||
|
/// </summary>
|
||||||
|
public PInvokeAttributes Attributes {
|
||||||
|
get => (PInvokeAttributes)attributes;
|
||||||
|
set => attributes = (int)value;
|
||||||
|
}
|
||||||
|
/// <summary>Attributes</summary>
|
||||||
|
protected int attributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column ImplMap.ImportName
|
||||||
|
/// </summary>
|
||||||
|
public UTF8String Name {
|
||||||
|
get => name;
|
||||||
|
set => name = value;
|
||||||
|
}
|
||||||
|
/// <summary>Name</summary>
|
||||||
|
protected UTF8String name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column ImplMap.ImportScope
|
||||||
|
/// </summary>
|
||||||
|
public ModuleRef Module {
|
||||||
|
get => module;
|
||||||
|
set => module = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected ModuleRef module;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Modify <see cref="attributes"/> property: <see cref="attributes"/> =
|
||||||
|
/// (<see cref="attributes"/> & <paramref name="andMask"/>) | <paramref name="orMask"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="andMask">Value to <c>AND</c></param>
|
||||||
|
/// <param name="orMask">Value to OR</param>
|
||||||
|
void ModifyAttributes(PInvokeAttributes andMask, PInvokeAttributes orMask) =>
|
||||||
|
attributes = (attributes & (int)andMask) | (int)orMask;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set or clear flags in <see cref="attributes"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="set"><c>true</c> if flags should be set, <c>false</c> if flags should
|
||||||
|
/// be cleared</param>
|
||||||
|
/// <param name="flags">Flags to set or clear</param>
|
||||||
|
void ModifyAttributes(bool set, PInvokeAttributes flags) {
|
||||||
|
if (set)
|
||||||
|
attributes |= (int)flags;
|
||||||
|
else
|
||||||
|
attributes &= ~(int)flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="PInvokeAttributes.NoMangle"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool IsNoMangle {
|
||||||
|
get => ((PInvokeAttributes)attributes & PInvokeAttributes.NoMangle) != 0;
|
||||||
|
set => ModifyAttributes(value, PInvokeAttributes.NoMangle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the char set
|
||||||
|
/// </summary>
|
||||||
|
public PInvokeAttributes CharSet {
|
||||||
|
get => (PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask;
|
||||||
|
set => ModifyAttributes(~PInvokeAttributes.CharSetMask, value & PInvokeAttributes.CharSetMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="PInvokeAttributes.CharSetNotSpec"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCharSetNotSpec => ((PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask) == PInvokeAttributes.CharSetNotSpec;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="PInvokeAttributes.CharSetAnsi"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCharSetAnsi => ((PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask) == PInvokeAttributes.CharSetAnsi;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="PInvokeAttributes.CharSetUnicode"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCharSetUnicode => ((PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask) == PInvokeAttributes.CharSetUnicode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="PInvokeAttributes.CharSetAuto"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCharSetAuto => ((PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask) == PInvokeAttributes.CharSetAuto;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets best fit
|
||||||
|
/// </summary>
|
||||||
|
public PInvokeAttributes BestFit {
|
||||||
|
get => (PInvokeAttributes)attributes & PInvokeAttributes.BestFitMask;
|
||||||
|
set => ModifyAttributes(~PInvokeAttributes.BestFitMask, value & PInvokeAttributes.BestFitMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="PInvokeAttributes.BestFitUseAssem"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsBestFitUseAssem => ((PInvokeAttributes)attributes & PInvokeAttributes.BestFitMask) == PInvokeAttributes.BestFitUseAssem;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="PInvokeAttributes.BestFitEnabled"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsBestFitEnabled => ((PInvokeAttributes)attributes & PInvokeAttributes.BestFitMask) == PInvokeAttributes.BestFitEnabled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="PInvokeAttributes.BestFitDisabled"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsBestFitDisabled => ((PInvokeAttributes)attributes & PInvokeAttributes.BestFitMask) == PInvokeAttributes.BestFitDisabled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets throw on unmappable char
|
||||||
|
/// </summary>
|
||||||
|
public PInvokeAttributes ThrowOnUnmappableChar {
|
||||||
|
get => (PInvokeAttributes)attributes & PInvokeAttributes.ThrowOnUnmappableCharMask;
|
||||||
|
set => ModifyAttributes(~PInvokeAttributes.ThrowOnUnmappableCharMask, value & PInvokeAttributes.ThrowOnUnmappableCharMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="PInvokeAttributes.ThrowOnUnmappableCharUseAssem"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsThrowOnUnmappableCharUseAssem => ((PInvokeAttributes)attributes & PInvokeAttributes.ThrowOnUnmappableCharMask) == PInvokeAttributes.ThrowOnUnmappableCharUseAssem;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="PInvokeAttributes.ThrowOnUnmappableCharEnabled"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsThrowOnUnmappableCharEnabled => ((PInvokeAttributes)attributes & PInvokeAttributes.ThrowOnUnmappableCharMask) == PInvokeAttributes.ThrowOnUnmappableCharEnabled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="PInvokeAttributes.ThrowOnUnmappableCharDisabled"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsThrowOnUnmappableCharDisabled => ((PInvokeAttributes)attributes & PInvokeAttributes.ThrowOnUnmappableCharMask) == PInvokeAttributes.ThrowOnUnmappableCharDisabled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the <see cref="PInvokeAttributes.SupportsLastError"/> bit
|
||||||
|
/// </summary>
|
||||||
|
public bool SupportsLastError {
|
||||||
|
get => ((PInvokeAttributes)attributes & PInvokeAttributes.SupportsLastError) != 0;
|
||||||
|
set => ModifyAttributes(value, PInvokeAttributes.SupportsLastError);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets calling convention
|
||||||
|
/// </summary>
|
||||||
|
public PInvokeAttributes CallConv {
|
||||||
|
get => (PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask;
|
||||||
|
set => ModifyAttributes(~PInvokeAttributes.CallConvMask, value & PInvokeAttributes.CallConvMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="PInvokeAttributes.CallConvWinapi"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCallConvWinapi => ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvWinapi;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="PInvokeAttributes.CallConvCdecl"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCallConvCdecl => ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvCdecl;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="PInvokeAttributes.CallConvStdcall"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCallConvStdcall => ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvStdcall;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="PInvokeAttributes.CallConvThiscall"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCallConvThiscall => ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvThiscall;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if <see cref="PInvokeAttributes.CallConvFastcall"/> is set
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCallConvFastcall => ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvFastcall;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether this <see cref="ImplMap"/> is a certain P/Invoke method
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dllName">Name of the DLL</param>
|
||||||
|
/// <param name="funcName">Name of the function within the DLL</param>
|
||||||
|
/// <returns><c>true</c> if it's the specified P/Invoke method, else <c>false</c></returns>
|
||||||
|
public bool IsPinvokeMethod(string dllName, string funcName) => IsPinvokeMethod(dllName, funcName, IsWindows());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether this <see cref="ImplMap"/> is a certain P/Invoke method
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dllName">Name of the DLL</param>
|
||||||
|
/// <param name="funcName">Name of the function within the DLL</param>
|
||||||
|
/// <param name="treatAsWindows">Treat as Windows</param>
|
||||||
|
/// <returns><c>true</c> if it's the specified P/Invoke method, else <c>false</c></returns>
|
||||||
|
public bool IsPinvokeMethod(string dllName, string funcName, bool treatAsWindows) {
|
||||||
|
if (name != funcName)
|
||||||
|
return false;
|
||||||
|
var mod = module;
|
||||||
|
if (mod is null)
|
||||||
|
return false;
|
||||||
|
return GetDllName(dllName, treatAsWindows).Equals(GetDllName(mod.Name, treatAsWindows), StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
static string GetDllName(string dllName, bool treatAsWindows) {
|
||||||
|
if (treatAsWindows)
|
||||||
|
dllName = dllName.TrimEnd(trimChars);
|
||||||
|
if (dllName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return dllName.Substring(0, dllName.Length - 4);
|
||||||
|
return dllName;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsWindows() =>
|
||||||
|
#if NETSTANDARD || NETCOREAPP
|
||||||
|
RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||||
|
#else
|
||||||
|
Path.DirectorySeparatorChar == '\\' || Path.AltDirectorySeparatorChar == '\\';
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static readonly char[] trimChars = { ' ' };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An ImplMap row created by the user and not present in the original .NET file
|
||||||
|
/// </summary>
|
||||||
|
public class ImplMapUser : ImplMap {
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public ImplMapUser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scope">Scope</param>
|
||||||
|
/// <param name="name">Name</param>
|
||||||
|
/// <param name="flags">Flags</param>
|
||||||
|
public ImplMapUser(ModuleRef scope, UTF8String name, PInvokeAttributes flags) {
|
||||||
|
module = scope;
|
||||||
|
this.name = name;
|
||||||
|
attributes = (int)flags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Created from a row in the ImplMap table
|
||||||
|
/// </summary>
|
||||||
|
sealed class ImplMapMD : ImplMap, IMDTokenProviderMD {
|
||||||
|
readonly uint origRid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint OrigRid => origRid;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readerModule">The module which contains this <c>ImplMap</c> row</param>
|
||||||
|
/// <param name="rid">Row ID</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If <paramref name="readerModule"/> is <c>null</c></exception>
|
||||||
|
/// <exception cref="ArgumentException">If <paramref name="rid"/> is invalid</exception>
|
||||||
|
public ImplMapMD(ModuleDefMD readerModule, uint rid) {
|
||||||
|
#if DEBUG
|
||||||
|
if (readerModule is null)
|
||||||
|
throw new ArgumentNullException("readerModule");
|
||||||
|
if (readerModule.TablesStream.ImplMapTable.IsInvalidRID(rid))
|
||||||
|
throw new BadImageFormatException($"ImplMap rid {rid} does not exist");
|
||||||
|
#endif
|
||||||
|
origRid = rid;
|
||||||
|
this.rid = rid;
|
||||||
|
bool b = readerModule.TablesStream.TryReadImplMapRow(origRid, out var row);
|
||||||
|
Debug.Assert(b);
|
||||||
|
attributes = row.MappingFlags;
|
||||||
|
name = readerModule.StringsStream.ReadNoNull(row.ImportName);
|
||||||
|
module = readerModule.ResolveModuleRef(row.ImportScope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,157 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
using dnlib.DotNet.MD;
|
||||||
|
using dnlib.DotNet.Pdb;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet {
|
||||||
|
/// <summary>
|
||||||
|
/// A high-level representation of a row in the InterfaceImpl table
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("{Interface}")]
|
||||||
|
public abstract class InterfaceImpl : IHasCustomAttribute, IContainsGenericParameter, IHasCustomDebugInformation {
|
||||||
|
/// <summary>
|
||||||
|
/// The row id in its table
|
||||||
|
/// </summary>
|
||||||
|
protected uint rid;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public MDToken MDToken => new MDToken(Table.InterfaceImpl, rid);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint Rid {
|
||||||
|
get => rid;
|
||||||
|
set => rid = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomAttributeTag => 5;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// From column InterfaceImpl.Interface
|
||||||
|
/// </summary>
|
||||||
|
public ITypeDefOrRef Interface {
|
||||||
|
get => @interface;
|
||||||
|
set => @interface = value;
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected ITypeDefOrRef @interface;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom attributes
|
||||||
|
/// </summary>
|
||||||
|
public CustomAttributeCollection CustomAttributes {
|
||||||
|
get {
|
||||||
|
if (customAttributes is null)
|
||||||
|
InitializeCustomAttributes();
|
||||||
|
return customAttributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected CustomAttributeCollection customAttributes;
|
||||||
|
/// <summary>Initializes <see cref="customAttributes"/></summary>
|
||||||
|
protected virtual void InitializeCustomAttributes() =>
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomAttributes => CustomAttributes.Count > 0;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int HasCustomDebugInformationTag => 5;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all custom debug infos
|
||||||
|
/// </summary>
|
||||||
|
public IList<PdbCustomDebugInfo> CustomDebugInfos {
|
||||||
|
get {
|
||||||
|
if (customDebugInfos is null)
|
||||||
|
InitializeCustomDebugInfos();
|
||||||
|
return customDebugInfos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary/>
|
||||||
|
protected IList<PdbCustomDebugInfo> customDebugInfos;
|
||||||
|
/// <summary>Initializes <see cref="customDebugInfos"/></summary>
|
||||||
|
protected virtual void InitializeCustomDebugInfos() =>
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, new List<PdbCustomDebugInfo>(), null);
|
||||||
|
|
||||||
|
bool IContainsGenericParameter.ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An InterfaceImpl row created by the user and not present in the original .NET file
|
||||||
|
/// </summary>
|
||||||
|
public class InterfaceImplUser : InterfaceImpl {
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public InterfaceImplUser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="interface">The interface the type implements</param>
|
||||||
|
public InterfaceImplUser(ITypeDefOrRef @interface) => this.@interface = @interface;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Created from a row in the InterfaceImpl table
|
||||||
|
/// </summary>
|
||||||
|
sealed class InterfaceImplMD : InterfaceImpl, IMDTokenProviderMD, IContainsGenericParameter2 {
|
||||||
|
/// <summary>The module where this instance is located</summary>
|
||||||
|
readonly ModuleDefMD readerModule;
|
||||||
|
|
||||||
|
readonly uint origRid;
|
||||||
|
readonly GenericParamContext gpContext;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint OrigRid => origRid;
|
||||||
|
|
||||||
|
bool IContainsGenericParameter2.ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomAttributes() {
|
||||||
|
var list = readerModule.Metadata.GetCustomAttributeRidList(Table.InterfaceImpl, origRid);
|
||||||
|
var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index]));
|
||||||
|
Interlocked.CompareExchange(ref customAttributes, tmp, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeCustomDebugInfos() {
|
||||||
|
var list = new List<PdbCustomDebugInfo>();
|
||||||
|
readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), gpContext, list);
|
||||||
|
Interlocked.CompareExchange(ref customDebugInfos, list, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readerModule">The module which contains this <c>InterfaceImpl</c> row</param>
|
||||||
|
/// <param name="rid">Row ID</param>
|
||||||
|
/// <param name="gpContext">Generic parameter context</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If <paramref name="readerModule"/> is <c>null</c></exception>
|
||||||
|
/// <exception cref="ArgumentException">If <paramref name="rid"/> is invalid</exception>
|
||||||
|
public InterfaceImplMD(ModuleDefMD readerModule, uint rid, GenericParamContext gpContext) {
|
||||||
|
#if DEBUG
|
||||||
|
if (readerModule is null)
|
||||||
|
throw new ArgumentNullException("readerModule");
|
||||||
|
if (readerModule.TablesStream.InterfaceImplTable.IsInvalidRID(rid))
|
||||||
|
throw new BadImageFormatException($"InterfaceImpl rid {rid} does not exist");
|
||||||
|
#endif
|
||||||
|
origRid = rid;
|
||||||
|
this.rid = rid;
|
||||||
|
this.readerModule = readerModule;
|
||||||
|
this.gpContext = gpContext;
|
||||||
|
bool b = readerModule.TablesStream.TryReadInterfaceImplRow(origRid, out var row);
|
||||||
|
Debug.Assert(b);
|
||||||
|
@interface = readerModule.ResolveTypeDefOrRef(row.Interface, gpContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using dnlib.IO;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the #Blob stream
|
||||||
|
/// </summary>
|
||||||
|
public sealed class BlobStream : HeapStream {
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public BlobStream() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public BlobStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader)
|
||||||
|
: base(mdReaderFactory, metadataBaseOffset, streamHeader) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">Offset of data</param>
|
||||||
|
/// <returns>The data or <c>null</c> if invalid offset</returns>
|
||||||
|
public byte[] Read(uint offset) {
|
||||||
|
// The CLR has a special check for offset 0. It always interprets it as
|
||||||
|
// 0-length data, even if that first byte isn't 0 at all.
|
||||||
|
if (offset == 0)
|
||||||
|
return Array2.Empty<byte>();
|
||||||
|
if (!TryCreateReader(offset, out var reader))
|
||||||
|
return null;
|
||||||
|
return reader.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads data just like <see cref="Read"/>, but returns an empty array if
|
||||||
|
/// offset is invalid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">Offset of data</param>
|
||||||
|
/// <returns>The data</returns>
|
||||||
|
public byte[] ReadNoNull(uint offset) => Read(offset) ?? Array2.Empty<byte>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a reader that can access a blob
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">Offset of blob</param>
|
||||||
|
/// <returns>A new stream</returns>
|
||||||
|
public DataReader CreateReader(uint offset) {
|
||||||
|
if (TryCreateReader(offset, out var reader))
|
||||||
|
return reader;
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a reader that can access a blob or returns false on failure
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">Offset of blob</param>
|
||||||
|
/// <param name="reader">Updated with the reader</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool TryCreateReader(uint offset, out DataReader reader) {
|
||||||
|
reader = dataReader;
|
||||||
|
if (!IsValidOffset(offset))
|
||||||
|
return false;
|
||||||
|
reader.Position = offset;
|
||||||
|
if (!reader.TryReadCompressedUInt32(out uint length))
|
||||||
|
return false;
|
||||||
|
if (!reader.CanRead(length))
|
||||||
|
return false;
|
||||||
|
reader = reader.Slice(reader.Position, length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,214 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Contains all possible coded token classes
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CodedToken {
|
||||||
|
/// <summary>TypeDefOrRef coded token</summary>
|
||||||
|
public static readonly CodedToken TypeDefOrRef = new CodedToken(2, new Table[3] {
|
||||||
|
Table.TypeDef, Table.TypeRef, Table.TypeSpec,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>HasConstant coded token</summary>
|
||||||
|
public static readonly CodedToken HasConstant = new CodedToken(2, new Table[3] {
|
||||||
|
Table.Field, Table.Param, Table.Property,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>HasCustomAttribute coded token</summary>
|
||||||
|
public static readonly CodedToken HasCustomAttribute = new CodedToken(5, new Table[24] {
|
||||||
|
Table.Method, Table.Field, Table.TypeRef, Table.TypeDef,
|
||||||
|
Table.Param, Table.InterfaceImpl, Table.MemberRef, Table.Module,
|
||||||
|
Table.DeclSecurity, Table.Property, Table.Event, Table.StandAloneSig,
|
||||||
|
Table.ModuleRef, Table.TypeSpec, Table.Assembly, Table.AssemblyRef,
|
||||||
|
Table.File, Table.ExportedType, Table.ManifestResource, Table.GenericParam,
|
||||||
|
Table.GenericParamConstraint, Table.MethodSpec, 0, 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>HasFieldMarshal coded token</summary>
|
||||||
|
public static readonly CodedToken HasFieldMarshal = new CodedToken(1, new Table[2] {
|
||||||
|
Table.Field, Table.Param,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>HasDeclSecurity coded token</summary>
|
||||||
|
public static readonly CodedToken HasDeclSecurity = new CodedToken(2, new Table[3] {
|
||||||
|
Table.TypeDef, Table.Method, Table.Assembly,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>MemberRefParent coded token</summary>
|
||||||
|
public static readonly CodedToken MemberRefParent = new CodedToken(3, new Table[5] {
|
||||||
|
Table.TypeDef, Table.TypeRef, Table.ModuleRef, Table.Method,
|
||||||
|
Table.TypeSpec,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>HasSemantic coded token</summary>
|
||||||
|
public static readonly CodedToken HasSemantic = new CodedToken(1, new Table[2] {
|
||||||
|
Table.Event, Table.Property,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>MethodDefOrRef coded token</summary>
|
||||||
|
public static readonly CodedToken MethodDefOrRef = new CodedToken(1, new Table[2] {
|
||||||
|
Table.Method, Table.MemberRef,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>MemberForwarded coded token</summary>
|
||||||
|
public static readonly CodedToken MemberForwarded = new CodedToken(1, new Table[2] {
|
||||||
|
Table.Field, Table.Method,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>Implementation coded token</summary>
|
||||||
|
public static readonly CodedToken Implementation = new CodedToken(2, new Table[3] {
|
||||||
|
Table.File, Table.AssemblyRef, Table.ExportedType,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>CustomAttributeType coded token</summary>
|
||||||
|
public static readonly CodedToken CustomAttributeType = new CodedToken(3, new Table[5] {
|
||||||
|
0, 0, Table.Method, Table.MemberRef, 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>ResolutionScope coded token</summary>
|
||||||
|
public static readonly CodedToken ResolutionScope = new CodedToken(2, new Table[4] {
|
||||||
|
Table.Module, Table.ModuleRef, Table.AssemblyRef, Table.TypeRef,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>TypeOrMethodDef coded token</summary>
|
||||||
|
public static readonly CodedToken TypeOrMethodDef = new CodedToken(1, new Table[2] {
|
||||||
|
Table.TypeDef, Table.Method,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>HasCustomDebugInformation coded token</summary>
|
||||||
|
public static readonly CodedToken HasCustomDebugInformation = new CodedToken(5, new Table[27] {
|
||||||
|
Table.Method, Table.Field, Table.TypeRef, Table.TypeDef,
|
||||||
|
Table.Param, Table.InterfaceImpl, Table.MemberRef, Table.Module,
|
||||||
|
Table.DeclSecurity, Table.Property, Table.Event, Table.StandAloneSig,
|
||||||
|
Table.ModuleRef, Table.TypeSpec, Table.Assembly, Table.AssemblyRef,
|
||||||
|
Table.File, Table.ExportedType, Table.ManifestResource, Table.GenericParam,
|
||||||
|
Table.GenericParamConstraint, Table.MethodSpec, Table.Document, Table.LocalScope,
|
||||||
|
Table.LocalVariable, Table.LocalConstant, Table.ImportScope,
|
||||||
|
});
|
||||||
|
|
||||||
|
readonly Table[] tableTypes;
|
||||||
|
readonly int bits;
|
||||||
|
readonly int mask;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns all types of tables
|
||||||
|
/// </summary>
|
||||||
|
public Table[] TableTypes => tableTypes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the number of bits that is used to encode table type
|
||||||
|
/// </summary>
|
||||||
|
public int Bits => bits;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bits">Number of bits used to encode token type</param>
|
||||||
|
/// <param name="tableTypes">All table types</param>
|
||||||
|
internal CodedToken(int bits, Table[] tableTypes) {
|
||||||
|
this.bits = bits;
|
||||||
|
mask = (1 << bits) - 1;
|
||||||
|
this.tableTypes = tableTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes a token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">The token</param>
|
||||||
|
/// <returns>Coded token</returns>
|
||||||
|
/// <seealso cref="Encode(MDToken,out uint)"/>
|
||||||
|
public uint Encode(MDToken token) => Encode(token.Raw);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes a token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">The token</param>
|
||||||
|
/// <returns>Coded token</returns>
|
||||||
|
/// <seealso cref="Encode(uint,out uint)"/>
|
||||||
|
public uint Encode(uint token) {
|
||||||
|
Encode(token, out uint codedToken);
|
||||||
|
return codedToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes a token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">The token</param>
|
||||||
|
/// <param name="codedToken">Coded token</param>
|
||||||
|
/// <returns><c>true</c> if successful</returns>
|
||||||
|
public bool Encode(MDToken token, out uint codedToken) => Encode(token.Raw, out codedToken);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes a token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">The token</param>
|
||||||
|
/// <param name="codedToken">Coded token</param>
|
||||||
|
/// <returns><c>true</c> if successful</returns>
|
||||||
|
public bool Encode(uint token, out uint codedToken) {
|
||||||
|
int index = Array.IndexOf(tableTypes, MDToken.ToTable(token));
|
||||||
|
if (index < 0) {
|
||||||
|
codedToken = uint.MaxValue;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// This shift can never overflow a uint since bits < 8 (it's at most 5), and
|
||||||
|
// ToRid() returns an integer <= 0x00FFFFFF.
|
||||||
|
codedToken = (MDToken.ToRID(token) << bits) | (uint)index;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a coded token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="codedToken">The coded token</param>
|
||||||
|
/// <returns>Decoded token or 0 on failure</returns>
|
||||||
|
/// <seealso cref="Decode(uint,out MDToken)"/>
|
||||||
|
public MDToken Decode2(uint codedToken) {
|
||||||
|
Decode(codedToken, out uint token);
|
||||||
|
return new MDToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a coded token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="codedToken">The coded token</param>
|
||||||
|
/// <returns>Decoded token or 0 on failure</returns>
|
||||||
|
/// <seealso cref="Decode(uint,out uint)"/>
|
||||||
|
public uint Decode(uint codedToken) {
|
||||||
|
Decode(codedToken, out uint token);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a coded token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="codedToken">The coded token</param>
|
||||||
|
/// <param name="token">Decoded token</param>
|
||||||
|
/// <returns><c>true</c> if successful</returns>
|
||||||
|
public bool Decode(uint codedToken, out MDToken token) {
|
||||||
|
bool result = Decode(codedToken, out uint decodedToken);
|
||||||
|
token = new MDToken(decodedToken);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a coded token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="codedToken">The coded token</param>
|
||||||
|
/// <param name="token">Decoded token</param>
|
||||||
|
/// <returns><c>true</c> if successful</returns>
|
||||||
|
public bool Decode(uint codedToken, out uint token) {
|
||||||
|
uint rid = codedToken >> bits;
|
||||||
|
int index = (int)(codedToken & mask);
|
||||||
|
if (rid > MDToken.RID_MAX || index >= tableTypes.Length) {
|
||||||
|
token = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = ((uint)tableTypes[index] << MDToken.TABLE_SHIFT) | rid;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using dnlib.DotNet.Writer;
|
||||||
|
using dnlib.IO;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Info about one column in a MD table
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("{offset} {size} {name}")]
|
||||||
|
public sealed class ColumnInfo {
|
||||||
|
readonly byte index;
|
||||||
|
byte offset;
|
||||||
|
readonly ColumnSize columnSize;
|
||||||
|
byte size;
|
||||||
|
readonly string name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the column index
|
||||||
|
/// </summary>
|
||||||
|
public int Index => index;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the column offset within the table row
|
||||||
|
/// </summary>
|
||||||
|
public int Offset {
|
||||||
|
get => offset;
|
||||||
|
internal set => offset = (byte)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the column size
|
||||||
|
/// </summary>
|
||||||
|
public int Size {
|
||||||
|
get => size;
|
||||||
|
internal set => size = (byte)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the column name
|
||||||
|
/// </summary>
|
||||||
|
public string Name => name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the ColumnSize enum value
|
||||||
|
/// </summary>
|
||||||
|
public ColumnSize ColumnSize => columnSize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">Column index</param>
|
||||||
|
/// <param name="name">The column name</param>
|
||||||
|
/// <param name="columnSize">Column size</param>
|
||||||
|
public ColumnInfo(byte index, string name, ColumnSize columnSize) {
|
||||||
|
this.index = index;
|
||||||
|
this.name = name;
|
||||||
|
this.columnSize = columnSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">Column index</param>
|
||||||
|
/// <param name="name">The column name</param>
|
||||||
|
/// <param name="columnSize">Column size</param>
|
||||||
|
/// <param name="offset">Offset of column</param>
|
||||||
|
/// <param name="size">Size of column</param>
|
||||||
|
public ColumnInfo(byte index, string name, ColumnSize columnSize, byte offset, byte size) {
|
||||||
|
this.index = index;
|
||||||
|
this.name = name;
|
||||||
|
this.columnSize = columnSize;
|
||||||
|
this.offset = offset;
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the column
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">A reader positioned on this column</param>
|
||||||
|
/// <returns>The column value</returns>
|
||||||
|
public uint Read(ref DataReader reader) =>
|
||||||
|
size switch {
|
||||||
|
1 => reader.ReadByte(),
|
||||||
|
2 => reader.ReadUInt16(),
|
||||||
|
4 => reader.ReadUInt32(),
|
||||||
|
_ => throw new InvalidOperationException("Invalid column size"),
|
||||||
|
};
|
||||||
|
|
||||||
|
internal uint Unsafe_Read24(ref DataReader reader) {
|
||||||
|
Debug.Assert(size == 2 || size == 4);
|
||||||
|
return size == 2 ? reader.Unsafe_ReadUInt16() : reader.Unsafe_ReadUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a column
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer">The writer position on this column</param>
|
||||||
|
/// <param name="value">The column value</param>
|
||||||
|
public void Write(DataWriter writer, uint value) {
|
||||||
|
switch (size) {
|
||||||
|
case 1: writer.WriteByte((byte)value); break;
|
||||||
|
case 2: writer.WriteUInt16((ushort)value); break;
|
||||||
|
case 4: writer.WriteUInt32(value); break;
|
||||||
|
default: throw new InvalidOperationException("Invalid column size");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Write24(DataWriter writer, uint value) {
|
||||||
|
Debug.Assert(size == 2 || size == 4);
|
||||||
|
if (size == 2)
|
||||||
|
writer.WriteUInt16((ushort)value);
|
||||||
|
else
|
||||||
|
writer.WriteUInt32(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,159 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// MD table column size
|
||||||
|
/// </summary>
|
||||||
|
public enum ColumnSize : byte {
|
||||||
|
/// <summary>RID into Module table</summary>
|
||||||
|
Module,
|
||||||
|
/// <summary>RID into TypeRef table</summary>
|
||||||
|
TypeRef,
|
||||||
|
/// <summary>RID into TypeDef table</summary>
|
||||||
|
TypeDef,
|
||||||
|
/// <summary>RID into FieldPtr table</summary>
|
||||||
|
FieldPtr,
|
||||||
|
/// <summary>RID into Field table</summary>
|
||||||
|
Field,
|
||||||
|
/// <summary>RID into MethodPtr table</summary>
|
||||||
|
MethodPtr,
|
||||||
|
/// <summary>RID into Method table</summary>
|
||||||
|
Method,
|
||||||
|
/// <summary>RID into ParamPtr table</summary>
|
||||||
|
ParamPtr,
|
||||||
|
/// <summary>RID into Param table</summary>
|
||||||
|
Param,
|
||||||
|
/// <summary>RID into InterfaceImpl table</summary>
|
||||||
|
InterfaceImpl,
|
||||||
|
/// <summary>RID into MemberRef table</summary>
|
||||||
|
MemberRef,
|
||||||
|
/// <summary>RID into Constant table</summary>
|
||||||
|
Constant,
|
||||||
|
/// <summary>RID into CustomAttribute table</summary>
|
||||||
|
CustomAttribute,
|
||||||
|
/// <summary>RID into FieldMarshal table</summary>
|
||||||
|
FieldMarshal,
|
||||||
|
/// <summary>RID into DeclSecurity table</summary>
|
||||||
|
DeclSecurity,
|
||||||
|
/// <summary>RID into ClassLayout table</summary>
|
||||||
|
ClassLayout,
|
||||||
|
/// <summary>RID into FieldLayout table</summary>
|
||||||
|
FieldLayout,
|
||||||
|
/// <summary>RID into StandAloneSig table</summary>
|
||||||
|
StandAloneSig,
|
||||||
|
/// <summary>RID into EventMap table</summary>
|
||||||
|
EventMap,
|
||||||
|
/// <summary>RID into EventPtr table</summary>
|
||||||
|
EventPtr,
|
||||||
|
/// <summary>RID into Event table</summary>
|
||||||
|
Event,
|
||||||
|
/// <summary>RID into PropertyMap table</summary>
|
||||||
|
PropertyMap,
|
||||||
|
/// <summary>RID into PropertyPtr table</summary>
|
||||||
|
PropertyPtr,
|
||||||
|
/// <summary>RID into Property table</summary>
|
||||||
|
Property,
|
||||||
|
/// <summary>RID into MethodSemantics table</summary>
|
||||||
|
MethodSemantics,
|
||||||
|
/// <summary>RID into MethodImpl table</summary>
|
||||||
|
MethodImpl,
|
||||||
|
/// <summary>RID into ModuleRef table</summary>
|
||||||
|
ModuleRef,
|
||||||
|
/// <summary>RID into TypeSpec table</summary>
|
||||||
|
TypeSpec,
|
||||||
|
/// <summary>RID into ImplMap table</summary>
|
||||||
|
ImplMap,
|
||||||
|
/// <summary>RID into FieldRVA table</summary>
|
||||||
|
FieldRVA,
|
||||||
|
/// <summary>RID into ENCLog table</summary>
|
||||||
|
ENCLog,
|
||||||
|
/// <summary>RID into ENCMap table</summary>
|
||||||
|
ENCMap,
|
||||||
|
/// <summary>RID into Assembly table</summary>
|
||||||
|
Assembly,
|
||||||
|
/// <summary>RID into AssemblyProcessor table</summary>
|
||||||
|
AssemblyProcessor,
|
||||||
|
/// <summary>RID into AssemblyOS table</summary>
|
||||||
|
AssemblyOS,
|
||||||
|
/// <summary>RID into AssemblyRef table</summary>
|
||||||
|
AssemblyRef,
|
||||||
|
/// <summary>RID into AssemblyRefProcessor table</summary>
|
||||||
|
AssemblyRefProcessor,
|
||||||
|
/// <summary>RID into AssemblyRefOS table</summary>
|
||||||
|
AssemblyRefOS,
|
||||||
|
/// <summary>RID into File table</summary>
|
||||||
|
File,
|
||||||
|
/// <summary>RID into ExportedType table</summary>
|
||||||
|
ExportedType,
|
||||||
|
/// <summary>RID into ManifestResource table</summary>
|
||||||
|
ManifestResource,
|
||||||
|
/// <summary>RID into NestedClass table</summary>
|
||||||
|
NestedClass,
|
||||||
|
/// <summary>RID into GenericParam table</summary>
|
||||||
|
GenericParam,
|
||||||
|
/// <summary>RID into MethodSpec table</summary>
|
||||||
|
MethodSpec,
|
||||||
|
/// <summary>RID into GenericParamConstraint table</summary>
|
||||||
|
GenericParamConstraint,
|
||||||
|
/// <summary>RID into Document table</summary>
|
||||||
|
Document = 0x30,
|
||||||
|
/// <summary>RID into MethodDebugInformation table</summary>
|
||||||
|
MethodDebugInformation,
|
||||||
|
/// <summary>RID into LocalScope table</summary>
|
||||||
|
LocalScope,
|
||||||
|
/// <summary>RID into LocalVariable table</summary>
|
||||||
|
LocalVariable,
|
||||||
|
/// <summary>RID into LocalConstant table</summary>
|
||||||
|
LocalConstant,
|
||||||
|
/// <summary>RID into ImportScope table</summary>
|
||||||
|
ImportScope,
|
||||||
|
/// <summary>RID into StateMachineMethod table</summary>
|
||||||
|
StateMachineMethod,
|
||||||
|
/// <summary>RID into CustomDebugInformation table</summary>
|
||||||
|
CustomDebugInformation,
|
||||||
|
/// <summary>8-bit byte</summary>
|
||||||
|
Byte = 0x40,
|
||||||
|
/// <summary>16-bit signed int</summary>
|
||||||
|
Int16,
|
||||||
|
/// <summary>16-bit unsigned int</summary>
|
||||||
|
UInt16,
|
||||||
|
/// <summary>32-bit signed int</summary>
|
||||||
|
Int32,
|
||||||
|
/// <summary>32-bit unsigned int</summary>
|
||||||
|
UInt32,
|
||||||
|
/// <summary>Index into #Strings stream</summary>
|
||||||
|
Strings,
|
||||||
|
/// <summary>Index into #GUID stream</summary>
|
||||||
|
GUID,
|
||||||
|
/// <summary>Index into #Blob stream</summary>
|
||||||
|
Blob,
|
||||||
|
/// <summary>TypeDefOrRef encoded token</summary>
|
||||||
|
TypeDefOrRef,
|
||||||
|
/// <summary>HasConstant encoded token</summary>
|
||||||
|
HasConstant,
|
||||||
|
/// <summary>HasCustomAttribute encoded token</summary>
|
||||||
|
HasCustomAttribute,
|
||||||
|
/// <summary>HasFieldMarshal encoded token</summary>
|
||||||
|
HasFieldMarshal,
|
||||||
|
/// <summary>HasDeclSecurity encoded token</summary>
|
||||||
|
HasDeclSecurity,
|
||||||
|
/// <summary>MemberRefParent encoded token</summary>
|
||||||
|
MemberRefParent,
|
||||||
|
/// <summary>HasSemantic encoded token</summary>
|
||||||
|
HasSemantic,
|
||||||
|
/// <summary>MethodDefOrRef encoded token</summary>
|
||||||
|
MethodDefOrRef,
|
||||||
|
/// <summary>MemberForwarded encoded token</summary>
|
||||||
|
MemberForwarded,
|
||||||
|
/// <summary>Implementation encoded token</summary>
|
||||||
|
Implementation,
|
||||||
|
/// <summary>CustomAttributeType encoded token</summary>
|
||||||
|
CustomAttributeType,
|
||||||
|
/// <summary>ResolutionScope encoded token</summary>
|
||||||
|
ResolutionScope,
|
||||||
|
/// <summary>TypeOrMethodDef encoded token</summary>
|
||||||
|
TypeOrMethodDef,
|
||||||
|
/// <summary>HasCustomDebugInformation encoded token</summary>
|
||||||
|
HasCustomDebugInformation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// See COMIMAGE_FLAGS_XXX in CorHdr.h in the Windows SDK
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum ComImageFlags : uint {
|
||||||
|
/// <summary>
|
||||||
|
/// See COMIMAGE_FLAGS_ILONLY in the Windows SDK
|
||||||
|
/// </summary>
|
||||||
|
ILOnly = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See COMIMAGE_FLAGS_32BITREQUIRED in the Windows SDK
|
||||||
|
/// </summary>
|
||||||
|
Bit32Required = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set if a native header exists (COMIMAGE_FLAGS_IL_LIBRARY)
|
||||||
|
/// </summary>
|
||||||
|
ILLibrary = 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See COMIMAGE_FLAGS_STRONGNAMESIGNED in the Windows SDK
|
||||||
|
/// </summary>
|
||||||
|
StrongNameSigned = 8,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See COMIMAGE_FLAGS_NATIVE_ENTRYPOINT in the Windows SDK
|
||||||
|
/// </summary>
|
||||||
|
NativeEntryPoint = 0x10,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See COMIMAGE_FLAGS_TRACKDEBUGDATA in the Windows SDK
|
||||||
|
/// </summary>
|
||||||
|
TrackDebugData = 0x10000,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See COMIMAGE_FLAGS_32BITPREFERRED in the Windows SDK
|
||||||
|
/// </summary>
|
||||||
|
Bit32Preferred = 0x20000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,178 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using dnlib.IO;
|
||||||
|
using dnlib.PE;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Used when a #~ stream is present in the metadata
|
||||||
|
/// </summary>
|
||||||
|
sealed class CompressedMetadata : MetadataBase {
|
||||||
|
readonly CLRRuntimeReaderKind runtime;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool IsCompressed => true;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public CompressedMetadata(IPEImage peImage, ImageCor20Header cor20Header, MetadataHeader mdHeader, CLRRuntimeReaderKind runtime)
|
||||||
|
: base(peImage, cor20Header, mdHeader) {
|
||||||
|
this.runtime = runtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
internal CompressedMetadata(MetadataHeader mdHeader, bool isStandalonePortablePdb, CLRRuntimeReaderKind runtime)
|
||||||
|
: base(mdHeader, isStandalonePortablePdb) {
|
||||||
|
this.runtime = runtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeInternal(DataReaderFactory mdReaderFactory, uint metadataBaseOffset) {
|
||||||
|
DotNetStream dns = null;
|
||||||
|
var newAllStreams = new List<DotNetStream>(allStreams);
|
||||||
|
bool forceAllBig = false;
|
||||||
|
try {
|
||||||
|
for (int i = mdHeader.StreamHeaders.Count - 1; i >= 0; i--) {
|
||||||
|
var sh = mdHeader.StreamHeaders[i];
|
||||||
|
switch (sh.Name) {
|
||||||
|
case "#Strings":
|
||||||
|
if (stringsStream is null) {
|
||||||
|
stringsStream = new StringsStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
newAllStreams.Add(stringsStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#US":
|
||||||
|
if (usStream is null) {
|
||||||
|
usStream = new USStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
newAllStreams.Add(usStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#Blob":
|
||||||
|
if (blobStream is null) {
|
||||||
|
blobStream = new BlobStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
newAllStreams.Add(blobStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#GUID":
|
||||||
|
if (guidStream is null) {
|
||||||
|
guidStream = new GuidStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
newAllStreams.Add(guidStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#~":
|
||||||
|
if (tablesStream is null) {
|
||||||
|
tablesStream = new TablesStream(mdReaderFactory, metadataBaseOffset, sh, runtime);
|
||||||
|
newAllStreams.Add(tablesStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#Pdb":
|
||||||
|
if (isStandalonePortablePdb && pdbStream is null) {
|
||||||
|
pdbStream = new PdbStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
newAllStreams.Add(pdbStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#JTD":
|
||||||
|
if (runtime == CLRRuntimeReaderKind.Mono) {
|
||||||
|
forceAllBig = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dns = new CustomDotNetStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
newAllStreams.Add(dns);
|
||||||
|
dns = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
dns?.Dispose();
|
||||||
|
newAllStreams.Reverse();
|
||||||
|
allStreams = newAllStreams;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tablesStream is null)
|
||||||
|
throw new BadImageFormatException("Missing MD stream");
|
||||||
|
|
||||||
|
if (pdbStream is not null)
|
||||||
|
tablesStream.Initialize(pdbStream.TypeSystemTableRows, forceAllBig);
|
||||||
|
else
|
||||||
|
tablesStream.Initialize(null, forceAllBig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override RidList GetFieldRidList(uint typeDefRid) => GetRidList(tablesStream.TypeDefTable, typeDefRid, 4, tablesStream.FieldTable);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override RidList GetMethodRidList(uint typeDefRid) => GetRidList(tablesStream.TypeDefTable, typeDefRid, 5, tablesStream.MethodTable);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override RidList GetParamRidList(uint methodRid) => GetRidList(tablesStream.MethodTable, methodRid, 5, tablesStream.ParamTable);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override RidList GetEventRidList(uint eventMapRid) => GetRidList(tablesStream.EventMapTable, eventMapRid, 1, tablesStream.EventTable);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override RidList GetPropertyRidList(uint propertyMapRid) => GetRidList(tablesStream.PropertyMapTable, propertyMapRid, 1, tablesStream.PropertyTable);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override RidList GetLocalVariableRidList(uint localScopeRid) => GetRidList(tablesStream.LocalScopeTable, localScopeRid, 2, tablesStream.LocalVariableTable);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override RidList GetLocalConstantRidList(uint localScopeRid) => GetRidList(tablesStream.LocalScopeTable, localScopeRid, 3, tablesStream.LocalConstantTable);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a rid list (eg. field list)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tableSource">Source table, eg. <c>TypeDef</c></param>
|
||||||
|
/// <param name="tableSourceRid">Row ID in <paramref name="tableSource"/></param>
|
||||||
|
/// <param name="colIndex">Column index in <paramref name="tableSource"/>, eg. 4 for <c>TypeDef.FieldList</c></param>
|
||||||
|
/// <param name="tableDest">Destination table, eg. <c>Field</c></param>
|
||||||
|
/// <returns>A new <see cref="RidList"/> instance</returns>
|
||||||
|
RidList GetRidList(MDTable tableSource, uint tableSourceRid, int colIndex, MDTable tableDest) {
|
||||||
|
var column = tableSource.TableInfo.Columns[colIndex];
|
||||||
|
if (!tablesStream.TryReadColumn24(tableSource, tableSourceRid, column, out uint startRid))
|
||||||
|
return RidList.Empty;
|
||||||
|
bool hasNext = tablesStream.TryReadColumn24(tableSource, tableSourceRid + 1, column, out uint nextListRid);
|
||||||
|
uint lastRid = tableDest.Rows + 1;
|
||||||
|
if (startRid == 0 || startRid >= lastRid)
|
||||||
|
return RidList.Empty;
|
||||||
|
uint endRid = !hasNext || (nextListRid == 0 && tableSourceRid + 1 == tableSource.Rows && tableDest.Rows == 0xFFFF) ? lastRid : nextListRid;
|
||||||
|
if (endRid < startRid)
|
||||||
|
endRid = startRid;
|
||||||
|
if (endRid > lastRid)
|
||||||
|
endRid = lastRid;
|
||||||
|
return RidList.Create(startRid, endRid - startRid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override uint BinarySearch(MDTable tableSource, int keyColIndex, uint key) {
|
||||||
|
var keyColumn = tableSource.TableInfo.Columns[keyColIndex];
|
||||||
|
uint ridLo = 1, ridHi = tableSource.Rows;
|
||||||
|
while (ridLo <= ridHi) {
|
||||||
|
uint rid = (ridLo + ridHi) / 2;
|
||||||
|
if (!tablesStream.TryReadColumn24(tableSource, rid, keyColumn, out uint key2))
|
||||||
|
break; // Never happens since rid is valid
|
||||||
|
if (key == key2)
|
||||||
|
return rid;
|
||||||
|
if (key2 > key)
|
||||||
|
ridHi = rid - 1;
|
||||||
|
else
|
||||||
|
ridLo = rid + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using dnlib.IO;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// A custom .NET metadata stream
|
||||||
|
/// </summary>
|
||||||
|
public class CustomDotNetStream : DotNetStream {
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
public CustomDotNetStream() { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mdReaderFactory">Data reader factory</param>
|
||||||
|
/// <param name="metadataBaseOffset">Offset of metadata</param>
|
||||||
|
/// <param name="streamHeader">The stream header</param>
|
||||||
|
public CustomDotNetStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader)
|
||||||
|
: base(mdReaderFactory, metadataBaseOffset, streamHeader) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using dnlib.IO;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// .NET metadata stream
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("{dataReader.Length} {streamHeader.Name}")]
|
||||||
|
public abstract class DotNetStream : IFileSection, IDisposable {
|
||||||
|
/// <summary>
|
||||||
|
/// Reader that can access the whole stream.
|
||||||
|
///
|
||||||
|
/// NOTE: Always copy this field to a local variable before using it since it must be thread safe.
|
||||||
|
/// </summary>
|
||||||
|
protected DataReader dataReader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>null</c> if it wasn't present in the file
|
||||||
|
/// </summary>
|
||||||
|
StreamHeader streamHeader;
|
||||||
|
|
||||||
|
DataReaderFactory mdReaderFactory;
|
||||||
|
uint metadataBaseOffset;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public FileOffset StartOffset => (FileOffset)dataReader.StartOffset;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public FileOffset EndOffset => (FileOffset)dataReader.EndOffset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the length of this stream in the metadata
|
||||||
|
/// </summary>
|
||||||
|
public uint StreamLength => dataReader.Length;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the stream header
|
||||||
|
/// </summary>
|
||||||
|
public StreamHeader StreamHeader => streamHeader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the stream
|
||||||
|
/// </summary>
|
||||||
|
public string Name => streamHeader is null ? string.Empty : streamHeader.Name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a data reader that can read the full stream
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public DataReader CreateReader() => dataReader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
protected DotNetStream() {
|
||||||
|
streamHeader = null;
|
||||||
|
dataReader = default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mdReaderFactory">Data reader factory</param>
|
||||||
|
/// <param name="metadataBaseOffset">Offset of metadata</param>
|
||||||
|
/// <param name="streamHeader">The stream header</param>
|
||||||
|
protected DotNetStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader) {
|
||||||
|
this.mdReaderFactory = mdReaderFactory;
|
||||||
|
mdReaderFactory.DataReaderInvalidated += DataReaderFactory_DataReaderInvalidated;
|
||||||
|
this.mdReaderFactory = mdReaderFactory;
|
||||||
|
this.metadataBaseOffset = metadataBaseOffset;
|
||||||
|
this.streamHeader = streamHeader;
|
||||||
|
RecreateReader(mdReaderFactory, metadataBaseOffset, streamHeader, notifyThisClass: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataReaderFactory_DataReaderInvalidated(object sender, EventArgs e) => RecreateReader(mdReaderFactory, metadataBaseOffset, streamHeader, notifyThisClass: true);
|
||||||
|
|
||||||
|
void RecreateReader(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader, bool notifyThisClass) {
|
||||||
|
if (mdReaderFactory is null || streamHeader is null)
|
||||||
|
dataReader = default;
|
||||||
|
else
|
||||||
|
dataReader = mdReaderFactory.CreateReader(metadataBaseOffset + streamHeader.Offset, streamHeader.StreamSize);
|
||||||
|
if (notifyThisClass)
|
||||||
|
OnReaderRecreated();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called after <see cref="dataReader"/> gets recreated
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnReaderRecreated() { }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Dispose() {
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose method
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing"><c>true</c> if called by <see cref="Dispose()"/></param>
|
||||||
|
protected virtual void Dispose(bool disposing) {
|
||||||
|
if (disposing) {
|
||||||
|
var mdReaderFactory = this.mdReaderFactory;
|
||||||
|
if (mdReaderFactory is not null)
|
||||||
|
mdReaderFactory.DataReaderInvalidated -= DataReaderFactory_DataReaderInvalidated;
|
||||||
|
streamHeader = null;
|
||||||
|
this.mdReaderFactory = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether an index is valid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">The index</param>
|
||||||
|
/// <returns><c>true</c> if the index is valid</returns>
|
||||||
|
public virtual bool IsValidIndex(uint index) => IsValidOffset(index);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check whether an offset is within the stream
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">Stream offset</param>
|
||||||
|
/// <returns><c>true</c> if the offset is valid</returns>
|
||||||
|
public bool IsValidOffset(uint offset) => offset == 0 || offset < dataReader.Length;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check whether an offset is within the stream
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">Stream offset</param>
|
||||||
|
/// <param name="size">Size of data</param>
|
||||||
|
/// <returns><c>true</c> if the offset is valid</returns>
|
||||||
|
public bool IsValidOffset(uint offset, int size) {
|
||||||
|
if (size == 0)
|
||||||
|
return IsValidOffset(offset);
|
||||||
|
return size > 0 && (ulong)offset + (uint)size <= dataReader.Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base class of #US, #Strings, #Blob, and #GUID classes
|
||||||
|
/// </summary>
|
||||||
|
public abstract class HeapStream : DotNetStream {
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected HeapStream() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected HeapStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader)
|
||||||
|
: base(mdReaderFactory, metadataBaseOffset, streamHeader) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,408 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes .NET table row sizes
|
||||||
|
/// </summary>
|
||||||
|
public sealed class DotNetTableSizes {
|
||||||
|
bool bigStrings;
|
||||||
|
bool bigGuid;
|
||||||
|
bool bigBlob;
|
||||||
|
bool forceAllBig;
|
||||||
|
TableInfo[] tableInfos;
|
||||||
|
|
||||||
|
internal static bool IsSystemTable(Table table) => table < Table.Document;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the table sizes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bigStrings"><c>true</c> if #Strings size >= 0x10000</param>
|
||||||
|
/// <param name="bigGuid"><c>true</c> if #GUID size >= 0x10000</param>
|
||||||
|
/// <param name="bigBlob"><c>true</c> if #Blob size >= 0x10000</param>
|
||||||
|
/// <param name="systemRowCounts">Count of rows in each table</param>
|
||||||
|
/// <param name="debugRowCounts">Count of rows in each table (debug tables)</param>
|
||||||
|
public void InitializeSizes(bool bigStrings, bool bigGuid, bool bigBlob, IList<uint> systemRowCounts, IList<uint> debugRowCounts) =>
|
||||||
|
InitializeSizes(bigStrings, bigGuid, bigBlob, systemRowCounts, debugRowCounts, false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the table sizes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bigStrings"><c>true</c> if #Strings size >= 0x10000</param>
|
||||||
|
/// <param name="bigGuid"><c>true</c> if #GUID size >= 0x10000</param>
|
||||||
|
/// <param name="bigBlob"><c>true</c> if #Blob size >= 0x10000</param>
|
||||||
|
/// <param name="systemRowCounts">Count of rows in each table</param>
|
||||||
|
/// <param name="debugRowCounts">Count of rows in each table (debug tables)</param>
|
||||||
|
/// <param name="forceAllBig">Force all columns to 4 bytes instead of 2 or 4 bytes</param>
|
||||||
|
internal void InitializeSizes(bool bigStrings, bool bigGuid, bool bigBlob, IList<uint> systemRowCounts, IList<uint> debugRowCounts, bool forceAllBig) {
|
||||||
|
this.bigStrings = bigStrings || forceAllBig;
|
||||||
|
this.bigGuid = bigGuid || forceAllBig;
|
||||||
|
this.bigBlob = bigBlob || forceAllBig;
|
||||||
|
this.forceAllBig = forceAllBig;
|
||||||
|
foreach (var tableInfo in tableInfos) {
|
||||||
|
var rowCounts = IsSystemTable(tableInfo.Table) ? systemRowCounts : debugRowCounts;
|
||||||
|
int colOffset = 0;
|
||||||
|
foreach (var colInfo in tableInfo.Columns) {
|
||||||
|
colInfo.Offset = colOffset;
|
||||||
|
var colSize = GetSize(colInfo.ColumnSize, rowCounts);
|
||||||
|
colInfo.Size = colSize;
|
||||||
|
colOffset += colSize;
|
||||||
|
}
|
||||||
|
tableInfo.RowSize = colOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetSize(ColumnSize columnSize, IList<uint> rowCounts) {
|
||||||
|
if (ColumnSize.Module <= columnSize && columnSize <= ColumnSize.CustomDebugInformation) {
|
||||||
|
int table = (int)(columnSize - ColumnSize.Module);
|
||||||
|
uint count = table >= rowCounts.Count ? 0 : rowCounts[table];
|
||||||
|
return forceAllBig || count > 0xFFFF ? 4 : 2;
|
||||||
|
}
|
||||||
|
else if (ColumnSize.TypeDefOrRef <= columnSize && columnSize <= ColumnSize.HasCustomDebugInformation) {
|
||||||
|
var info = columnSize switch {
|
||||||
|
ColumnSize.TypeDefOrRef => CodedToken.TypeDefOrRef,
|
||||||
|
ColumnSize.HasConstant => CodedToken.HasConstant,
|
||||||
|
ColumnSize.HasCustomAttribute => CodedToken.HasCustomAttribute,
|
||||||
|
ColumnSize.HasFieldMarshal => CodedToken.HasFieldMarshal,
|
||||||
|
ColumnSize.HasDeclSecurity => CodedToken.HasDeclSecurity,
|
||||||
|
ColumnSize.MemberRefParent => CodedToken.MemberRefParent,
|
||||||
|
ColumnSize.HasSemantic => CodedToken.HasSemantic,
|
||||||
|
ColumnSize.MethodDefOrRef => CodedToken.MethodDefOrRef,
|
||||||
|
ColumnSize.MemberForwarded => CodedToken.MemberForwarded,
|
||||||
|
ColumnSize.Implementation => CodedToken.Implementation,
|
||||||
|
ColumnSize.CustomAttributeType => CodedToken.CustomAttributeType,
|
||||||
|
ColumnSize.ResolutionScope => CodedToken.ResolutionScope,
|
||||||
|
ColumnSize.TypeOrMethodDef => CodedToken.TypeOrMethodDef,
|
||||||
|
ColumnSize.HasCustomDebugInformation => CodedToken.HasCustomDebugInformation,
|
||||||
|
_ => throw new InvalidOperationException($"Invalid ColumnSize: {columnSize}"),
|
||||||
|
};
|
||||||
|
uint maxRows = 0;
|
||||||
|
foreach (var tableType in info.TableTypes) {
|
||||||
|
int index = (int)tableType;
|
||||||
|
var tableRows = index >= rowCounts.Count ? 0 : rowCounts[index];
|
||||||
|
if (tableRows > maxRows)
|
||||||
|
maxRows = tableRows;
|
||||||
|
}
|
||||||
|
// Can't overflow since maxRows <= 0x00FFFFFF and info.Bits < 8
|
||||||
|
uint finalRows = maxRows << info.Bits;
|
||||||
|
return forceAllBig || finalRows > 0xFFFF ? 4 : 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch (columnSize) {
|
||||||
|
case ColumnSize.Byte: return 1;
|
||||||
|
case ColumnSize.Int16: return 2;
|
||||||
|
case ColumnSize.UInt16: return 2;
|
||||||
|
case ColumnSize.Int32: return 4;
|
||||||
|
case ColumnSize.UInt32: return 4;
|
||||||
|
case ColumnSize.Strings:return forceAllBig || bigStrings ? 4 : 2;
|
||||||
|
case ColumnSize.GUID: return forceAllBig || bigGuid ? 4 : 2;
|
||||||
|
case ColumnSize.Blob: return forceAllBig || bigBlob ? 4 : 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new InvalidOperationException($"Invalid ColumnSize: {columnSize}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the table infos
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="majorVersion">Major table version</param>
|
||||||
|
/// <param name="minorVersion">Minor table version</param>
|
||||||
|
/// <returns>All table infos (not completely initialized)</returns>
|
||||||
|
public TableInfo[] CreateTables(byte majorVersion, byte minorVersion) =>
|
||||||
|
CreateTables(majorVersion, minorVersion, out int maxPresentTables);
|
||||||
|
|
||||||
|
internal const int normalMaxTables = (int)Table.CustomDebugInformation + 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the table infos
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="majorVersion">Major table version</param>
|
||||||
|
/// <param name="minorVersion">Minor table version</param>
|
||||||
|
/// <param name="maxPresentTables">Initialized to max present tables (eg. 42 or 45)</param>
|
||||||
|
/// <returns>All table infos (not completely initialized)</returns>
|
||||||
|
public TableInfo[] CreateTables(byte majorVersion, byte minorVersion, out int maxPresentTables) {
|
||||||
|
maxPresentTables = (majorVersion == 1 && minorVersion == 0) ? (int)Table.NestedClass + 1 : normalMaxTables;
|
||||||
|
|
||||||
|
var tableInfos = new TableInfo[normalMaxTables];
|
||||||
|
|
||||||
|
tableInfos[(int)Table.Module] = new TableInfo(Table.Module, "Module", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Generation", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(1, "Name", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(2, "Mvid", ColumnSize.GUID),
|
||||||
|
new ColumnInfo(3, "EncId", ColumnSize.GUID),
|
||||||
|
new ColumnInfo(4, "EncBaseId", ColumnSize.GUID),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.TypeRef] = new TableInfo(Table.TypeRef, "TypeRef", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "ResolutionScope", ColumnSize.ResolutionScope),
|
||||||
|
new ColumnInfo(1, "Name", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(2, "Namespace", ColumnSize.Strings),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.TypeDef] = new TableInfo(Table.TypeDef, "TypeDef", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Flags", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(1, "Name", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(2, "Namespace", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(3, "Extends", ColumnSize.TypeDefOrRef),
|
||||||
|
new ColumnInfo(4, "FieldList", ColumnSize.Field),
|
||||||
|
new ColumnInfo(5, "MethodList", ColumnSize.Method),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.FieldPtr] = new TableInfo(Table.FieldPtr, "FieldPtr", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Field", ColumnSize.Field),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.Field] = new TableInfo(Table.Field, "Field", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Flags", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(1, "Name", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(2, "Signature", ColumnSize.Blob),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.MethodPtr] = new TableInfo(Table.MethodPtr, "MethodPtr", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Method", ColumnSize.Method),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.Method] = new TableInfo(Table.Method, "Method", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "RVA", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(1, "ImplFlags", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(2, "Flags", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(3, "Name", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(4, "Signature", ColumnSize.Blob),
|
||||||
|
new ColumnInfo(5, "ParamList", ColumnSize.Param),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.ParamPtr] = new TableInfo(Table.ParamPtr, "ParamPtr", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Param", ColumnSize.Param),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.Param] = new TableInfo(Table.Param, "Param", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Flags", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(1, "Sequence", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(2, "Name", ColumnSize.Strings),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.InterfaceImpl] = new TableInfo(Table.InterfaceImpl, "InterfaceImpl", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Class", ColumnSize.TypeDef),
|
||||||
|
new ColumnInfo(1, "Interface", ColumnSize.TypeDefOrRef),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.MemberRef] = new TableInfo(Table.MemberRef, "MemberRef", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Class", ColumnSize.MemberRefParent),
|
||||||
|
new ColumnInfo(1, "Name", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(2, "Signature", ColumnSize.Blob),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.Constant] = new TableInfo(Table.Constant, "Constant", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Type", ColumnSize.Byte),
|
||||||
|
new ColumnInfo(1, "Padding", ColumnSize.Byte),
|
||||||
|
new ColumnInfo(2, "Parent", ColumnSize.HasConstant),
|
||||||
|
new ColumnInfo(3, "Value", ColumnSize.Blob),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.CustomAttribute] = new TableInfo(Table.CustomAttribute, "CustomAttribute", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Parent", ColumnSize.HasCustomAttribute),
|
||||||
|
new ColumnInfo(1, "Type", ColumnSize.CustomAttributeType),
|
||||||
|
new ColumnInfo(2, "Value", ColumnSize.Blob),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.FieldMarshal] = new TableInfo(Table.FieldMarshal, "FieldMarshal", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Parent", ColumnSize.HasFieldMarshal),
|
||||||
|
new ColumnInfo(1, "NativeType", ColumnSize.Blob),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.DeclSecurity] = new TableInfo(Table.DeclSecurity, "DeclSecurity", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Action", ColumnSize.Int16),
|
||||||
|
new ColumnInfo(1, "Parent", ColumnSize.HasDeclSecurity),
|
||||||
|
new ColumnInfo(2, "PermissionSet", ColumnSize.Blob),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.ClassLayout] = new TableInfo(Table.ClassLayout, "ClassLayout", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "PackingSize", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(1, "ClassSize", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(2, "Parent", ColumnSize.TypeDef),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.FieldLayout] = new TableInfo(Table.FieldLayout, "FieldLayout", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "OffSet", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(1, "Field", ColumnSize.Field),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.StandAloneSig] = new TableInfo(Table.StandAloneSig, "StandAloneSig", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Signature", ColumnSize.Blob),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.EventMap] = new TableInfo(Table.EventMap, "EventMap", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Parent", ColumnSize.TypeDef),
|
||||||
|
new ColumnInfo(1, "EventList", ColumnSize.Event),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.EventPtr] = new TableInfo(Table.EventPtr, "EventPtr", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Event", ColumnSize.Event),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.Event] = new TableInfo(Table.Event, "Event", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "EventFlags", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(1, "Name", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(2, "EventType", ColumnSize.TypeDefOrRef),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.PropertyMap] = new TableInfo(Table.PropertyMap, "PropertyMap", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Parent", ColumnSize.TypeDef),
|
||||||
|
new ColumnInfo(1, "PropertyList", ColumnSize.Property),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.PropertyPtr] = new TableInfo(Table.PropertyPtr, "PropertyPtr", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Property", ColumnSize.Property),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.Property] = new TableInfo(Table.Property, "Property", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "PropFlags", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(1, "Name", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(2, "Type", ColumnSize.Blob),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.MethodSemantics] = new TableInfo(Table.MethodSemantics, "MethodSemantics", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Semantic", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(1, "Method", ColumnSize.Method),
|
||||||
|
new ColumnInfo(2, "Association", ColumnSize.HasSemantic),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.MethodImpl] = new TableInfo(Table.MethodImpl, "MethodImpl", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Class", ColumnSize.TypeDef),
|
||||||
|
new ColumnInfo(1, "MethodBody", ColumnSize.MethodDefOrRef),
|
||||||
|
new ColumnInfo(2, "MethodDeclaration", ColumnSize.MethodDefOrRef),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.ModuleRef] = new TableInfo(Table.ModuleRef, "ModuleRef", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Name", ColumnSize.Strings),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.TypeSpec] = new TableInfo(Table.TypeSpec, "TypeSpec", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Signature", ColumnSize.Blob),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.ImplMap] = new TableInfo(Table.ImplMap, "ImplMap", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "MappingFlags", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(1, "MemberForwarded", ColumnSize.MemberForwarded),
|
||||||
|
new ColumnInfo(2, "ImportName", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(3, "ImportScope", ColumnSize.ModuleRef),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.FieldRVA] = new TableInfo(Table.FieldRVA, "FieldRVA", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "RVA", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(1, "Field", ColumnSize.Field),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.ENCLog] = new TableInfo(Table.ENCLog, "ENCLog", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Token", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(1, "FuncCode", ColumnSize.UInt32),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.ENCMap] = new TableInfo(Table.ENCMap, "ENCMap", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Token", ColumnSize.UInt32),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.Assembly] = new TableInfo(Table.Assembly, "Assembly", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "HashAlgId", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(1, "MajorVersion", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(2, "MinorVersion", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(3, "BuildNumber", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(4, "RevisionNumber", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(5, "Flags", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(6, "PublicKey", ColumnSize.Blob),
|
||||||
|
new ColumnInfo(7, "Name", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(8, "Locale", ColumnSize.Strings),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.AssemblyProcessor] = new TableInfo(Table.AssemblyProcessor, "AssemblyProcessor", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Processor", ColumnSize.UInt32),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.AssemblyOS] = new TableInfo(Table.AssemblyOS, "AssemblyOS", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "OSPlatformId", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(1, "OSMajorVersion", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(2, "OSMinorVersion", ColumnSize.UInt32),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.AssemblyRef] = new TableInfo(Table.AssemblyRef, "AssemblyRef", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "MajorVersion", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(1, "MinorVersion", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(2, "BuildNumber", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(3, "RevisionNumber", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(4, "Flags", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(5, "PublicKeyOrToken", ColumnSize.Blob),
|
||||||
|
new ColumnInfo(6, "Name", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(7, "Locale", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(8, "HashValue", ColumnSize.Blob),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.AssemblyRefProcessor] = new TableInfo(Table.AssemblyRefProcessor, "AssemblyRefProcessor", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Processor", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(1, "AssemblyRef", ColumnSize.AssemblyRef),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.AssemblyRefOS] = new TableInfo(Table.AssemblyRefOS, "AssemblyRefOS", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "OSPlatformId", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(1, "OSMajorVersion", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(2, "OSMinorVersion", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(3, "AssemblyRef", ColumnSize.AssemblyRef),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.File] = new TableInfo(Table.File, "File", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Flags", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(1, "Name", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(2, "HashValue", ColumnSize.Blob),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.ExportedType] = new TableInfo(Table.ExportedType, "ExportedType", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Flags", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(1, "TypeDefId", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(2, "TypeName", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(3, "TypeNamespace", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(4, "Implementation", ColumnSize.Implementation),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.ManifestResource] = new TableInfo(Table.ManifestResource, "ManifestResource", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Offset", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(1, "Flags", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(2, "Name", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(3, "Implementation", ColumnSize.Implementation),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.NestedClass] = new TableInfo(Table.NestedClass, "NestedClass", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "NestedClass", ColumnSize.TypeDef),
|
||||||
|
new ColumnInfo(1, "EnclosingClass", ColumnSize.TypeDef),
|
||||||
|
});
|
||||||
|
if (majorVersion == 1 && minorVersion == 1) {
|
||||||
|
tableInfos[(int)Table.GenericParam] = new TableInfo(Table.GenericParam, "GenericParam", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Number", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(1, "Flags", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(2, "Owner", ColumnSize.TypeOrMethodDef),
|
||||||
|
new ColumnInfo(3, "Name", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(4, "Kind", ColumnSize.TypeDefOrRef),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tableInfos[(int)Table.GenericParam] = new TableInfo(Table.GenericParam, "GenericParam", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Number", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(1, "Flags", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(2, "Owner", ColumnSize.TypeOrMethodDef),
|
||||||
|
new ColumnInfo(3, "Name", ColumnSize.Strings),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
tableInfos[(int)Table.MethodSpec] = new TableInfo(Table.MethodSpec, "MethodSpec", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Method", ColumnSize.MethodDefOrRef),
|
||||||
|
new ColumnInfo(1, "Instantiation", ColumnSize.Blob),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.GenericParamConstraint] = new TableInfo(Table.GenericParamConstraint, "GenericParamConstraint", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Owner", ColumnSize.GenericParam),
|
||||||
|
new ColumnInfo(1, "Constraint", ColumnSize.TypeDefOrRef),
|
||||||
|
});
|
||||||
|
tableInfos[0x2D] = new TableInfo((Table)0x2D, string.Empty, new ColumnInfo[] { });
|
||||||
|
tableInfos[0x2E] = new TableInfo((Table)0x2E, string.Empty, new ColumnInfo[] { });
|
||||||
|
tableInfos[0x2F] = new TableInfo((Table)0x2F, string.Empty, new ColumnInfo[] { });
|
||||||
|
tableInfos[(int)Table.Document] = new TableInfo(Table.Document, "Document", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Name", ColumnSize.Blob),
|
||||||
|
new ColumnInfo(1, "HashAlgorithm", ColumnSize.GUID),
|
||||||
|
new ColumnInfo(2, "Hash", ColumnSize.Blob),
|
||||||
|
new ColumnInfo(3, "Language", ColumnSize.GUID),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.MethodDebugInformation] = new TableInfo(Table.MethodDebugInformation, "MethodDebugInformation", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Document", ColumnSize.Document),
|
||||||
|
new ColumnInfo(1, "SequencePoints", ColumnSize.Blob),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.LocalScope] = new TableInfo(Table.LocalScope, "LocalScope", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Method", ColumnSize.Method),
|
||||||
|
new ColumnInfo(1, "ImportScope", ColumnSize.ImportScope),
|
||||||
|
new ColumnInfo(2, "VariableList", ColumnSize.LocalVariable),
|
||||||
|
new ColumnInfo(3, "ConstantList", ColumnSize.LocalConstant),
|
||||||
|
new ColumnInfo(4, "StartOffset", ColumnSize.UInt32),
|
||||||
|
new ColumnInfo(5, "Length", ColumnSize.UInt32),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.LocalVariable] = new TableInfo(Table.LocalVariable, "LocalVariable", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Attributes", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(1, "Index", ColumnSize.UInt16),
|
||||||
|
new ColumnInfo(2, "Name", ColumnSize.Strings),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.LocalConstant] = new TableInfo(Table.LocalConstant, "LocalConstant", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Name", ColumnSize.Strings),
|
||||||
|
new ColumnInfo(1, "Signature", ColumnSize.Blob),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.ImportScope] = new TableInfo(Table.ImportScope, "ImportScope", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Parent", ColumnSize.ImportScope),
|
||||||
|
new ColumnInfo(1, "Imports", ColumnSize.Blob),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.StateMachineMethod] = new TableInfo(Table.StateMachineMethod, "StateMachineMethod", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "MoveNextMethod", ColumnSize.Method),
|
||||||
|
new ColumnInfo(1, "KickoffMethod", ColumnSize.Method),
|
||||||
|
});
|
||||||
|
tableInfos[(int)Table.CustomDebugInformation] = new TableInfo(Table.CustomDebugInformation, "CustomDebugInformation", new ColumnInfo[] {
|
||||||
|
new ColumnInfo(0, "Parent", ColumnSize.HasCustomDebugInformation),
|
||||||
|
new ColumnInfo(1, "Kind", ColumnSize.GUID),
|
||||||
|
new ColumnInfo(2, "Value", ColumnSize.Blob),
|
||||||
|
});
|
||||||
|
return this.tableInfos = tableInfos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,518 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using dnlib.IO;
|
||||||
|
using dnlib.PE;
|
||||||
|
using dnlib.Threading;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Used when a #- stream is present in the metadata
|
||||||
|
/// </summary>
|
||||||
|
sealed class ENCMetadata : MetadataBase {
|
||||||
|
static readonly UTF8String DeletedName = "_Deleted";
|
||||||
|
bool hasMethodPtr, hasFieldPtr, hasParamPtr, hasEventPtr, hasPropertyPtr;
|
||||||
|
bool hasDeletedFields;
|
||||||
|
bool hasDeletedNonFields;
|
||||||
|
readonly CLRRuntimeReaderKind runtime;
|
||||||
|
readonly Dictionary<Table, SortedTable> sortedTables = new Dictionary<Table, SortedTable>();
|
||||||
|
#if THREAD_SAFE
|
||||||
|
readonly Lock theLock = Lock.Create();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool IsCompressed => false;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ENCMetadata(IPEImage peImage, ImageCor20Header cor20Header, MetadataHeader mdHeader, CLRRuntimeReaderKind runtime)
|
||||||
|
: base(peImage, cor20Header, mdHeader) {
|
||||||
|
this.runtime = runtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
internal ENCMetadata(MetadataHeader mdHeader, bool isStandalonePortablePdb, CLRRuntimeReaderKind runtime)
|
||||||
|
: base(mdHeader, isStandalonePortablePdb) {
|
||||||
|
this.runtime = runtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void InitializeInternal(DataReaderFactory mdReaderFactory, uint metadataBaseOffset) {
|
||||||
|
DotNetStream dns = null;
|
||||||
|
bool forceAllBig = false;
|
||||||
|
try {
|
||||||
|
if (runtime == CLRRuntimeReaderKind.Mono) {
|
||||||
|
var newAllStreams = new List<DotNetStream>(allStreams);
|
||||||
|
for (int i = mdHeader.StreamHeaders.Count - 1; i >= 0; i--) {
|
||||||
|
var sh = mdHeader.StreamHeaders[i];
|
||||||
|
switch (sh.Name) {
|
||||||
|
case "#Strings":
|
||||||
|
if (stringsStream is null) {
|
||||||
|
stringsStream = new StringsStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
newAllStreams.Add(stringsStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#US":
|
||||||
|
if (usStream is null) {
|
||||||
|
usStream = new USStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
newAllStreams.Add(usStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#Blob":
|
||||||
|
if (blobStream is null) {
|
||||||
|
blobStream = new BlobStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
newAllStreams.Add(blobStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#GUID":
|
||||||
|
if (guidStream is null) {
|
||||||
|
guidStream = new GuidStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
newAllStreams.Add(guidStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#~":
|
||||||
|
case "#-":
|
||||||
|
if (tablesStream is null) {
|
||||||
|
tablesStream = new TablesStream(mdReaderFactory, metadataBaseOffset, sh, runtime);
|
||||||
|
newAllStreams.Add(tablesStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#Pdb":
|
||||||
|
if (isStandalonePortablePdb && pdbStream is null) {
|
||||||
|
pdbStream = new PdbStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
newAllStreams.Add(pdbStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#JTD":
|
||||||
|
forceAllBig = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dns = new CustomDotNetStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
newAllStreams.Add(dns);
|
||||||
|
dns = null;
|
||||||
|
}
|
||||||
|
newAllStreams.Reverse();
|
||||||
|
allStreams = newAllStreams;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Debug.Assert(runtime == CLRRuntimeReaderKind.CLR);
|
||||||
|
foreach (var sh in mdHeader.StreamHeaders) {
|
||||||
|
switch (sh.Name.ToUpperInvariant()) {
|
||||||
|
case "#STRINGS":
|
||||||
|
if (stringsStream is null) {
|
||||||
|
stringsStream = new StringsStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
allStreams.Add(stringsStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#US":
|
||||||
|
if (usStream is null) {
|
||||||
|
usStream = new USStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
allStreams.Add(usStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#BLOB":
|
||||||
|
if (blobStream is null) {
|
||||||
|
blobStream = new BlobStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
allStreams.Add(blobStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#GUID":
|
||||||
|
if (guidStream is null) {
|
||||||
|
guidStream = new GuidStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
allStreams.Add(guidStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#~": // Only if #Schema is used
|
||||||
|
case "#-":
|
||||||
|
if (tablesStream is null) {
|
||||||
|
tablesStream = new TablesStream(mdReaderFactory, metadataBaseOffset, sh, runtime);
|
||||||
|
allStreams.Add(tablesStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#PDB":
|
||||||
|
// Case sensitive comparison since it's a stream that's not read by the CLR,
|
||||||
|
// only by other libraries eg. System.Reflection.Metadata.
|
||||||
|
if (isStandalonePortablePdb && pdbStream is null && sh.Name == "#Pdb") {
|
||||||
|
pdbStream = new PdbStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
allStreams.Add(pdbStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#JTD":
|
||||||
|
forceAllBig = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dns = new CustomDotNetStream(mdReaderFactory, metadataBaseOffset, sh);
|
||||||
|
allStreams.Add(dns);
|
||||||
|
dns = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
dns?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tablesStream is null)
|
||||||
|
throw new BadImageFormatException("Missing MD stream");
|
||||||
|
|
||||||
|
if (pdbStream is not null)
|
||||||
|
tablesStream.Initialize(pdbStream.TypeSystemTableRows, forceAllBig);
|
||||||
|
else
|
||||||
|
tablesStream.Initialize(null, forceAllBig);
|
||||||
|
|
||||||
|
// The pointer tables are used iff row count != 0
|
||||||
|
hasFieldPtr = !tablesStream.FieldPtrTable.IsEmpty;
|
||||||
|
hasMethodPtr = !tablesStream.MethodPtrTable.IsEmpty;
|
||||||
|
hasParamPtr = !tablesStream.ParamPtrTable.IsEmpty;
|
||||||
|
hasEventPtr = !tablesStream.EventPtrTable.IsEmpty;
|
||||||
|
hasPropertyPtr = !tablesStream.PropertyPtrTable.IsEmpty;
|
||||||
|
|
||||||
|
switch (runtime) {
|
||||||
|
case CLRRuntimeReaderKind.CLR:
|
||||||
|
hasDeletedFields = tablesStream.HasDelete;
|
||||||
|
hasDeletedNonFields = tablesStream.HasDelete;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CLRRuntimeReaderKind.Mono:
|
||||||
|
hasDeletedFields = true;
|
||||||
|
hasDeletedNonFields = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override RidList GetTypeDefRidList() {
|
||||||
|
if (!hasDeletedNonFields)
|
||||||
|
return base.GetTypeDefRidList();
|
||||||
|
uint rows = tablesStream.TypeDefTable.Rows;
|
||||||
|
var list = new List<uint>((int)rows);
|
||||||
|
for (uint rid = 1; rid <= rows; rid++) {
|
||||||
|
if (!tablesStream.TryReadTypeDefRow(rid, out var row))
|
||||||
|
continue; // Should never happen since rid is valid
|
||||||
|
|
||||||
|
// RTSpecialName is ignored by the CLR. It's only the name that indicates
|
||||||
|
// whether it's been deleted.
|
||||||
|
// It's not possible to delete the global type (<Module>)
|
||||||
|
if (rid != 1 && stringsStream.ReadNoNull(row.Name).StartsWith(DeletedName))
|
||||||
|
continue; // ignore this deleted row
|
||||||
|
list.Add(rid);
|
||||||
|
}
|
||||||
|
return RidList.Create(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override RidList GetExportedTypeRidList() {
|
||||||
|
if (!hasDeletedNonFields)
|
||||||
|
return base.GetExportedTypeRidList();
|
||||||
|
uint rows = tablesStream.ExportedTypeTable.Rows;
|
||||||
|
var list = new List<uint>((int)rows);
|
||||||
|
for (uint rid = 1; rid <= rows; rid++) {
|
||||||
|
if (!tablesStream.TryReadExportedTypeRow(rid, out var row))
|
||||||
|
continue; // Should never happen since rid is valid
|
||||||
|
|
||||||
|
// RTSpecialName is ignored by the CLR. It's only the name that indicates
|
||||||
|
// whether it's been deleted.
|
||||||
|
if (stringsStream.ReadNoNull(row.TypeName).StartsWith(DeletedName))
|
||||||
|
continue; // ignore this deleted row
|
||||||
|
list.Add(rid);
|
||||||
|
}
|
||||||
|
return RidList.Create(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a logical <c>Field</c> rid to a physical <c>Field</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listRid">A valid rid</param>
|
||||||
|
/// <returns>Converted rid or any invalid rid value if <paramref name="listRid"/> is invalid</returns>
|
||||||
|
uint ToFieldRid(uint listRid) {
|
||||||
|
if (!hasFieldPtr)
|
||||||
|
return listRid;
|
||||||
|
return tablesStream.TryReadColumn24(tablesStream.FieldPtrTable, listRid, 0, out uint listValue) ? listValue : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a logical <c>Method</c> rid to a physical <c>Method</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listRid">A valid rid</param>
|
||||||
|
/// <returns>Converted rid or any invalid rid value if <paramref name="listRid"/> is invalid</returns>
|
||||||
|
uint ToMethodRid(uint listRid) {
|
||||||
|
if (!hasMethodPtr)
|
||||||
|
return listRid;
|
||||||
|
return tablesStream.TryReadColumn24(tablesStream.MethodPtrTable, listRid, 0, out uint listValue) ? listValue : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a logical <c>Param</c> rid to a physical <c>Param</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listRid">A valid rid</param>
|
||||||
|
/// <returns>Converted rid or any invalid rid value if <paramref name="listRid"/> is invalid</returns>
|
||||||
|
uint ToParamRid(uint listRid) {
|
||||||
|
if (!hasParamPtr)
|
||||||
|
return listRid;
|
||||||
|
return tablesStream.TryReadColumn24(tablesStream.ParamPtrTable, listRid, 0, out uint listValue) ? listValue : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a logical <c>Event</c> rid to a physical <c>Event</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listRid">A valid rid</param>
|
||||||
|
/// <returns>Converted rid or any invalid rid value if <paramref name="listRid"/> is invalid</returns>
|
||||||
|
uint ToEventRid(uint listRid) {
|
||||||
|
if (!hasEventPtr)
|
||||||
|
return listRid;
|
||||||
|
return tablesStream.TryReadColumn24(tablesStream.EventPtrTable, listRid, 0, out uint listValue) ? listValue : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a logical <c>Property</c> rid to a physical <c>Property</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listRid">A valid rid</param>
|
||||||
|
/// <returns>Converted rid or any invalid rid value if <paramref name="listRid"/> is invalid</returns>
|
||||||
|
uint ToPropertyRid(uint listRid) {
|
||||||
|
if (!hasPropertyPtr)
|
||||||
|
return listRid;
|
||||||
|
return tablesStream.TryReadColumn24(tablesStream.PropertyPtrTable, listRid, 0, out uint listValue) ? listValue : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override RidList GetFieldRidList(uint typeDefRid) {
|
||||||
|
var list = GetRidList(tablesStream.TypeDefTable, typeDefRid, 4, tablesStream.FieldTable);
|
||||||
|
if (list.Count == 0 || (!hasFieldPtr && !hasDeletedFields))
|
||||||
|
return list;
|
||||||
|
|
||||||
|
var destTable = tablesStream.FieldTable;
|
||||||
|
var newList = new List<uint>(list.Count);
|
||||||
|
for (int i = 0; i < list.Count; i++) {
|
||||||
|
var rid = ToFieldRid(list[i]);
|
||||||
|
if (destTable.IsInvalidRID(rid))
|
||||||
|
continue;
|
||||||
|
if (hasDeletedFields) {
|
||||||
|
// It's a deleted row if RTSpecialName is set and name is "_Deleted"
|
||||||
|
if (!tablesStream.TryReadFieldRow(rid, out var row))
|
||||||
|
continue; // Should never happen since rid is valid
|
||||||
|
if (runtime == CLRRuntimeReaderKind.CLR) {
|
||||||
|
if ((row.Flags & (uint)FieldAttributes.RTSpecialName) != 0) {
|
||||||
|
if (stringsStream.ReadNoNull(row.Name).StartsWith(DeletedName))
|
||||||
|
continue; // ignore this deleted row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ((row.Flags & (uint)(FieldAttributes.SpecialName | FieldAttributes.RTSpecialName)) == (uint)(FieldAttributes.SpecialName | FieldAttributes.RTSpecialName)) {
|
||||||
|
if (stringsStream.ReadNoNull(row.Name) == DeletedName)
|
||||||
|
continue; // ignore this deleted row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// It's a valid non-deleted rid so add it
|
||||||
|
newList.Add(rid);
|
||||||
|
}
|
||||||
|
return RidList.Create(newList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override RidList GetMethodRidList(uint typeDefRid) {
|
||||||
|
var list = GetRidList(tablesStream.TypeDefTable, typeDefRid, 5, tablesStream.MethodTable);
|
||||||
|
if (list.Count == 0 || (!hasMethodPtr && !hasDeletedNonFields))
|
||||||
|
return list;
|
||||||
|
|
||||||
|
var destTable = tablesStream.MethodTable;
|
||||||
|
var newList = new List<uint>(list.Count);
|
||||||
|
for (int i = 0; i < list.Count; i++) {
|
||||||
|
var rid = ToMethodRid(list[i]);
|
||||||
|
if (destTable.IsInvalidRID(rid))
|
||||||
|
continue;
|
||||||
|
if (hasDeletedNonFields) {
|
||||||
|
// It's a deleted row if RTSpecialName is set and name is "_Deleted"
|
||||||
|
if (!tablesStream.TryReadMethodRow(rid, out var row))
|
||||||
|
continue; // Should never happen since rid is valid
|
||||||
|
if ((row.Flags & (uint)MethodAttributes.RTSpecialName) != 0) {
|
||||||
|
if (stringsStream.ReadNoNull(row.Name).StartsWith(DeletedName))
|
||||||
|
continue; // ignore this deleted row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// It's a valid non-deleted rid so add it
|
||||||
|
newList.Add(rid);
|
||||||
|
}
|
||||||
|
return RidList.Create(newList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override RidList GetParamRidList(uint methodRid) {
|
||||||
|
var list = GetRidList(tablesStream.MethodTable, methodRid, 5, tablesStream.ParamTable);
|
||||||
|
if (list.Count == 0 || !hasParamPtr)
|
||||||
|
return list;
|
||||||
|
|
||||||
|
var destTable = tablesStream.ParamTable;
|
||||||
|
var newList = new List<uint>(list.Count);
|
||||||
|
for (int i = 0; i < list.Count; i++) {
|
||||||
|
var rid = ToParamRid(list[i]);
|
||||||
|
if (destTable.IsInvalidRID(rid))
|
||||||
|
continue;
|
||||||
|
newList.Add(rid);
|
||||||
|
}
|
||||||
|
return RidList.Create(newList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override RidList GetEventRidList(uint eventMapRid) {
|
||||||
|
var list = GetRidList(tablesStream.EventMapTable, eventMapRid, 1, tablesStream.EventTable);
|
||||||
|
if (list.Count == 0 || (!hasEventPtr && !hasDeletedNonFields))
|
||||||
|
return list;
|
||||||
|
|
||||||
|
var destTable = tablesStream.EventTable;
|
||||||
|
var newList = new List<uint>(list.Count);
|
||||||
|
for (int i = 0; i < list.Count; i++) {
|
||||||
|
var rid = ToEventRid(list[i]);
|
||||||
|
if (destTable.IsInvalidRID(rid))
|
||||||
|
continue;
|
||||||
|
if (hasDeletedNonFields) {
|
||||||
|
// It's a deleted row if RTSpecialName is set and name is "_Deleted"
|
||||||
|
if (!tablesStream.TryReadEventRow(rid, out var row))
|
||||||
|
continue; // Should never happen since rid is valid
|
||||||
|
if ((row.EventFlags & (uint)EventAttributes.RTSpecialName) != 0) {
|
||||||
|
if (stringsStream.ReadNoNull(row.Name).StartsWith(DeletedName))
|
||||||
|
continue; // ignore this deleted row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// It's a valid non-deleted rid so add it
|
||||||
|
newList.Add(rid);
|
||||||
|
}
|
||||||
|
return RidList.Create(newList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override RidList GetPropertyRidList(uint propertyMapRid) {
|
||||||
|
var list = GetRidList(tablesStream.PropertyMapTable, propertyMapRid, 1, tablesStream.PropertyTable);
|
||||||
|
if (list.Count == 0 || (!hasPropertyPtr && !hasDeletedNonFields))
|
||||||
|
return list;
|
||||||
|
|
||||||
|
var destTable = tablesStream.PropertyTable;
|
||||||
|
var newList = new List<uint>(list.Count);
|
||||||
|
for (int i = 0; i < list.Count; i++) {
|
||||||
|
var rid = ToPropertyRid(list[i]);
|
||||||
|
if (destTable.IsInvalidRID(rid))
|
||||||
|
continue;
|
||||||
|
if (hasDeletedNonFields) {
|
||||||
|
// It's a deleted row if RTSpecialName is set and name is "_Deleted"
|
||||||
|
if (!tablesStream.TryReadPropertyRow(rid, out var row))
|
||||||
|
continue; // Should never happen since rid is valid
|
||||||
|
if ((row.PropFlags & (uint)PropertyAttributes.RTSpecialName) != 0) {
|
||||||
|
if (stringsStream.ReadNoNull(row.Name).StartsWith(DeletedName))
|
||||||
|
continue; // ignore this deleted row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// It's a valid non-deleted rid so add it
|
||||||
|
newList.Add(rid);
|
||||||
|
}
|
||||||
|
return RidList.Create(newList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override RidList GetLocalVariableRidList(uint localScopeRid) => GetRidList(tablesStream.LocalScopeTable, localScopeRid, 2, tablesStream.LocalVariableTable);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override RidList GetLocalConstantRidList(uint localScopeRid) => GetRidList(tablesStream.LocalScopeTable, localScopeRid, 3, tablesStream.LocalConstantTable);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a rid list (eg. field list)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tableSource">Source table, eg. <c>TypeDef</c></param>
|
||||||
|
/// <param name="tableSourceRid">Row ID in <paramref name="tableSource"/></param>
|
||||||
|
/// <param name="colIndex">Column index in <paramref name="tableSource"/>, eg. 4 for <c>TypeDef.FieldList</c></param>
|
||||||
|
/// <param name="tableDest">Destination table, eg. <c>Field</c></param>
|
||||||
|
/// <returns>A new <see cref="RidList"/> instance</returns>
|
||||||
|
RidList GetRidList(MDTable tableSource, uint tableSourceRid, int colIndex, MDTable tableDest) {
|
||||||
|
var column = tableSource.TableInfo.Columns[colIndex];
|
||||||
|
if (!tablesStream.TryReadColumn24(tableSource, tableSourceRid, column, out uint startRid))
|
||||||
|
return RidList.Empty;
|
||||||
|
bool hasNext = tablesStream.TryReadColumn24(tableSource, tableSourceRid + 1, column, out uint nextListRid);
|
||||||
|
uint lastRid = tableDest.Rows + 1;
|
||||||
|
if (startRid == 0 || startRid >= lastRid)
|
||||||
|
return RidList.Empty;
|
||||||
|
uint endRid = hasNext && nextListRid != 0 ? nextListRid : lastRid;
|
||||||
|
if (endRid < startRid)
|
||||||
|
endRid = startRid;
|
||||||
|
if (endRid > lastRid)
|
||||||
|
endRid = lastRid;
|
||||||
|
return RidList.Create(startRid, endRid - startRid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override uint BinarySearch(MDTable tableSource, int keyColIndex, uint key) {
|
||||||
|
var keyColumn = tableSource.TableInfo.Columns[keyColIndex];
|
||||||
|
uint ridLo = 1, ridHi = tableSource.Rows;
|
||||||
|
while (ridLo <= ridHi) {
|
||||||
|
uint rid = (ridLo + ridHi) / 2;
|
||||||
|
if (!tablesStream.TryReadColumn24(tableSource, rid, keyColumn, out uint key2))
|
||||||
|
break; // Never happens since rid is valid
|
||||||
|
if (key == key2)
|
||||||
|
return rid;
|
||||||
|
if (key2 > key)
|
||||||
|
ridHi = rid - 1;
|
||||||
|
else
|
||||||
|
ridLo = rid + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tableSource.Table == Table.GenericParam && !tablesStream.IsSorted(tableSource))
|
||||||
|
return LinearSearch(tableSource, keyColIndex, key);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint LinearSearch(MDTable tableSource, int keyColIndex, uint key) {
|
||||||
|
if (tableSource is null)
|
||||||
|
return 0;
|
||||||
|
var keyColumn = tableSource.TableInfo.Columns[keyColIndex];
|
||||||
|
for (uint rid = 1; rid <= tableSource.Rows; rid++) {
|
||||||
|
if (!tablesStream.TryReadColumn24(tableSource, rid, keyColumn, out uint key2))
|
||||||
|
break; // Never happens since rid is valid
|
||||||
|
if (key == key2)
|
||||||
|
return rid;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override RidList FindAllRowsUnsorted(MDTable tableSource, int keyColIndex, uint key) {
|
||||||
|
if (tablesStream.IsSorted(tableSource))
|
||||||
|
return FindAllRows(tableSource, keyColIndex, key);
|
||||||
|
SortedTable sortedTable;
|
||||||
|
#if THREAD_SAFE
|
||||||
|
theLock.EnterWriteLock(); try {
|
||||||
|
#endif
|
||||||
|
if (!sortedTables.TryGetValue(tableSource.Table, out sortedTable))
|
||||||
|
sortedTables[tableSource.Table] = sortedTable = new SortedTable(tableSource, keyColIndex);
|
||||||
|
#if THREAD_SAFE
|
||||||
|
} finally { theLock.ExitWriteLock(); }
|
||||||
|
#endif
|
||||||
|
return sortedTable.FindAllRows(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using dnlib.IO;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the #GUID stream
|
||||||
|
/// </summary>
|
||||||
|
public sealed class GuidStream : HeapStream {
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public GuidStream() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public GuidStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader)
|
||||||
|
: base(mdReaderFactory, metadataBaseOffset, streamHeader) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool IsValidIndex(uint index) => index == 0 || (index <= 0x10000000 && IsValidOffset((index - 1) * 16, 16));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read a <see cref="Guid"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">Index into this stream</param>
|
||||||
|
/// <returns>A <see cref="Guid"/> or <c>null</c> if <paramref name="index"/> is 0 or invalid</returns>
|
||||||
|
public Guid? Read(uint index) {
|
||||||
|
if (index == 0 || !IsValidIndex(index))
|
||||||
|
return null;
|
||||||
|
var reader = dataReader;
|
||||||
|
reader.Position = (index - 1) * 16;
|
||||||
|
return reader.ReadGuid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Heap type. The values are set in stone by MS. Don't change.
|
||||||
|
/// </summary>
|
||||||
|
public enum HeapType : uint {
|
||||||
|
/// <summary>#Strings heap</summary>
|
||||||
|
Strings = 0,
|
||||||
|
/// <summary>#GUID heap</summary>
|
||||||
|
Guid = 1,
|
||||||
|
/// <summary>#Blob heap</summary>
|
||||||
|
Blob = 2,
|
||||||
|
/// <summary>#US heap</summary>
|
||||||
|
US = 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Reads metadata table columns
|
||||||
|
/// </summary>
|
||||||
|
public interface IColumnReader {
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a column
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="table">The table to read from</param>
|
||||||
|
/// <param name="rid">Table row id</param>
|
||||||
|
/// <param name="column">The column to read</param>
|
||||||
|
/// <param name="value">Result</param>
|
||||||
|
/// <returns><c>true</c> if <paramref name="value"/> was updated, <c>false</c> if
|
||||||
|
/// the column should be read from the original table.</returns>
|
||||||
|
bool ReadColumn(MDTable table, uint rid, ColumnInfo column, out uint value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads table rows
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TRow">Raw row</typeparam>
|
||||||
|
public interface IRowReader<TRow> where TRow : struct {
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a table row or returns false if the row should be read from the original table
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rid">Row id</param>
|
||||||
|
/// <param name="row">The row</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool TryReadRow(uint rid, out TRow row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using dnlib.IO;
|
||||||
|
using dnlib.PE;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the IMAGE_COR20_HEADER structure
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ImageCor20Header : FileSection {
|
||||||
|
readonly uint cb;
|
||||||
|
readonly ushort majorRuntimeVersion;
|
||||||
|
readonly ushort minorRuntimeVersion;
|
||||||
|
readonly ImageDataDirectory metadata;
|
||||||
|
readonly ComImageFlags flags;
|
||||||
|
readonly uint entryPointToken_or_RVA;
|
||||||
|
readonly ImageDataDirectory resources;
|
||||||
|
readonly ImageDataDirectory strongNameSignature;
|
||||||
|
readonly ImageDataDirectory codeManagerTable;
|
||||||
|
readonly ImageDataDirectory vtableFixups;
|
||||||
|
readonly ImageDataDirectory exportAddressTableJumps;
|
||||||
|
readonly ImageDataDirectory managedNativeHeader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns <c>true</c> if it has a native header
|
||||||
|
/// </summary>
|
||||||
|
public bool HasNativeHeader => (flags & ComImageFlags.ILLibrary) != 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the IMAGE_COR20_HEADER.cb field
|
||||||
|
/// </summary>
|
||||||
|
public uint CB => cb;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the IMAGE_COR20_HEADER.MajorRuntimeVersion field
|
||||||
|
/// </summary>
|
||||||
|
public ushort MajorRuntimeVersion => majorRuntimeVersion;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the IMAGE_COR20_HEADER.MinorRuntimeVersion field
|
||||||
|
/// </summary>
|
||||||
|
public ushort MinorRuntimeVersion => minorRuntimeVersion;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the IMAGE_COR20_HEADER.Metadata field
|
||||||
|
/// </summary>
|
||||||
|
public ImageDataDirectory Metadata => metadata;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the IMAGE_COR20_HEADER.Flags field
|
||||||
|
/// </summary>
|
||||||
|
public ComImageFlags Flags => flags;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the IMAGE_COR20_HEADER.EntryPointToken/EntryPointTokenRVA field
|
||||||
|
/// </summary>
|
||||||
|
public uint EntryPointToken_or_RVA => entryPointToken_or_RVA;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the IMAGE_COR20_HEADER.Resources field
|
||||||
|
/// </summary>
|
||||||
|
public ImageDataDirectory Resources => resources;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the IMAGE_COR20_HEADER.StrongNameSignature field
|
||||||
|
/// </summary>
|
||||||
|
public ImageDataDirectory StrongNameSignature => strongNameSignature;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the IMAGE_COR20_HEADER.CodeManagerTable field
|
||||||
|
/// </summary>
|
||||||
|
public ImageDataDirectory CodeManagerTable => codeManagerTable;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the IMAGE_COR20_HEADER.VTableFixups field
|
||||||
|
/// </summary>
|
||||||
|
public ImageDataDirectory VTableFixups => vtableFixups;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the IMAGE_COR20_HEADER.ExportAddressTableJumps field
|
||||||
|
/// </summary>
|
||||||
|
public ImageDataDirectory ExportAddressTableJumps => exportAddressTableJumps;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the IMAGE_COR20_HEADER.ManagedNativeHeader field
|
||||||
|
/// </summary>
|
||||||
|
public ImageDataDirectory ManagedNativeHeader => managedNativeHeader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">PE file reader pointing to the start of this section</param>
|
||||||
|
/// <param name="verify">Verify section</param>
|
||||||
|
/// <exception cref="BadImageFormatException">Thrown if verification fails</exception>
|
||||||
|
public ImageCor20Header(ref DataReader reader, bool verify) {
|
||||||
|
SetStartOffset(ref reader);
|
||||||
|
cb = reader.ReadUInt32();
|
||||||
|
if (verify && cb < 0x48)
|
||||||
|
throw new BadImageFormatException("Invalid IMAGE_COR20_HEADER.cb value");
|
||||||
|
majorRuntimeVersion = reader.ReadUInt16();
|
||||||
|
minorRuntimeVersion = reader.ReadUInt16();
|
||||||
|
metadata = new ImageDataDirectory(ref reader, verify);
|
||||||
|
flags = (ComImageFlags)reader.ReadUInt32();
|
||||||
|
entryPointToken_or_RVA = reader.ReadUInt32();
|
||||||
|
resources = new ImageDataDirectory(ref reader, verify);
|
||||||
|
strongNameSignature = new ImageDataDirectory(ref reader, verify);
|
||||||
|
codeManagerTable = new ImageDataDirectory(ref reader, verify);
|
||||||
|
vtableFixups = new ImageDataDirectory(ref reader, verify);
|
||||||
|
exportAddressTableJumps = new ImageDataDirectory(ref reader, verify);
|
||||||
|
managedNativeHeader = new ImageDataDirectory(ref reader, verify);
|
||||||
|
SetEndoffset(ref reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Version strings found in the meta data header
|
||||||
|
/// </summary>
|
||||||
|
public static class MDHeaderRuntimeVersion {
|
||||||
|
/// <summary>
|
||||||
|
/// MS CLR 1.0 version string (.NET Framework 1.0)
|
||||||
|
/// </summary>
|
||||||
|
public const string MS_CLR_10 = "v1.0.3705";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MS CLR 1.0 version string (.NET Framework 1.0). This is an incorrect version that shouldn't be used.
|
||||||
|
/// </summary>
|
||||||
|
public const string MS_CLR_10_X86RETAIL = "v1.x86ret";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MS CLR 1.0 version string (.NET Framework 1.0). This is an incorrect version that shouldn't be used.
|
||||||
|
/// </summary>
|
||||||
|
public const string MS_CLR_10_RETAIL = "retail";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MS CLR 1.0 version string (.NET Framework 1.0). This is an incorrect version that shouldn't be used.
|
||||||
|
/// </summary>
|
||||||
|
public const string MS_CLR_10_COMPLUS = "COMPLUS";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MS CLR 1.1 version string (.NET Framework 1.1)
|
||||||
|
/// </summary>
|
||||||
|
public const string MS_CLR_11 = "v1.1.4322";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MS CLR 2.0 version string (.NET Framework 2.0-3.5)
|
||||||
|
/// </summary>
|
||||||
|
public const string MS_CLR_20 = "v2.0.50727";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MS CLR 4.0 version string (.NET Framework 4.0-4.5)
|
||||||
|
/// </summary>
|
||||||
|
public const string MS_CLR_40 = "v4.0.30319";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MS CLR 1.0 any version
|
||||||
|
/// </summary>
|
||||||
|
public const string MS_CLR_10_PREFIX = "v1.0";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MS CLR 1.0 any version
|
||||||
|
/// </summary>
|
||||||
|
public const string MS_CLR_10_PREFIX_X86RETAIL = "v1.x86";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MS CLR 1.1 any version
|
||||||
|
/// </summary>
|
||||||
|
public const string MS_CLR_11_PREFIX = "v1.1";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MS CLR 2.0 any version
|
||||||
|
/// </summary>
|
||||||
|
public const string MS_CLR_20_PREFIX = "v2.0";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MS CLR 4.0 any version
|
||||||
|
/// </summary>
|
||||||
|
public const string MS_CLR_40_PREFIX = "v4.0";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ECMA 2002 version string
|
||||||
|
/// </summary>
|
||||||
|
public const string ECMA_2002 = "Standard CLI 2002";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ECMA 2005 version string
|
||||||
|
/// </summary>
|
||||||
|
public const string ECMA_2005 = "Standard CLI 2005";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Portable PDB v1.0
|
||||||
|
/// </summary>
|
||||||
|
public const string PORTABLE_PDB_V1_0 = "PDB v1.0";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// MDStream flags
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum MDStreamFlags : byte {
|
||||||
|
/// <summary>#Strings stream is big and requires 4 byte offsets</summary>
|
||||||
|
BigStrings = 1,
|
||||||
|
/// <summary>#GUID stream is big and requires 4 byte offsets</summary>
|
||||||
|
BigGUID = 2,
|
||||||
|
/// <summary>#Blob stream is big and requires 4 byte offsets</summary>
|
||||||
|
BigBlob = 4,
|
||||||
|
/// <summary/>
|
||||||
|
Padding = 8,
|
||||||
|
/// <summary/>
|
||||||
|
DeltaOnly = 0x20,
|
||||||
|
/// <summary>Extra data follows the row counts</summary>
|
||||||
|
ExtraData = 0x40,
|
||||||
|
/// <summary>Set if certain tables can contain deleted rows. The name column (if present) is set to "_Deleted"</summary>
|
||||||
|
HasDelete = 0x80,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using dnlib.IO;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// A MD table (eg. Method table)
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("DL:{dataReader.Length} R:{numRows} RS:{tableInfo.RowSize} C:{Count} {tableInfo.Name}")]
|
||||||
|
public sealed class MDTable : IDisposable, IFileSection {
|
||||||
|
readonly Table table;
|
||||||
|
uint numRows;
|
||||||
|
TableInfo tableInfo;
|
||||||
|
DataReader dataReader;
|
||||||
|
|
||||||
|
// Fix for VS2015 expression evaluator: "The debugger is unable to evaluate this expression"
|
||||||
|
int Count => tableInfo.Columns.Length;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public FileOffset StartOffset => (FileOffset)dataReader.StartOffset;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public FileOffset EndOffset => (FileOffset)dataReader.EndOffset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the table
|
||||||
|
/// </summary>
|
||||||
|
public Table Table => table;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of this table
|
||||||
|
/// </summary>
|
||||||
|
public string Name => tableInfo.Name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns total number of rows
|
||||||
|
/// </summary>
|
||||||
|
public uint Rows => numRows;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the total size in bytes of one row in this table
|
||||||
|
/// </summary>
|
||||||
|
public uint RowSize => (uint)tableInfo.RowSize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns all the columns
|
||||||
|
/// </summary>
|
||||||
|
public IList<ColumnInfo> Columns => tableInfo.Columns;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns <c>true</c> if there are no valid rows
|
||||||
|
/// </summary>
|
||||||
|
public bool IsEmpty => numRows == 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns info about this table
|
||||||
|
/// </summary>
|
||||||
|
public TableInfo TableInfo => tableInfo;
|
||||||
|
|
||||||
|
internal DataReader DataReader {
|
||||||
|
get => dataReader;
|
||||||
|
set => dataReader = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="table">The table</param>
|
||||||
|
/// <param name="numRows">Number of rows in this table</param>
|
||||||
|
/// <param name="tableInfo">Info about this table</param>
|
||||||
|
internal MDTable(Table table, uint numRows, TableInfo tableInfo) {
|
||||||
|
this.table = table;
|
||||||
|
this.numRows = numRows;
|
||||||
|
this.tableInfo = tableInfo;
|
||||||
|
|
||||||
|
var columns = tableInfo.Columns;
|
||||||
|
int length = columns.Length;
|
||||||
|
if (length > 0) Column0 = columns[0];
|
||||||
|
if (length > 1) Column1 = columns[1];
|
||||||
|
if (length > 2) Column2 = columns[2];
|
||||||
|
if (length > 3) Column3 = columns[3];
|
||||||
|
if (length > 4) Column4 = columns[4];
|
||||||
|
if (length > 5) Column5 = columns[5];
|
||||||
|
if (length > 6) Column6 = columns[6];
|
||||||
|
if (length > 7) Column7 = columns[7];
|
||||||
|
if (length > 8) Column8 = columns[8];
|
||||||
|
}
|
||||||
|
|
||||||
|
// So we don't have to call IList<T> indexer
|
||||||
|
internal readonly ColumnInfo Column0;
|
||||||
|
internal readonly ColumnInfo Column1;
|
||||||
|
internal readonly ColumnInfo Column2;
|
||||||
|
internal readonly ColumnInfo Column3;
|
||||||
|
internal readonly ColumnInfo Column4;
|
||||||
|
internal readonly ColumnInfo Column5;
|
||||||
|
internal readonly ColumnInfo Column6;
|
||||||
|
internal readonly ColumnInfo Column7;
|
||||||
|
internal readonly ColumnInfo Column8;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the row <paramref name="rid"/> exists
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rid">Row ID</param>
|
||||||
|
public bool IsValidRID(uint rid) => rid != 0 && rid <= numRows;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the row <paramref name="rid"/> does not exist
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rid">Row ID</param>
|
||||||
|
public bool IsInvalidRID(uint rid) => rid == 0 || rid > numRows;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Dispose() {
|
||||||
|
numRows = 0;
|
||||||
|
tableInfo = null;
|
||||||
|
dataReader = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,371 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using dnlib.PE;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Reads .NET metadata
|
||||||
|
/// </summary>
|
||||||
|
public abstract class Metadata : IDisposable {
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if the compressed (normal) metadata is used, <c>false</c> if the non-compressed
|
||||||
|
/// (Edit N' Continue) metadata is used. This can be <c>false</c> even if the table stream
|
||||||
|
/// is <c>#~</c> but that's very uncommon.
|
||||||
|
/// </summary>
|
||||||
|
public abstract bool IsCompressed { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c> if this is standalone Portable PDB metadata
|
||||||
|
/// </summary>
|
||||||
|
public abstract bool IsStandalonePortablePdb { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the .NET header
|
||||||
|
/// </summary>
|
||||||
|
public abstract ImageCor20Header ImageCor20Header { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the version found in the metadata header. The major version number is in the high 16 bits
|
||||||
|
/// and the lower version number is in the low 16 bits.
|
||||||
|
/// </summary>
|
||||||
|
public abstract uint Version { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the version string found in the metadata header
|
||||||
|
/// </summary>
|
||||||
|
public abstract string VersionString { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="IPEImage"/>
|
||||||
|
/// </summary>
|
||||||
|
public abstract IPEImage PEImage { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the metadata header
|
||||||
|
/// </summary>
|
||||||
|
public abstract MetadataHeader MetadataHeader { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the #Strings stream or a default empty one if it's not present
|
||||||
|
/// </summary>
|
||||||
|
public abstract StringsStream StringsStream { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the #US stream or a default empty one if it's not present
|
||||||
|
/// </summary>
|
||||||
|
public abstract USStream USStream { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the #Blob stream or a default empty one if it's not present
|
||||||
|
/// </summary>
|
||||||
|
public abstract BlobStream BlobStream { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the #GUID stream or a default empty one if it's not present
|
||||||
|
/// </summary>
|
||||||
|
public abstract GuidStream GuidStream { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the #~ or #- tables stream
|
||||||
|
/// </summary>
|
||||||
|
public abstract TablesStream TablesStream { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the #Pdb stream or null if it's not a standalone portable PDB file
|
||||||
|
/// </summary>
|
||||||
|
public abstract PdbStream PdbStream { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all streams
|
||||||
|
/// </summary>
|
||||||
|
public abstract IList<DotNetStream> AllStreams { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of all the valid <c>TypeDef</c> rids. It's usually every rid in the
|
||||||
|
/// <c>TypeDef</c> table, but could be less if a type has been deleted.
|
||||||
|
/// </summary>
|
||||||
|
public abstract RidList GetTypeDefRidList();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of all the valid <c>ExportedType</c> rids. It's usually every rid in the
|
||||||
|
/// <c>ExportedType</c> table, but could be less if a type has been deleted.
|
||||||
|
/// </summary>
|
||||||
|
public abstract RidList GetExportedTypeRidList();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <c>Field</c> rid list
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeDefRid"><c>TypeDef</c> rid</param>
|
||||||
|
/// <returns>A new <see cref="RidList"/> instance</returns>
|
||||||
|
public abstract RidList GetFieldRidList(uint typeDefRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <c>Method</c> rid list
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeDefRid"><c>TypeDef</c> rid</param>
|
||||||
|
/// <returns>A new <see cref="RidList"/> instance</returns>
|
||||||
|
public abstract RidList GetMethodRidList(uint typeDefRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <c>Param</c> rid list
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="methodRid"><c>Method</c> rid</param>
|
||||||
|
/// <returns>A new <see cref="RidList"/> instance</returns>
|
||||||
|
public abstract RidList GetParamRidList(uint methodRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <c>Event</c> rid list
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventMapRid"><c>EventMap</c> rid</param>
|
||||||
|
/// <returns>A new <see cref="RidList"/> instance</returns>
|
||||||
|
public abstract RidList GetEventRidList(uint eventMapRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <c>Property</c> rid list
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyMapRid"><c>PropertyMap</c> rid</param>
|
||||||
|
/// <returns>A new <see cref="RidList"/> instance</returns>
|
||||||
|
public abstract RidList GetPropertyRidList(uint propertyMapRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all <c>InterfaceImpl</c> rids owned by <paramref name="typeDefRid"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeDefRid">Owner <c>TypeDef</c> rid</param>
|
||||||
|
/// <returns>A <see cref="RidList"/> instance containing the valid <c>InterfaceImpl</c> rids</returns>
|
||||||
|
public abstract RidList GetInterfaceImplRidList(uint typeDefRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all <c>GenericParam</c> rids owned by <paramref name="rid"/> in table <paramref name="table"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="table">A <c>TypeOrMethodDef</c> table</param>
|
||||||
|
/// <param name="rid">Owner rid</param>
|
||||||
|
/// <returns>A <see cref="RidList"/> instance containing the valid <c>GenericParam</c> rids</returns>
|
||||||
|
public abstract RidList GetGenericParamRidList(Table table, uint rid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all <c>GenericParamConstraint</c> rids owned by <paramref name="genericParamRid"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="genericParamRid">Owner <c>GenericParam</c> rid</param>
|
||||||
|
/// <returns>A <see cref="RidList"/> instance containing the valid <c>GenericParamConstraint</c> rids</returns>
|
||||||
|
public abstract RidList GetGenericParamConstraintRidList(uint genericParamRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all <c>CustomAttribute</c> rids owned by <paramref name="rid"/> in table <paramref name="table"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="table">A <c>HasCustomAttribute</c> table</param>
|
||||||
|
/// <param name="rid">Owner rid</param>
|
||||||
|
/// <returns>A <see cref="RidList"/> instance containing the valid <c>CustomAttribute</c> rids</returns>
|
||||||
|
public abstract RidList GetCustomAttributeRidList(Table table, uint rid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all <c>DeclSecurity</c> rids owned by <paramref name="rid"/> in table <paramref name="table"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="table">A <c>HasDeclSecurity</c> table</param>
|
||||||
|
/// <param name="rid">Owner rid</param>
|
||||||
|
/// <returns>A <see cref="RidList"/> instance containing the valid <c>DeclSecurity</c> rids</returns>
|
||||||
|
public abstract RidList GetDeclSecurityRidList(Table table, uint rid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all <c>MethodSemantics</c> rids owned by <paramref name="rid"/> in table <paramref name="table"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="table">A <c>HasSemantic</c> table</param>
|
||||||
|
/// <param name="rid">Owner rid</param>
|
||||||
|
/// <returns>A <see cref="RidList"/> instance containing the valid <c>MethodSemantics</c> rids</returns>
|
||||||
|
public abstract RidList GetMethodSemanticsRidList(Table table, uint rid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all <c>MethodImpl</c> rids owned by <paramref name="typeDefRid"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeDefRid">Owner <c>TypeDef</c> rid</param>
|
||||||
|
/// <returns>A <see cref="RidList"/> instance containing the valid <c>MethodImpl</c> rids</returns>
|
||||||
|
public abstract RidList GetMethodImplRidList(uint typeDefRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a <c>ClassLayout</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeDefRid">Owner <c>TypeDef</c> rid</param>
|
||||||
|
/// <returns>The <c>ClassLayout</c> rid or 0 if <paramref name="typeDefRid"/> is invalid
|
||||||
|
/// or if it has no row in the <c>ClassLayout</c> table.</returns>
|
||||||
|
public abstract uint GetClassLayoutRid(uint typeDefRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a <c>FieldLayout</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fieldRid">Owner <c>Field</c> rid</param>
|
||||||
|
/// <returns>The <c>FieldLayout</c> rid or 0 if <paramref name="fieldRid"/> is invalid
|
||||||
|
/// or if it has no row in the <c>FieldLayout</c> table.</returns>
|
||||||
|
public abstract uint GetFieldLayoutRid(uint fieldRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a <c>FieldMarshal</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="table">A <c>HasFieldMarshal</c> table</param>
|
||||||
|
/// <param name="rid">Owner rid</param>
|
||||||
|
/// <returns>The <c>FieldMarshal</c> rid or 0 if <paramref name="rid"/> is invalid
|
||||||
|
/// or if it has no row in the <c>FieldMarshal</c> table.</returns>
|
||||||
|
public abstract uint GetFieldMarshalRid(Table table, uint rid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a <c>FieldRVA</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fieldRid">Owner <c>Field</c> rid</param>
|
||||||
|
/// <returns>The <c>FieldRVA</c> rid or 0 if <paramref name="fieldRid"/> is invalid
|
||||||
|
/// or if it has no row in the <c>FieldRVA</c> table.</returns>
|
||||||
|
public abstract uint GetFieldRVARid(uint fieldRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds an <c>ImplMap</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="table">A <c>MemberForwarded</c> table</param>
|
||||||
|
/// <param name="rid">Owner rid</param>
|
||||||
|
/// <returns>The <c>ImplMap</c> rid or 0 if <paramref name="rid"/> is invalid
|
||||||
|
/// or if it has no row in the <c>ImplMap</c> table.</returns>
|
||||||
|
public abstract uint GetImplMapRid(Table table, uint rid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a <c>NestedClass</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeDefRid">Owner <c>TypeDef</c> rid</param>
|
||||||
|
/// <returns>The <c>NestedClass</c> rid or 0 if <paramref name="typeDefRid"/> is invalid
|
||||||
|
/// or if it has no row in the <c>NestedClass</c> table.</returns>
|
||||||
|
public abstract uint GetNestedClassRid(uint typeDefRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds an <c>EventMap</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeDefRid">Owner <c>TypeDef</c> rid</param>
|
||||||
|
/// <returns>The <c>EventMap</c> rid or 0 if <paramref name="typeDefRid"/> is invalid
|
||||||
|
/// or if it has no row in the <c>EventMap</c> table.</returns>
|
||||||
|
public abstract uint GetEventMapRid(uint typeDefRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a <c>PropertyMap</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeDefRid">Owner <c>TypeDef</c> rid</param>
|
||||||
|
/// <returns>The <c>PropertyMap</c> rid or 0 if <paramref name="typeDefRid"/> is invalid
|
||||||
|
/// or if it has no row in the <c>PropertyMap</c> table.</returns>
|
||||||
|
public abstract uint GetPropertyMapRid(uint typeDefRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a <c>Constant</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="table">A <c>HasConstant</c> table</param>
|
||||||
|
/// <param name="rid">Owner rid</param>
|
||||||
|
/// <returns>The <c>Constant</c> rid or 0 if <paramref name="rid"/> is invalid
|
||||||
|
/// or if it has no row in the <c>Constant</c> table.</returns>
|
||||||
|
public abstract uint GetConstantRid(Table table, uint rid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the owner <c>TypeDef</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fieldRid">A <c>Field</c> rid</param>
|
||||||
|
/// <returns>The owner <c>TypeDef</c> rid or 0 if <paramref name="fieldRid"/> is invalid
|
||||||
|
/// or if it has no owner.</returns>
|
||||||
|
public abstract uint GetOwnerTypeOfField(uint fieldRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the owner <c>TypeDef</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="methodRid">A <c>Method</c> rid</param>
|
||||||
|
/// <returns>The owner <c>TypeDef</c> rid or 0 if <paramref name="methodRid"/> is invalid
|
||||||
|
/// or if it has no owner.</returns>
|
||||||
|
public abstract uint GetOwnerTypeOfMethod(uint methodRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the owner <c>TypeDef</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventRid">A <c>Event</c> rid</param>
|
||||||
|
/// <returns>The owner <c>TypeDef</c> rid or 0 if <paramref name="eventRid"/> is invalid
|
||||||
|
/// or if it has no owner.</returns>
|
||||||
|
public abstract uint GetOwnerTypeOfEvent(uint eventRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the owner <c>TypeDef</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyRid">A <c>Property</c> rid</param>
|
||||||
|
/// <returns>The owner <c>TypeDef</c> rid or 0 if <paramref name="propertyRid"/> is invalid
|
||||||
|
/// or if it has no owner.</returns>
|
||||||
|
public abstract uint GetOwnerTypeOfProperty(uint propertyRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the owner <c>TypeOrMethodDef</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="gpRid">A <c>GenericParam</c> rid</param>
|
||||||
|
/// <returns>The owner <c>TypeOrMethodDef</c> rid or 0 if <paramref name="gpRid"/> is
|
||||||
|
/// invalid or if it has no owner.</returns>
|
||||||
|
public abstract uint GetOwnerOfGenericParam(uint gpRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the owner <c>GenericParam</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="gpcRid">A <c>GenericParamConstraint</c> rid</param>
|
||||||
|
/// <returns>The owner <c>GenericParam</c> rid or 0 if <paramref name="gpcRid"/> is
|
||||||
|
/// invalid or if it has no owner.</returns>
|
||||||
|
public abstract uint GetOwnerOfGenericParamConstraint(uint gpcRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the owner <c>Method</c> rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="paramRid">A <c>Param</c> rid</param>
|
||||||
|
/// <returns>The owner <c>Method</c> rid or 0 if <paramref name="paramRid"/> is invalid
|
||||||
|
/// or if it has no owner.</returns>
|
||||||
|
public abstract uint GetOwnerOfParam(uint paramRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of all nested classes owned by <paramref name="typeDefRid"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeDefRid">A <c>TypeDef</c> rid</param>
|
||||||
|
/// <returns>A new <see cref="RidList"/> instance</returns>
|
||||||
|
public abstract RidList GetNestedClassRidList(uint typeDefRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of all non-nested classes. A type is a non-nested type if
|
||||||
|
/// <see cref="GetNestedClassRidList(uint)"/> returns an empty list.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A new <see cref="RidList"/> instance</returns>
|
||||||
|
public abstract RidList GetNonNestedClassRidList();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all <c>LocalScope</c> rids owned by <paramref name="methodRid"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="methodRid">Owner <c>Method</c> rid</param>
|
||||||
|
/// <returns>A <see cref="RidList"/> instance containing the valid <c>LocalScope</c> rids</returns>
|
||||||
|
public abstract RidList GetLocalScopeRidList(uint methodRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all <c>LocalVariable</c> rids owned by <paramref name="localScopeRid"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="localScopeRid">Owner <c>LocalScope</c> rid</param>
|
||||||
|
/// <returns>A <see cref="RidList"/> instance containing the valid <c>LocalVariable</c> rids</returns>
|
||||||
|
public abstract RidList GetLocalVariableRidList(uint localScopeRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all <c>LocalConstant</c> rids owned by <paramref name="localScopeRid"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="localScopeRid">Owner <c>LocalScope</c> rid</param>
|
||||||
|
/// <returns>A <see cref="RidList"/> instance containing the valid <c>LocalConstant</c> rids</returns>
|
||||||
|
public abstract RidList GetLocalConstantRidList(uint localScopeRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <c>StateMachineMethod</c> rid or 0 if it's not a state machine method
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="methodRid">Owner <c>Method</c> rid</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public abstract uint GetStateMachineMethodRid(uint methodRid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all <c>CustomDebugInformation</c> rids owned by <paramref name="rid"/> in table <paramref name="table"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="table">A <c>HasCustomDebugInformation</c> table</param>
|
||||||
|
/// <param name="rid">Owner rid</param>
|
||||||
|
/// <returns>A <see cref="RidList"/> instance containing the valid <c>CustomDebugInformation</c> rids</returns>
|
||||||
|
public abstract RidList GetCustomDebugInformationRidList(Table table, uint rid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes of this instance
|
||||||
|
/// </summary>
|
||||||
|
public abstract void Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,763 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
using dnlib.IO;
|
||||||
|
using dnlib.PE;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Common base class for #~ and #- metadata classes
|
||||||
|
/// </summary>
|
||||||
|
abstract class MetadataBase : Metadata {
|
||||||
|
/// <summary>
|
||||||
|
/// The PE image
|
||||||
|
/// </summary>
|
||||||
|
protected IPEImage peImage;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The .NET header
|
||||||
|
/// </summary>
|
||||||
|
protected ImageCor20Header cor20Header;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The MD header
|
||||||
|
/// </summary>
|
||||||
|
protected MetadataHeader mdHeader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The #Strings stream
|
||||||
|
/// </summary>
|
||||||
|
protected StringsStream stringsStream;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The #US stream
|
||||||
|
/// </summary>
|
||||||
|
protected USStream usStream;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The #Blob stream
|
||||||
|
/// </summary>
|
||||||
|
protected BlobStream blobStream;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The #GUID stream
|
||||||
|
/// </summary>
|
||||||
|
protected GuidStream guidStream;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The #~ or #- stream
|
||||||
|
/// </summary>
|
||||||
|
protected TablesStream tablesStream;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The #Pdb stream
|
||||||
|
/// </summary>
|
||||||
|
protected PdbStream pdbStream;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All the streams that are present in the PE image
|
||||||
|
/// </summary>
|
||||||
|
protected IList<DotNetStream> allStreams;
|
||||||
|
|
||||||
|
public override bool IsStandalonePortablePdb => isStandalonePortablePdb;
|
||||||
|
/// <summary><c>true</c> if this is standalone Portable PDB metadata</summary>
|
||||||
|
protected readonly bool isStandalonePortablePdb;
|
||||||
|
|
||||||
|
uint[] fieldRidToTypeDefRid;
|
||||||
|
uint[] methodRidToTypeDefRid;
|
||||||
|
uint[] eventRidToTypeDefRid;
|
||||||
|
uint[] propertyRidToTypeDefRid;
|
||||||
|
uint[] gpRidToOwnerRid;
|
||||||
|
uint[] gpcRidToOwnerRid;
|
||||||
|
uint[] paramRidToOwnerRid;
|
||||||
|
Dictionary<uint, List<uint>> typeDefRidToNestedClasses;
|
||||||
|
StrongBox<RidList> nonNestedTypes;
|
||||||
|
|
||||||
|
DataReaderFactory mdReaderFactoryToDisposeLater;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sorts a table by key column
|
||||||
|
/// </summary>
|
||||||
|
protected sealed class SortedTable {
|
||||||
|
RowInfo[] rows;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remembers <c>rid</c> and key
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("{rid} {key}")]
|
||||||
|
readonly struct RowInfo : IComparable<RowInfo> {
|
||||||
|
public readonly uint rid;
|
||||||
|
public readonly uint key;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rid">Row ID</param>
|
||||||
|
/// <param name="key">Key</param>
|
||||||
|
public RowInfo(uint rid, uint key) {
|
||||||
|
this.rid = rid;
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CompareTo(RowInfo other) {
|
||||||
|
if (key < other.key)
|
||||||
|
return -1;
|
||||||
|
if (key > other.key)
|
||||||
|
return 1;
|
||||||
|
return rid.CompareTo(other.rid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mdTable">The MD table</param>
|
||||||
|
/// <param name="keyColIndex">Index of key column</param>
|
||||||
|
public SortedTable(MDTable mdTable, int keyColIndex) {
|
||||||
|
InitializeKeys(mdTable, keyColIndex);
|
||||||
|
Array.Sort(rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeKeys(MDTable mdTable, int keyColIndex) {
|
||||||
|
var keyColumn = mdTable.TableInfo.Columns[keyColIndex];
|
||||||
|
Debug.Assert(keyColumn.Size == 2 || keyColumn.Size == 4);
|
||||||
|
rows = new RowInfo[mdTable.Rows + 1];
|
||||||
|
if (mdTable.Rows == 0)
|
||||||
|
return;
|
||||||
|
var reader = mdTable.DataReader;
|
||||||
|
reader.Position = (uint)keyColumn.Offset;
|
||||||
|
uint increment = (uint)(mdTable.TableInfo.RowSize - keyColumn.Size);
|
||||||
|
for (uint i = 1; i <= mdTable.Rows; i++) {
|
||||||
|
rows[i] = new RowInfo(i, keyColumn.Unsafe_Read24(ref reader));
|
||||||
|
if (i < mdTable.Rows)
|
||||||
|
reader.Position += increment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Binary searches for a row with a certain key
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key</param>
|
||||||
|
/// <returns>The row or 0 if not found</returns>
|
||||||
|
int BinarySearch(uint key) {
|
||||||
|
int lo = 1, hi = rows.Length - 1;
|
||||||
|
while (lo <= hi && hi != -1) {
|
||||||
|
int curr = (lo + hi) / 2;
|
||||||
|
uint key2 = rows[curr].key;
|
||||||
|
if (key == key2)
|
||||||
|
return curr;
|
||||||
|
if (key2 > key)
|
||||||
|
hi = curr - 1;
|
||||||
|
else
|
||||||
|
lo = curr + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Find all rids that contain <paramref name="key"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key</param>
|
||||||
|
/// <returns>A new <see cref="RidList"/> instance</returns>
|
||||||
|
public RidList FindAllRows(uint key) {
|
||||||
|
int startIndex = BinarySearch(key);
|
||||||
|
if (startIndex == 0)
|
||||||
|
return RidList.Empty;
|
||||||
|
int endIndex = startIndex + 1;
|
||||||
|
for (; startIndex > 1; startIndex--) {
|
||||||
|
if (key != rows[startIndex - 1].key)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (; endIndex < rows.Length; endIndex++) {
|
||||||
|
if (key != rows[endIndex].key)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var list = new List<uint>(endIndex - startIndex);
|
||||||
|
for (int i = startIndex; i < endIndex; i++)
|
||||||
|
list.Add(rows[i].rid);
|
||||||
|
return RidList.Create(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SortedTable eventMapSortedTable;
|
||||||
|
SortedTable propertyMapSortedTable;
|
||||||
|
|
||||||
|
public override ImageCor20Header ImageCor20Header => cor20Header;
|
||||||
|
public override uint Version => ((uint)mdHeader.MajorVersion << 16) | mdHeader.MinorVersion;
|
||||||
|
public override string VersionString => mdHeader.VersionString;
|
||||||
|
public override IPEImage PEImage => peImage;
|
||||||
|
public override MetadataHeader MetadataHeader => mdHeader;
|
||||||
|
public override StringsStream StringsStream => stringsStream;
|
||||||
|
public override USStream USStream => usStream;
|
||||||
|
public override BlobStream BlobStream => blobStream;
|
||||||
|
public override GuidStream GuidStream => guidStream;
|
||||||
|
public override TablesStream TablesStream => tablesStream;
|
||||||
|
public override PdbStream PdbStream => pdbStream;
|
||||||
|
public override IList<DotNetStream> AllStreams => allStreams;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="peImage">The PE image</param>
|
||||||
|
/// <param name="cor20Header">The .NET header</param>
|
||||||
|
/// <param name="mdHeader">The MD header</param>
|
||||||
|
protected MetadataBase(IPEImage peImage, ImageCor20Header cor20Header, MetadataHeader mdHeader) {
|
||||||
|
try {
|
||||||
|
allStreams = new List<DotNetStream>();
|
||||||
|
this.peImage = peImage;
|
||||||
|
this.cor20Header = cor20Header;
|
||||||
|
this.mdHeader = mdHeader;
|
||||||
|
isStandalonePortablePdb = false;
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
if (peImage is not null)
|
||||||
|
peImage.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal MetadataBase(MetadataHeader mdHeader, bool isStandalonePortablePdb) {
|
||||||
|
allStreams = new List<DotNetStream>();
|
||||||
|
peImage = null;
|
||||||
|
cor20Header = null;
|
||||||
|
this.mdHeader = mdHeader;
|
||||||
|
this.isStandalonePortablePdb = isStandalonePortablePdb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the metadata, tables, streams
|
||||||
|
/// </summary>
|
||||||
|
public void Initialize(DataReaderFactory mdReaderFactory) {
|
||||||
|
mdReaderFactoryToDisposeLater = mdReaderFactory;
|
||||||
|
uint metadataBaseOffset;
|
||||||
|
if (peImage is not null) {
|
||||||
|
Debug.Assert(mdReaderFactory is null);
|
||||||
|
Debug.Assert(cor20Header is not null);
|
||||||
|
metadataBaseOffset = (uint)peImage.ToFileOffset(cor20Header.Metadata.VirtualAddress);
|
||||||
|
mdReaderFactory = peImage.DataReaderFactory;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Debug.Assert(mdReaderFactory is not null);
|
||||||
|
metadataBaseOffset = 0;
|
||||||
|
}
|
||||||
|
InitializeInternal(mdReaderFactory, metadataBaseOffset);
|
||||||
|
|
||||||
|
if (tablesStream is null)
|
||||||
|
throw new BadImageFormatException("Missing MD stream");
|
||||||
|
if (isStandalonePortablePdb && pdbStream is null)
|
||||||
|
throw new BadImageFormatException("Missing #Pdb stream");
|
||||||
|
InitializeNonExistentHeaps();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates empty heap objects if they're not present in the metadata
|
||||||
|
/// </summary>
|
||||||
|
protected void InitializeNonExistentHeaps() {
|
||||||
|
if (stringsStream is null)
|
||||||
|
stringsStream = new StringsStream();
|
||||||
|
if (usStream is null)
|
||||||
|
usStream = new USStream();
|
||||||
|
if (blobStream is null)
|
||||||
|
blobStream = new BlobStream();
|
||||||
|
if (guidStream is null)
|
||||||
|
guidStream = new GuidStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called by <see cref="Initialize(DataReaderFactory)"/>
|
||||||
|
/// </summary>
|
||||||
|
protected abstract void InitializeInternal(DataReaderFactory mdReaderFactory, uint metadataBaseOffset);
|
||||||
|
|
||||||
|
public override RidList GetTypeDefRidList() => RidList.Create(1, tablesStream.TypeDefTable.Rows);
|
||||||
|
public override RidList GetExportedTypeRidList() => RidList.Create(1, tablesStream.ExportedTypeTable.Rows);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Binary searches the table for a <c>rid</c> whose key column at index
|
||||||
|
/// <paramref name="keyColIndex"/> is equal to <paramref name="key"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tableSource">Table to search</param>
|
||||||
|
/// <param name="keyColIndex">Key column index</param>
|
||||||
|
/// <param name="key">Key</param>
|
||||||
|
/// <returns>The <c>rid</c> of the found row, or 0 if none found</returns>
|
||||||
|
protected abstract uint BinarySearch(MDTable tableSource, int keyColIndex, uint key);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all rows owned by <paramref name="key"/> in table <paramref name="tableSource"/>
|
||||||
|
/// whose index is <paramref name="keyColIndex"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tableSource">Table to search</param>
|
||||||
|
/// <param name="keyColIndex">Key column index</param>
|
||||||
|
/// <param name="key">Key</param>
|
||||||
|
/// <returns>A <see cref="RidList"/> instance</returns>
|
||||||
|
protected RidList FindAllRows(MDTable tableSource, int keyColIndex, uint key) {
|
||||||
|
uint startRid = BinarySearch(tableSource, keyColIndex, key);
|
||||||
|
if (tableSource.IsInvalidRID(startRid))
|
||||||
|
return RidList.Empty;
|
||||||
|
uint endRid = startRid + 1;
|
||||||
|
var column = tableSource.TableInfo.Columns[keyColIndex];
|
||||||
|
for (; startRid > 1; startRid--) {
|
||||||
|
if (!tablesStream.TryReadColumn24(tableSource, startRid - 1, column, out uint key2))
|
||||||
|
break; // Should never happen since startRid is valid
|
||||||
|
if (key != key2)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (; endRid <= tableSource.Rows; endRid++) {
|
||||||
|
if (!tablesStream.TryReadColumn24(tableSource, endRid, column, out uint key2))
|
||||||
|
break; // Should never happen since endRid is valid
|
||||||
|
if (key != key2)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return RidList.Create(startRid, endRid - startRid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all rows owned by <paramref name="key"/> in table <paramref name="tableSource"/>
|
||||||
|
/// whose index is <paramref name="keyColIndex"/>. Should be called if <paramref name="tableSource"/>
|
||||||
|
/// could be unsorted.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tableSource">Table to search</param>
|
||||||
|
/// <param name="keyColIndex">Key column index</param>
|
||||||
|
/// <param name="key">Key</param>
|
||||||
|
/// <returns>A <see cref="RidList"/> instance</returns>
|
||||||
|
protected virtual RidList FindAllRowsUnsorted(MDTable tableSource, int keyColIndex, uint key) => FindAllRows(tableSource, keyColIndex, key);
|
||||||
|
|
||||||
|
public override RidList GetInterfaceImplRidList(uint typeDefRid) => FindAllRowsUnsorted(tablesStream.InterfaceImplTable, 0, typeDefRid);
|
||||||
|
|
||||||
|
public override RidList GetGenericParamRidList(Table table, uint rid) {
|
||||||
|
if (!CodedToken.TypeOrMethodDef.Encode(new MDToken(table, rid), out uint codedToken))
|
||||||
|
return RidList.Empty;
|
||||||
|
return FindAllRowsUnsorted(tablesStream.GenericParamTable, 2, codedToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override RidList GetGenericParamConstraintRidList(uint genericParamRid) =>
|
||||||
|
FindAllRowsUnsorted(tablesStream.GenericParamConstraintTable, 0, genericParamRid);
|
||||||
|
|
||||||
|
public override RidList GetCustomAttributeRidList(Table table, uint rid) {
|
||||||
|
if (!CodedToken.HasCustomAttribute.Encode(new MDToken(table, rid), out uint codedToken))
|
||||||
|
return RidList.Empty;
|
||||||
|
return FindAllRowsUnsorted(tablesStream.CustomAttributeTable, 0, codedToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override RidList GetDeclSecurityRidList(Table table, uint rid) {
|
||||||
|
if (!CodedToken.HasDeclSecurity.Encode(new MDToken(table, rid), out uint codedToken))
|
||||||
|
return RidList.Empty;
|
||||||
|
return FindAllRowsUnsorted(tablesStream.DeclSecurityTable, 1, codedToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override RidList GetMethodSemanticsRidList(Table table, uint rid) {
|
||||||
|
if (!CodedToken.HasSemantic.Encode(new MDToken(table, rid), out uint codedToken))
|
||||||
|
return RidList.Empty;
|
||||||
|
return FindAllRowsUnsorted(tablesStream.MethodSemanticsTable, 2, codedToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override RidList GetMethodImplRidList(uint typeDefRid) => FindAllRowsUnsorted(tablesStream.MethodImplTable, 0, typeDefRid);
|
||||||
|
|
||||||
|
public override uint GetClassLayoutRid(uint typeDefRid) {
|
||||||
|
var list = FindAllRowsUnsorted(tablesStream.ClassLayoutTable, 2, typeDefRid);
|
||||||
|
return list.Count == 0 ? 0 : list[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetFieldLayoutRid(uint fieldRid) {
|
||||||
|
var list = FindAllRowsUnsorted(tablesStream.FieldLayoutTable, 1, fieldRid);
|
||||||
|
return list.Count == 0 ? 0 : list[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetFieldMarshalRid(Table table, uint rid) {
|
||||||
|
if (!CodedToken.HasFieldMarshal.Encode(new MDToken(table, rid), out uint codedToken))
|
||||||
|
return 0;
|
||||||
|
var list = FindAllRowsUnsorted(tablesStream.FieldMarshalTable, 0, codedToken);
|
||||||
|
return list.Count == 0 ? 0 : list[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetFieldRVARid(uint fieldRid) {
|
||||||
|
var list = FindAllRowsUnsorted(tablesStream.FieldRVATable, 1, fieldRid);
|
||||||
|
return list.Count == 0 ? 0 : list[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetImplMapRid(Table table, uint rid) {
|
||||||
|
if (!CodedToken.MemberForwarded.Encode(new MDToken(table, rid), out uint codedToken))
|
||||||
|
return 0;
|
||||||
|
var list = FindAllRowsUnsorted(tablesStream.ImplMapTable, 1, codedToken);
|
||||||
|
return list.Count == 0 ? 0 : list[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetNestedClassRid(uint typeDefRid) {
|
||||||
|
var list = FindAllRowsUnsorted(tablesStream.NestedClassTable, 0, typeDefRid);
|
||||||
|
return list.Count == 0 ? 0 : list[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetEventMapRid(uint typeDefRid) {
|
||||||
|
// The EventMap and PropertyMap tables can only be trusted to be sorted if it's
|
||||||
|
// an NGen image and it's the normal #- stream. The IsSorted bit must not be used
|
||||||
|
// to check whether the tables are sorted. See coreclr: md/inc/metamodel.h / IsVerified()
|
||||||
|
if (eventMapSortedTable is null)
|
||||||
|
Interlocked.CompareExchange(ref eventMapSortedTable, new SortedTable(tablesStream.EventMapTable, 0), null);
|
||||||
|
var list = eventMapSortedTable.FindAllRows(typeDefRid);
|
||||||
|
return list.Count == 0 ? 0 : list[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetPropertyMapRid(uint typeDefRid) {
|
||||||
|
// Always unsorted, see comment in GetEventMapRid() above
|
||||||
|
if (propertyMapSortedTable is null)
|
||||||
|
Interlocked.CompareExchange(ref propertyMapSortedTable, new SortedTable(tablesStream.PropertyMapTable, 0), null);
|
||||||
|
var list = propertyMapSortedTable.FindAllRows(typeDefRid);
|
||||||
|
return list.Count == 0 ? 0 : list[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetConstantRid(Table table, uint rid) {
|
||||||
|
if (!CodedToken.HasConstant.Encode(new MDToken(table, rid), out uint codedToken))
|
||||||
|
return 0;
|
||||||
|
var list = FindAllRowsUnsorted(tablesStream.ConstantTable, 2, codedToken);
|
||||||
|
return list.Count == 0 ? 0 : list[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetOwnerTypeOfField(uint fieldRid) {
|
||||||
|
if (fieldRidToTypeDefRid is null)
|
||||||
|
InitializeInverseFieldOwnerRidList();
|
||||||
|
uint index = fieldRid - 1;
|
||||||
|
if (index >= fieldRidToTypeDefRid.LongLength)
|
||||||
|
return 0;
|
||||||
|
return fieldRidToTypeDefRid[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeInverseFieldOwnerRidList() {
|
||||||
|
if (fieldRidToTypeDefRid is not null)
|
||||||
|
return;
|
||||||
|
var newFieldRidToTypeDefRid = new uint[tablesStream.FieldTable.Rows];
|
||||||
|
var ownerList = GetTypeDefRidList();
|
||||||
|
for (int i = 0; i < ownerList.Count; i++) {
|
||||||
|
var ownerRid = ownerList[i];
|
||||||
|
var fieldList = GetFieldRidList(ownerRid);
|
||||||
|
for (int j = 0; j < fieldList.Count; j++) {
|
||||||
|
uint ridIndex = fieldList[j] - 1;
|
||||||
|
if (newFieldRidToTypeDefRid[ridIndex] != 0)
|
||||||
|
continue;
|
||||||
|
newFieldRidToTypeDefRid[ridIndex] = ownerRid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interlocked.CompareExchange(ref fieldRidToTypeDefRid, newFieldRidToTypeDefRid, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetOwnerTypeOfMethod(uint methodRid) {
|
||||||
|
if (methodRidToTypeDefRid is null)
|
||||||
|
InitializeInverseMethodOwnerRidList();
|
||||||
|
uint index = methodRid - 1;
|
||||||
|
if (index >= methodRidToTypeDefRid.LongLength)
|
||||||
|
return 0;
|
||||||
|
return methodRidToTypeDefRid[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeInverseMethodOwnerRidList() {
|
||||||
|
if (methodRidToTypeDefRid is not null)
|
||||||
|
return;
|
||||||
|
var newMethodRidToTypeDefRid = new uint[tablesStream.MethodTable.Rows];
|
||||||
|
var ownerList = GetTypeDefRidList();
|
||||||
|
for (int i = 0; i < ownerList.Count; i++) {
|
||||||
|
var ownerRid = ownerList[i];
|
||||||
|
var methodList = GetMethodRidList(ownerRid);
|
||||||
|
for (int j = 0; j < methodList.Count; j++) {
|
||||||
|
uint ridIndex = methodList[j] - 1;
|
||||||
|
if (newMethodRidToTypeDefRid[ridIndex] != 0)
|
||||||
|
continue;
|
||||||
|
newMethodRidToTypeDefRid[ridIndex] = ownerRid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interlocked.CompareExchange(ref methodRidToTypeDefRid, newMethodRidToTypeDefRid, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetOwnerTypeOfEvent(uint eventRid) {
|
||||||
|
if (eventRidToTypeDefRid is null)
|
||||||
|
InitializeInverseEventOwnerRidList();
|
||||||
|
uint index = eventRid - 1;
|
||||||
|
if (index >= eventRidToTypeDefRid.LongLength)
|
||||||
|
return 0;
|
||||||
|
return eventRidToTypeDefRid[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeInverseEventOwnerRidList() {
|
||||||
|
if (eventRidToTypeDefRid is not null)
|
||||||
|
return;
|
||||||
|
var newEventRidToTypeDefRid = new uint[tablesStream.EventTable.Rows];
|
||||||
|
var ownerList = GetTypeDefRidList();
|
||||||
|
for (int i = 0; i < ownerList.Count; i++) {
|
||||||
|
var ownerRid = ownerList[i];
|
||||||
|
var eventList = GetEventRidList(GetEventMapRid(ownerRid));
|
||||||
|
for (int j = 0; j < eventList.Count; j++) {
|
||||||
|
uint ridIndex = eventList[j] - 1;
|
||||||
|
if (newEventRidToTypeDefRid[ridIndex] != 0)
|
||||||
|
continue;
|
||||||
|
newEventRidToTypeDefRid[ridIndex] = ownerRid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interlocked.CompareExchange(ref eventRidToTypeDefRid, newEventRidToTypeDefRid, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetOwnerTypeOfProperty(uint propertyRid) {
|
||||||
|
if (propertyRidToTypeDefRid is null)
|
||||||
|
InitializeInversePropertyOwnerRidList();
|
||||||
|
uint index = propertyRid - 1;
|
||||||
|
if (index >= propertyRidToTypeDefRid.LongLength)
|
||||||
|
return 0;
|
||||||
|
return propertyRidToTypeDefRid[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeInversePropertyOwnerRidList() {
|
||||||
|
if (propertyRidToTypeDefRid is not null)
|
||||||
|
return;
|
||||||
|
var newPropertyRidToTypeDefRid = new uint[tablesStream.PropertyTable.Rows];
|
||||||
|
var ownerList = GetTypeDefRidList();
|
||||||
|
for (int i = 0; i < ownerList.Count; i++) {
|
||||||
|
var ownerRid = ownerList[i];
|
||||||
|
var propertyList = GetPropertyRidList(GetPropertyMapRid(ownerRid));
|
||||||
|
for (int j = 0; j < propertyList.Count; j++) {
|
||||||
|
uint ridIndex = propertyList[j] - 1;
|
||||||
|
if (newPropertyRidToTypeDefRid[ridIndex] != 0)
|
||||||
|
continue;
|
||||||
|
newPropertyRidToTypeDefRid[ridIndex] = ownerRid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interlocked.CompareExchange(ref propertyRidToTypeDefRid, newPropertyRidToTypeDefRid, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetOwnerOfGenericParam(uint gpRid) {
|
||||||
|
// Don't use GenericParam.Owner column. If the GP table is sorted, it's
|
||||||
|
// possible to have two blocks of GPs with the same owner. Only one of the
|
||||||
|
// blocks is the "real" generic params for the owner. Of course, rarely
|
||||||
|
// if ever will this occur, but could happen if some obfuscator has
|
||||||
|
// added it.
|
||||||
|
|
||||||
|
if (gpRidToOwnerRid is null)
|
||||||
|
InitializeInverseGenericParamOwnerRidList();
|
||||||
|
uint index = gpRid - 1;
|
||||||
|
if (index >= gpRidToOwnerRid.LongLength)
|
||||||
|
return 0;
|
||||||
|
return gpRidToOwnerRid[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeInverseGenericParamOwnerRidList() {
|
||||||
|
if (gpRidToOwnerRid is not null)
|
||||||
|
return;
|
||||||
|
var gpTable = tablesStream.GenericParamTable;
|
||||||
|
var newGpRidToOwnerRid = new uint[gpTable.Rows];
|
||||||
|
|
||||||
|
// Find all owners by reading the GenericParam.Owner column
|
||||||
|
var ownerCol = gpTable.TableInfo.Columns[2];
|
||||||
|
var ownersDict = new Dictionary<uint, bool>();
|
||||||
|
for (uint rid = 1; rid <= gpTable.Rows; rid++) {
|
||||||
|
if (!tablesStream.TryReadColumn24(gpTable, rid, ownerCol, out uint owner))
|
||||||
|
continue;
|
||||||
|
ownersDict[owner] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we have the owners, find all the generic params they own. An obfuscated
|
||||||
|
// module could have 2+ owners pointing to the same generic param row.
|
||||||
|
var owners = new List<uint>(ownersDict.Keys);
|
||||||
|
owners.Sort();
|
||||||
|
for (int i = 0; i < owners.Count; i++) {
|
||||||
|
if (!CodedToken.TypeOrMethodDef.Decode(owners[i], out uint ownerToken))
|
||||||
|
continue;
|
||||||
|
var ridList = GetGenericParamRidList(MDToken.ToTable(ownerToken), MDToken.ToRID(ownerToken));
|
||||||
|
for (int j = 0; j < ridList.Count; j++) {
|
||||||
|
uint ridIndex = ridList[j] - 1;
|
||||||
|
if (newGpRidToOwnerRid[ridIndex] != 0)
|
||||||
|
continue;
|
||||||
|
newGpRidToOwnerRid[ridIndex] = owners[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interlocked.CompareExchange(ref gpRidToOwnerRid, newGpRidToOwnerRid, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetOwnerOfGenericParamConstraint(uint gpcRid) {
|
||||||
|
// Don't use GenericParamConstraint.Owner column for the same reason
|
||||||
|
// as described in GetOwnerOfGenericParam().
|
||||||
|
|
||||||
|
if (gpcRidToOwnerRid is null)
|
||||||
|
InitializeInverseGenericParamConstraintOwnerRidList();
|
||||||
|
uint index = gpcRid - 1;
|
||||||
|
if (index >= gpcRidToOwnerRid.LongLength)
|
||||||
|
return 0;
|
||||||
|
return gpcRidToOwnerRid[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeInverseGenericParamConstraintOwnerRidList() {
|
||||||
|
if (gpcRidToOwnerRid is not null)
|
||||||
|
return;
|
||||||
|
var gpcTable = tablesStream.GenericParamConstraintTable;
|
||||||
|
var newGpcRidToOwnerRid = new uint[gpcTable.Rows];
|
||||||
|
|
||||||
|
var ownerCol = gpcTable.TableInfo.Columns[0];
|
||||||
|
var ownersDict = new Dictionary<uint, bool>();
|
||||||
|
for (uint rid = 1; rid <= gpcTable.Rows; rid++) {
|
||||||
|
if (!tablesStream.TryReadColumn24(gpcTable, rid, ownerCol, out uint owner))
|
||||||
|
continue;
|
||||||
|
ownersDict[owner] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var owners = new List<uint>(ownersDict.Keys);
|
||||||
|
owners.Sort();
|
||||||
|
for (int i = 0; i < owners.Count; i++) {
|
||||||
|
uint ownerToken = owners[i];
|
||||||
|
var ridList = GetGenericParamConstraintRidList(ownerToken);
|
||||||
|
for (int j = 0; j < ridList.Count; j++) {
|
||||||
|
uint ridIndex = ridList[j] - 1;
|
||||||
|
if (newGpcRidToOwnerRid[ridIndex] != 0)
|
||||||
|
continue;
|
||||||
|
newGpcRidToOwnerRid[ridIndex] = ownerToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interlocked.CompareExchange(ref gpcRidToOwnerRid, newGpcRidToOwnerRid, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetOwnerOfParam(uint paramRid) {
|
||||||
|
if (paramRidToOwnerRid is null)
|
||||||
|
InitializeInverseParamOwnerRidList();
|
||||||
|
uint index = paramRid - 1;
|
||||||
|
if (index >= paramRidToOwnerRid.LongLength)
|
||||||
|
return 0;
|
||||||
|
return paramRidToOwnerRid[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeInverseParamOwnerRidList() {
|
||||||
|
if (paramRidToOwnerRid is not null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var newParamRidToOwnerRid = new uint[tablesStream.ParamTable.Rows];
|
||||||
|
var table = tablesStream.MethodTable;
|
||||||
|
for (uint rid = 1; rid <= table.Rows; rid++) {
|
||||||
|
var ridList = GetParamRidList(rid);
|
||||||
|
for (int j = 0; j < ridList.Count; j++) {
|
||||||
|
uint ridIndex = ridList[j] - 1;
|
||||||
|
if (newParamRidToOwnerRid[ridIndex] != 0)
|
||||||
|
continue;
|
||||||
|
newParamRidToOwnerRid[ridIndex] = rid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interlocked.CompareExchange(ref paramRidToOwnerRid, newParamRidToOwnerRid, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override RidList GetNestedClassRidList(uint typeDefRid) {
|
||||||
|
if (typeDefRidToNestedClasses is null)
|
||||||
|
InitializeNestedClassesDictionary();
|
||||||
|
if (typeDefRidToNestedClasses.TryGetValue(typeDefRid, out var ridList))
|
||||||
|
return RidList.Create(ridList);
|
||||||
|
return RidList.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeNestedClassesDictionary() {
|
||||||
|
var table = tablesStream.NestedClassTable;
|
||||||
|
var destTable = tablesStream.TypeDefTable;
|
||||||
|
|
||||||
|
Dictionary<uint, bool> validTypeDefRids = null;
|
||||||
|
var typeDefRidList = GetTypeDefRidList();
|
||||||
|
if ((uint)typeDefRidList.Count != destTable.Rows) {
|
||||||
|
validTypeDefRids = new Dictionary<uint, bool>(typeDefRidList.Count);
|
||||||
|
for (int i = 0; i < typeDefRidList.Count; i++)
|
||||||
|
validTypeDefRids[typeDefRidList[i]] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nestedRidsDict = new Dictionary<uint, bool>((int)table.Rows);
|
||||||
|
var nestedRids = new List<uint>((int)table.Rows); // Need it so we add the rids in correct order
|
||||||
|
for (uint rid = 1; rid <= table.Rows; rid++) {
|
||||||
|
if (validTypeDefRids is not null && !validTypeDefRids.ContainsKey(rid))
|
||||||
|
continue;
|
||||||
|
if (!tablesStream.TryReadNestedClassRow(rid, out var row))
|
||||||
|
continue; // Should never happen since rid is valid
|
||||||
|
if (!destTable.IsValidRID(row.NestedClass) || !destTable.IsValidRID(row.EnclosingClass))
|
||||||
|
continue;
|
||||||
|
if (nestedRidsDict.ContainsKey(row.NestedClass))
|
||||||
|
continue;
|
||||||
|
nestedRidsDict[row.NestedClass] = true;
|
||||||
|
nestedRids.Add(row.NestedClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
var newTypeDefRidToNestedClasses = new Dictionary<uint, List<uint>>();
|
||||||
|
int count = nestedRids.Count;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
var nestedRid = nestedRids[i];
|
||||||
|
if (!tablesStream.TryReadNestedClassRow(GetNestedClassRid(nestedRid), out var row))
|
||||||
|
continue;
|
||||||
|
if (!newTypeDefRidToNestedClasses.TryGetValue(row.EnclosingClass, out var ridList))
|
||||||
|
newTypeDefRidToNestedClasses[row.EnclosingClass] = ridList = new List<uint>();
|
||||||
|
ridList.Add(nestedRid);
|
||||||
|
}
|
||||||
|
|
||||||
|
var newNonNestedTypes = new List<uint>((int)(destTable.Rows - nestedRidsDict.Count));
|
||||||
|
for (uint rid = 1; rid <= destTable.Rows; rid++) {
|
||||||
|
if (validTypeDefRids is not null && !validTypeDefRids.ContainsKey(rid))
|
||||||
|
continue;
|
||||||
|
if (nestedRidsDict.ContainsKey(rid))
|
||||||
|
continue;
|
||||||
|
newNonNestedTypes.Add(rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
Interlocked.CompareExchange(ref nonNestedTypes, new StrongBox<RidList>(RidList.Create(newNonNestedTypes)), null);
|
||||||
|
|
||||||
|
// Initialize this one last since it's tested by the callers of this method
|
||||||
|
Interlocked.CompareExchange(ref typeDefRidToNestedClasses, newTypeDefRidToNestedClasses, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override RidList GetNonNestedClassRidList() {
|
||||||
|
// Check typeDefRidToNestedClasses and not nonNestedTypes since
|
||||||
|
// InitializeNestedClassesDictionary() writes to typeDefRidToNestedClasses last.
|
||||||
|
if (typeDefRidToNestedClasses is null)
|
||||||
|
InitializeNestedClassesDictionary();
|
||||||
|
return nonNestedTypes.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override RidList GetLocalScopeRidList(uint methodRid) => FindAllRows(tablesStream.LocalScopeTable, 0, methodRid);
|
||||||
|
|
||||||
|
public override uint GetStateMachineMethodRid(uint methodRid) {
|
||||||
|
var list = FindAllRows(tablesStream.StateMachineMethodTable, 0, methodRid);
|
||||||
|
return list.Count == 0 ? 0 : list[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override RidList GetCustomDebugInformationRidList(Table table, uint rid) {
|
||||||
|
if (!CodedToken.HasCustomDebugInformation.Encode(new MDToken(table, rid), out uint codedToken))
|
||||||
|
return RidList.Empty;
|
||||||
|
return FindAllRows(tablesStream.CustomDebugInformationTable, 0, codedToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose() {
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose method
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing"><c>true</c> if called by <see cref="Dispose()"/></param>
|
||||||
|
protected virtual void Dispose(bool disposing) {
|
||||||
|
if (!disposing)
|
||||||
|
return;
|
||||||
|
peImage?.Dispose();
|
||||||
|
stringsStream?.Dispose();
|
||||||
|
usStream?.Dispose();
|
||||||
|
blobStream?.Dispose();
|
||||||
|
guidStream?.Dispose();
|
||||||
|
tablesStream?.Dispose();
|
||||||
|
var as2 = allStreams;
|
||||||
|
if (as2 is not null) {
|
||||||
|
foreach (var stream in as2)
|
||||||
|
stream?.Dispose();
|
||||||
|
}
|
||||||
|
mdReaderFactoryToDisposeLater?.Dispose();
|
||||||
|
peImage = null;
|
||||||
|
cor20Header = null;
|
||||||
|
mdHeader = null;
|
||||||
|
stringsStream = null;
|
||||||
|
usStream = null;
|
||||||
|
blobStream = null;
|
||||||
|
guidStream = null;
|
||||||
|
tablesStream = null;
|
||||||
|
allStreams = null;
|
||||||
|
fieldRidToTypeDefRid = null;
|
||||||
|
methodRidToTypeDefRid = null;
|
||||||
|
typeDefRidToNestedClasses = null;
|
||||||
|
mdReaderFactoryToDisposeLater = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,222 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using dnlib.IO;
|
||||||
|
using dnlib.PE;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Low level access to a .NET file's metadata
|
||||||
|
/// </summary>
|
||||||
|
public static class MetadataFactory {
|
||||||
|
enum MetadataType {
|
||||||
|
Unknown,
|
||||||
|
Compressed, // #~ (normal)
|
||||||
|
ENC, // #- (edit and continue)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static MetadataBase Load(string fileName, CLRRuntimeReaderKind runtime) {
|
||||||
|
IPEImage peImage = null;
|
||||||
|
try {
|
||||||
|
return Load(peImage = new PEImage(fileName), runtime);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
if (peImage is not null)
|
||||||
|
peImage.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static MetadataBase Load(byte[] data, CLRRuntimeReaderKind runtime) {
|
||||||
|
IPEImage peImage = null;
|
||||||
|
try {
|
||||||
|
return Load(peImage = new PEImage(data), runtime);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
if (peImage is not null)
|
||||||
|
peImage.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static MetadataBase Load(IntPtr addr, CLRRuntimeReaderKind runtime) {
|
||||||
|
IPEImage peImage = null;
|
||||||
|
|
||||||
|
// We don't know what layout it is. Memory is more common so try that first.
|
||||||
|
try {
|
||||||
|
return Load(peImage = new PEImage(addr, ImageLayout.Memory, true), runtime);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
if (peImage is not null)
|
||||||
|
peImage.Dispose();
|
||||||
|
peImage = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return Load(peImage = new PEImage(addr, ImageLayout.File, true), runtime);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
if (peImage is not null)
|
||||||
|
peImage.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static MetadataBase Load(IntPtr addr, ImageLayout imageLayout, CLRRuntimeReaderKind runtime) {
|
||||||
|
IPEImage peImage = null;
|
||||||
|
try {
|
||||||
|
return Load(peImage = new PEImage(addr, imageLayout, true), runtime);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
if (peImage is not null)
|
||||||
|
peImage.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static MetadataBase Load(IPEImage peImage, CLRRuntimeReaderKind runtime) => Create(peImage, runtime, true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a <see cref="Metadata"/> instance
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="peImage">The PE image</param>
|
||||||
|
/// <returns>A new <see cref="Metadata"/> instance</returns>
|
||||||
|
public static Metadata CreateMetadata(IPEImage peImage) => CreateMetadata(peImage, CLRRuntimeReaderKind.CLR);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a <see cref="Metadata"/> instance
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="peImage">The PE image</param>
|
||||||
|
/// <param name="runtime">Runtime reader kind</param>
|
||||||
|
/// <returns>A new <see cref="Metadata"/> instance</returns>
|
||||||
|
public static Metadata CreateMetadata(IPEImage peImage, CLRRuntimeReaderKind runtime) => Create(peImage, runtime, true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a <see cref="Metadata"/> instance
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="peImage">The PE image</param>
|
||||||
|
/// <param name="verify"><c>true</c> if we should verify that it's a .NET PE file</param>
|
||||||
|
/// <returns>A new <see cref="Metadata"/> instance</returns>
|
||||||
|
public static Metadata CreateMetadata(IPEImage peImage, bool verify) => CreateMetadata(peImage, CLRRuntimeReaderKind.CLR, verify);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a <see cref="Metadata"/> instance
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="peImage">The PE image</param>
|
||||||
|
/// <param name="runtime">Runtime reader kind</param>
|
||||||
|
/// <param name="verify"><c>true</c> if we should verify that it's a .NET PE file</param>
|
||||||
|
/// <returns>A new <see cref="Metadata"/> instance</returns>
|
||||||
|
public static Metadata CreateMetadata(IPEImage peImage, CLRRuntimeReaderKind runtime, bool verify) => Create(peImage, runtime, verify);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a <see cref="MetadataBase"/> instance
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="peImage">The PE image</param>
|
||||||
|
/// <param name="runtime">Runtime reader kind</param>
|
||||||
|
/// <param name="verify"><c>true</c> if we should verify that it's a .NET PE file</param>
|
||||||
|
/// <returns>A new <see cref="MetadataBase"/> instance</returns>
|
||||||
|
static MetadataBase Create(IPEImage peImage, CLRRuntimeReaderKind runtime, bool verify) {
|
||||||
|
MetadataBase md = null;
|
||||||
|
try {
|
||||||
|
var dotNetDir = peImage.ImageNTHeaders.OptionalHeader.DataDirectories[14];
|
||||||
|
// Mono doesn't check that the Size field is >= 0x48
|
||||||
|
if (dotNetDir.VirtualAddress == 0)
|
||||||
|
throw new BadImageFormatException(".NET data directory RVA is 0");
|
||||||
|
var cor20HeaderReader = peImage.CreateReader(dotNetDir.VirtualAddress, 0x48);
|
||||||
|
var cor20Header = new ImageCor20Header(ref cor20HeaderReader, verify && runtime == CLRRuntimeReaderKind.CLR);
|
||||||
|
if (cor20Header.Metadata.VirtualAddress == 0)
|
||||||
|
throw new BadImageFormatException(".NET metadata RVA is 0");
|
||||||
|
var mdRva = cor20Header.Metadata.VirtualAddress;
|
||||||
|
// Don't use the size field, Mono ignores it. Create a reader that can read to EOF.
|
||||||
|
var mdHeaderReader = peImage.CreateReader(mdRva);
|
||||||
|
var mdHeader = new MetadataHeader(ref mdHeaderReader, runtime, verify);
|
||||||
|
if (verify) {
|
||||||
|
foreach (var sh in mdHeader.StreamHeaders) {
|
||||||
|
if ((ulong)sh.Offset + sh.StreamSize > mdHeaderReader.EndOffset)
|
||||||
|
throw new BadImageFormatException("Invalid stream header");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
md = GetMetadataType(mdHeader.StreamHeaders, runtime) switch {
|
||||||
|
MetadataType.Compressed => new CompressedMetadata(peImage, cor20Header, mdHeader, runtime),
|
||||||
|
MetadataType.ENC => new ENCMetadata(peImage, cor20Header, mdHeader, runtime),
|
||||||
|
_ => throw new BadImageFormatException("No #~ or #- stream found"),
|
||||||
|
};
|
||||||
|
md.Initialize(null);
|
||||||
|
|
||||||
|
return md;
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
if (md is not null)
|
||||||
|
md.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a standalone portable PDB <see cref="MetadataBase"/> instance
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mdReaderFactory">Metadata stream</param>
|
||||||
|
/// <param name="verify"><c>true</c> if we should verify that it's a .NET PE file</param>
|
||||||
|
/// <returns>A new <see cref="MetadataBase"/> instance</returns>
|
||||||
|
internal static MetadataBase CreateStandalonePortablePDB(DataReaderFactory mdReaderFactory, bool verify) {
|
||||||
|
const CLRRuntimeReaderKind runtime = CLRRuntimeReaderKind.CLR;
|
||||||
|
MetadataBase md = null;
|
||||||
|
try {
|
||||||
|
var reader = mdReaderFactory.CreateReader();
|
||||||
|
var mdHeader = new MetadataHeader(ref reader, runtime, verify);
|
||||||
|
if (verify) {
|
||||||
|
foreach (var sh in mdHeader.StreamHeaders) {
|
||||||
|
if (sh.Offset + sh.StreamSize < sh.Offset || sh.Offset + sh.StreamSize > reader.Length)
|
||||||
|
throw new BadImageFormatException("Invalid stream header");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
md = GetMetadataType(mdHeader.StreamHeaders, runtime) switch {
|
||||||
|
MetadataType.Compressed => new CompressedMetadata(mdHeader, true, runtime),
|
||||||
|
MetadataType.ENC => new ENCMetadata(mdHeader, true, runtime),
|
||||||
|
_ => throw new BadImageFormatException("No #~ or #- stream found"),
|
||||||
|
};
|
||||||
|
md.Initialize(mdReaderFactory);
|
||||||
|
|
||||||
|
return md;
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
md?.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static MetadataType GetMetadataType(IList<StreamHeader> streamHeaders, CLRRuntimeReaderKind runtime) {
|
||||||
|
MetadataType? mdType = null;
|
||||||
|
if (runtime == CLRRuntimeReaderKind.CLR) {
|
||||||
|
foreach (var sh in streamHeaders) {
|
||||||
|
if (mdType is null) {
|
||||||
|
if (sh.Name == "#~")
|
||||||
|
mdType = MetadataType.Compressed;
|
||||||
|
else if (sh.Name == "#-")
|
||||||
|
mdType = MetadataType.ENC;
|
||||||
|
}
|
||||||
|
if (sh.Name == "#Schema")
|
||||||
|
mdType = MetadataType.ENC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (runtime == CLRRuntimeReaderKind.Mono) {
|
||||||
|
foreach (var sh in streamHeaders) {
|
||||||
|
if (sh.Name == "#~")
|
||||||
|
mdType = MetadataType.Compressed;
|
||||||
|
else if (sh.Name == "#-") {
|
||||||
|
mdType = MetadataType.ENC;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(runtime));
|
||||||
|
if (mdType is null)
|
||||||
|
return MetadataType.Unknown;
|
||||||
|
return mdType.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using dnlib.IO;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the .NET metadata header
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks><c>IMAGE_COR20_HEADER.Metadata</c> points to this header</remarks>
|
||||||
|
public sealed class MetadataHeader : FileSection {
|
||||||
|
readonly uint signature;
|
||||||
|
readonly ushort majorVersion;
|
||||||
|
readonly ushort minorVersion;
|
||||||
|
readonly uint reserved1;
|
||||||
|
readonly uint stringLength;
|
||||||
|
readonly string versionString;
|
||||||
|
readonly FileOffset offset2ndPart;
|
||||||
|
readonly StorageFlags flags;
|
||||||
|
readonly byte reserved2;
|
||||||
|
readonly ushort streams;
|
||||||
|
readonly IList<StreamHeader> streamHeaders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the signature (should be 0x424A5342)
|
||||||
|
/// </summary>
|
||||||
|
public uint Signature => signature;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the major version
|
||||||
|
/// </summary>
|
||||||
|
public ushort MajorVersion => majorVersion;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the minor version
|
||||||
|
/// </summary>
|
||||||
|
public ushort MinorVersion => minorVersion;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the reserved dword (pointer to extra header data)
|
||||||
|
/// </summary>
|
||||||
|
public uint Reserved1 => reserved1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the version string length value
|
||||||
|
/// </summary>
|
||||||
|
public uint StringLength => stringLength;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the version string
|
||||||
|
/// </summary>
|
||||||
|
public string VersionString => versionString;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the offset of <c>STORAGEHEADER</c>
|
||||||
|
/// </summary>
|
||||||
|
public FileOffset StorageHeaderOffset => offset2ndPart;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the flags (reserved)
|
||||||
|
/// </summary>
|
||||||
|
public StorageFlags Flags => flags;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the reserved byte (padding)
|
||||||
|
/// </summary>
|
||||||
|
public byte Reserved2 => reserved2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the number of streams
|
||||||
|
/// </summary>
|
||||||
|
public ushort Streams => streams;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns all stream headers
|
||||||
|
/// </summary>
|
||||||
|
public IList<StreamHeader> StreamHeaders => streamHeaders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">PE file reader pointing to the start of this section</param>
|
||||||
|
/// <param name="verify">Verify section</param>
|
||||||
|
/// <exception cref="BadImageFormatException">Thrown if verification fails</exception>
|
||||||
|
public MetadataHeader(ref DataReader reader, bool verify)
|
||||||
|
: this(ref reader, CLRRuntimeReaderKind.CLR, verify) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">PE file reader pointing to the start of this section</param>
|
||||||
|
/// <param name="runtime">Runtime reader kind</param>
|
||||||
|
/// <param name="verify">Verify section</param>
|
||||||
|
/// <exception cref="BadImageFormatException">Thrown if verification fails</exception>
|
||||||
|
public MetadataHeader(ref DataReader reader, CLRRuntimeReaderKind runtime, bool verify) {
|
||||||
|
SetStartOffset(ref reader);
|
||||||
|
signature = reader.ReadUInt32();
|
||||||
|
if (verify && signature != 0x424A5342)
|
||||||
|
throw new BadImageFormatException("Invalid metadata header signature");
|
||||||
|
majorVersion = reader.ReadUInt16();
|
||||||
|
minorVersion = reader.ReadUInt16();
|
||||||
|
reserved1 = reader.ReadUInt32();
|
||||||
|
stringLength = reader.ReadUInt32();
|
||||||
|
versionString = ReadString(ref reader, stringLength, runtime);
|
||||||
|
offset2ndPart = (FileOffset)reader.CurrentOffset;
|
||||||
|
flags = (StorageFlags)reader.ReadByte();
|
||||||
|
reserved2 = reader.ReadByte();
|
||||||
|
streams = reader.ReadUInt16();
|
||||||
|
streamHeaders = new StreamHeader[streams];
|
||||||
|
for (int i = 0; i < streamHeaders.Count; i++) {
|
||||||
|
// Mono doesn't verify all of these so we can't either
|
||||||
|
var sh = new StreamHeader(ref reader, throwOnError: false, verify, runtime, out bool failedVerification);
|
||||||
|
if (failedVerification || (ulong)sh.Offset + sh.StreamSize > reader.EndOffset)
|
||||||
|
sh = new StreamHeader(0, 0, "<invalid>");
|
||||||
|
streamHeaders[i] = sh;
|
||||||
|
}
|
||||||
|
SetEndoffset(ref reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
static string ReadString(ref DataReader reader, uint maxLength, CLRRuntimeReaderKind runtime) {
|
||||||
|
ulong endOffset = (ulong)reader.CurrentOffset + maxLength;
|
||||||
|
if (runtime == CLRRuntimeReaderKind.Mono)
|
||||||
|
endOffset = (endOffset + 3) / 4 * 4;
|
||||||
|
if (endOffset > reader.EndOffset)
|
||||||
|
throw new BadImageFormatException("Invalid MD version string");
|
||||||
|
var utf8Bytes = new byte[maxLength];
|
||||||
|
uint i;
|
||||||
|
for (i = 0; i < maxLength; i++) {
|
||||||
|
byte b = reader.ReadByte();
|
||||||
|
if (b == 0)
|
||||||
|
break;
|
||||||
|
utf8Bytes[i] = b;
|
||||||
|
}
|
||||||
|
reader.CurrentOffset = (uint)endOffset;
|
||||||
|
return Encoding.UTF8.GetString(utf8Bytes, 0, (int)i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using dnlib.IO;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// #Pdb stream
|
||||||
|
/// </summary>
|
||||||
|
public sealed class PdbStream : HeapStream {
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the PDB id
|
||||||
|
/// </summary>
|
||||||
|
public byte[] Id { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the entry point token or 0
|
||||||
|
/// </summary>
|
||||||
|
public MDToken EntryPoint { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the referenced type system tables in the PE metadata file
|
||||||
|
/// </summary>
|
||||||
|
public ulong ReferencedTypeSystemTables { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all type system table rows. This array has exactly 64 elements.
|
||||||
|
/// </summary>
|
||||||
|
public uint[] TypeSystemTableRows { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public PdbStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader)
|
||||||
|
: base(mdReaderFactory, metadataBaseOffset, streamHeader) {
|
||||||
|
var reader = CreateReader();
|
||||||
|
Id = reader.ReadBytes(20);
|
||||||
|
EntryPoint = new MDToken(reader.ReadUInt32());
|
||||||
|
var tables = reader.ReadUInt64();
|
||||||
|
ReferencedTypeSystemTables = tables;
|
||||||
|
var rows = new uint[64];
|
||||||
|
for (int i = 0; i < rows.Length; i++, tables >>= 1) {
|
||||||
|
if (((uint)tables & 1) != 0)
|
||||||
|
rows[i] = reader.ReadUInt32();
|
||||||
|
}
|
||||||
|
TypeSystemTableRows = rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,550 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#pragma warning disable 1591 // XML doc comments
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Equality comparer for all raw rows
|
||||||
|
/// </summary>
|
||||||
|
public sealed class RawRowEqualityComparer : IEqualityComparer<RawModuleRow>,
|
||||||
|
IEqualityComparer<RawTypeRefRow>, IEqualityComparer<RawTypeDefRow>,
|
||||||
|
IEqualityComparer<RawFieldPtrRow>, IEqualityComparer<RawFieldRow>,
|
||||||
|
IEqualityComparer<RawMethodPtrRow>, IEqualityComparer<RawMethodRow>,
|
||||||
|
IEqualityComparer<RawParamPtrRow>, IEqualityComparer<RawParamRow>,
|
||||||
|
IEqualityComparer<RawInterfaceImplRow>, IEqualityComparer<RawMemberRefRow>,
|
||||||
|
IEqualityComparer<RawConstantRow>, IEqualityComparer<RawCustomAttributeRow>,
|
||||||
|
IEqualityComparer<RawFieldMarshalRow>, IEqualityComparer<RawDeclSecurityRow>,
|
||||||
|
IEqualityComparer<RawClassLayoutRow>, IEqualityComparer<RawFieldLayoutRow>,
|
||||||
|
IEqualityComparer<RawStandAloneSigRow>, IEqualityComparer<RawEventMapRow>,
|
||||||
|
IEqualityComparer<RawEventPtrRow>, IEqualityComparer<RawEventRow>,
|
||||||
|
IEqualityComparer<RawPropertyMapRow>, IEqualityComparer<RawPropertyPtrRow>,
|
||||||
|
IEqualityComparer<RawPropertyRow>, IEqualityComparer<RawMethodSemanticsRow>,
|
||||||
|
IEqualityComparer<RawMethodImplRow>, IEqualityComparer<RawModuleRefRow>,
|
||||||
|
IEqualityComparer<RawTypeSpecRow>, IEqualityComparer<RawImplMapRow>,
|
||||||
|
IEqualityComparer<RawFieldRVARow>, IEqualityComparer<RawENCLogRow>,
|
||||||
|
IEqualityComparer<RawENCMapRow>, IEqualityComparer<RawAssemblyRow>,
|
||||||
|
IEqualityComparer<RawAssemblyProcessorRow>, IEqualityComparer<RawAssemblyOSRow>,
|
||||||
|
IEqualityComparer<RawAssemblyRefRow>, IEqualityComparer<RawAssemblyRefProcessorRow>,
|
||||||
|
IEqualityComparer<RawAssemblyRefOSRow>, IEqualityComparer<RawFileRow>,
|
||||||
|
IEqualityComparer<RawExportedTypeRow>, IEqualityComparer<RawManifestResourceRow>,
|
||||||
|
IEqualityComparer<RawNestedClassRow>, IEqualityComparer<RawGenericParamRow>,
|
||||||
|
IEqualityComparer<RawMethodSpecRow>, IEqualityComparer<RawGenericParamConstraintRow>,
|
||||||
|
IEqualityComparer<RawDocumentRow>, IEqualityComparer<RawMethodDebugInformationRow>,
|
||||||
|
IEqualityComparer<RawLocalScopeRow>, IEqualityComparer<RawLocalVariableRow>,
|
||||||
|
IEqualityComparer<RawLocalConstantRow>, IEqualityComparer<RawImportScopeRow>,
|
||||||
|
IEqualityComparer<RawStateMachineMethodRow>, IEqualityComparer<RawCustomDebugInformationRow> {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default instance
|
||||||
|
/// </summary>
|
||||||
|
public static readonly RawRowEqualityComparer Instance = new RawRowEqualityComparer();
|
||||||
|
|
||||||
|
static int rol(uint val, int shift) => (int)((val << shift) | (val >> (32 - shift)));
|
||||||
|
|
||||||
|
public bool Equals(RawModuleRow x, RawModuleRow y) =>
|
||||||
|
x.Generation == y.Generation &&
|
||||||
|
x.Name == y.Name &&
|
||||||
|
x.Mvid == y.Mvid &&
|
||||||
|
x.EncId == y.EncId &&
|
||||||
|
x.EncBaseId == y.EncBaseId;
|
||||||
|
|
||||||
|
public int GetHashCode(RawModuleRow obj) =>
|
||||||
|
obj.Generation +
|
||||||
|
rol(obj.Name, 3) +
|
||||||
|
rol(obj.Mvid, 7) +
|
||||||
|
rol(obj.EncId, 11) +
|
||||||
|
rol(obj.EncBaseId, 15);
|
||||||
|
|
||||||
|
public bool Equals(RawTypeRefRow x, RawTypeRefRow y) =>
|
||||||
|
x.ResolutionScope == y.ResolutionScope &&
|
||||||
|
x.Name == y.Name &&
|
||||||
|
x.Namespace == y.Namespace;
|
||||||
|
|
||||||
|
public int GetHashCode(RawTypeRefRow obj) =>
|
||||||
|
(int)obj.ResolutionScope +
|
||||||
|
rol(obj.Name, 3) +
|
||||||
|
rol(obj.Namespace, 7);
|
||||||
|
|
||||||
|
public bool Equals(RawTypeDefRow x, RawTypeDefRow y) =>
|
||||||
|
x.Flags == y.Flags &&
|
||||||
|
x.Name == y.Name &&
|
||||||
|
x.Namespace == y.Namespace &&
|
||||||
|
x.Extends == y.Extends &&
|
||||||
|
x.FieldList == y.FieldList &&
|
||||||
|
x.MethodList == y.MethodList;
|
||||||
|
|
||||||
|
public int GetHashCode(RawTypeDefRow obj) =>
|
||||||
|
(int)obj.Flags +
|
||||||
|
rol(obj.Name, 3) +
|
||||||
|
rol(obj.Namespace, 7) +
|
||||||
|
rol(obj.Extends, 11) +
|
||||||
|
rol(obj.FieldList, 15) +
|
||||||
|
rol(obj.MethodList, 19);
|
||||||
|
|
||||||
|
public bool Equals(RawFieldPtrRow x, RawFieldPtrRow y) => x.Field == y.Field;
|
||||||
|
|
||||||
|
public int GetHashCode(RawFieldPtrRow obj) => (int)obj.Field;
|
||||||
|
|
||||||
|
public bool Equals(RawFieldRow x, RawFieldRow y) =>
|
||||||
|
x.Flags == y.Flags &&
|
||||||
|
x.Name == y.Name &&
|
||||||
|
x.Signature == y.Signature;
|
||||||
|
|
||||||
|
public int GetHashCode(RawFieldRow obj) =>
|
||||||
|
(int)obj.Flags +
|
||||||
|
rol(obj.Name, 3) +
|
||||||
|
rol(obj.Signature, 7);
|
||||||
|
|
||||||
|
public bool Equals(RawMethodPtrRow x, RawMethodPtrRow y) => x.Method == y.Method;
|
||||||
|
|
||||||
|
public int GetHashCode(RawMethodPtrRow obj) => (int)obj.Method;
|
||||||
|
|
||||||
|
public bool Equals(RawMethodRow x, RawMethodRow y) =>
|
||||||
|
x.RVA == y.RVA &&
|
||||||
|
x.ImplFlags == y.ImplFlags &&
|
||||||
|
x.Flags == y.Flags &&
|
||||||
|
x.Name == y.Name &&
|
||||||
|
x.Signature == y.Signature &&
|
||||||
|
x.ParamList == y.ParamList;
|
||||||
|
|
||||||
|
public int GetHashCode(RawMethodRow obj) =>
|
||||||
|
(int)obj.RVA +
|
||||||
|
rol(obj.ImplFlags, 3) +
|
||||||
|
rol(obj.Flags, 7) +
|
||||||
|
rol(obj.Name, 11) +
|
||||||
|
rol(obj.Signature, 15) +
|
||||||
|
rol(obj.ParamList, 19);
|
||||||
|
|
||||||
|
public bool Equals(RawParamPtrRow x, RawParamPtrRow y) => x.Param == y.Param;
|
||||||
|
|
||||||
|
public int GetHashCode(RawParamPtrRow obj) => (int)obj.Param;
|
||||||
|
|
||||||
|
public bool Equals(RawParamRow x, RawParamRow y) =>
|
||||||
|
x.Flags == y.Flags &&
|
||||||
|
x.Sequence == y.Sequence &&
|
||||||
|
x.Name == y.Name;
|
||||||
|
|
||||||
|
public int GetHashCode(RawParamRow obj) =>
|
||||||
|
(int)obj.Flags +
|
||||||
|
rol(obj.Sequence, 3) +
|
||||||
|
rol(obj.Name, 7);
|
||||||
|
|
||||||
|
public bool Equals(RawInterfaceImplRow x, RawInterfaceImplRow y) =>
|
||||||
|
x.Class == y.Class &&
|
||||||
|
x.Interface == y.Interface;
|
||||||
|
|
||||||
|
public int GetHashCode(RawInterfaceImplRow obj) =>
|
||||||
|
(int)obj.Class +
|
||||||
|
rol(obj.Interface, 3);
|
||||||
|
|
||||||
|
public bool Equals(RawMemberRefRow x, RawMemberRefRow y) =>
|
||||||
|
x.Class == y.Class &&
|
||||||
|
x.Name == y.Name &&
|
||||||
|
x.Signature == y.Signature;
|
||||||
|
|
||||||
|
public int GetHashCode(RawMemberRefRow obj) =>
|
||||||
|
(int)obj.Class +
|
||||||
|
rol(obj.Name, 3) +
|
||||||
|
rol(obj.Signature, 7);
|
||||||
|
|
||||||
|
public bool Equals(RawConstantRow x, RawConstantRow y) =>
|
||||||
|
x.Type == y.Type &&
|
||||||
|
x.Padding == y.Padding &&
|
||||||
|
x.Parent == y.Parent &&
|
||||||
|
x.Value == y.Value;
|
||||||
|
|
||||||
|
public int GetHashCode(RawConstantRow obj) =>
|
||||||
|
(int)obj.Type +
|
||||||
|
rol(obj.Padding, 3) +
|
||||||
|
rol(obj.Parent, 7) +
|
||||||
|
rol(obj.Value, 11);
|
||||||
|
|
||||||
|
public bool Equals(RawCustomAttributeRow x, RawCustomAttributeRow y) =>
|
||||||
|
x.Parent == y.Parent &&
|
||||||
|
x.Type == y.Type &&
|
||||||
|
x.Value == y.Value;
|
||||||
|
|
||||||
|
public int GetHashCode(RawCustomAttributeRow obj) =>
|
||||||
|
(int)obj.Parent +
|
||||||
|
rol(obj.Type, 3) +
|
||||||
|
rol(obj.Value, 7);
|
||||||
|
|
||||||
|
public bool Equals(RawFieldMarshalRow x, RawFieldMarshalRow y) =>
|
||||||
|
x.Parent == y.Parent &&
|
||||||
|
x.NativeType == y.NativeType;
|
||||||
|
|
||||||
|
public int GetHashCode(RawFieldMarshalRow obj) =>
|
||||||
|
(int)obj.Parent +
|
||||||
|
rol(obj.NativeType, 3);
|
||||||
|
|
||||||
|
public bool Equals(RawDeclSecurityRow x, RawDeclSecurityRow y) =>
|
||||||
|
x.Action == y.Action &&
|
||||||
|
x.Parent == y.Parent &&
|
||||||
|
x.PermissionSet == y.PermissionSet;
|
||||||
|
|
||||||
|
public int GetHashCode(RawDeclSecurityRow obj) =>
|
||||||
|
(int)obj.Action +
|
||||||
|
rol(obj.Parent, 3) +
|
||||||
|
rol(obj.PermissionSet, 7);
|
||||||
|
|
||||||
|
public bool Equals(RawClassLayoutRow x, RawClassLayoutRow y) =>
|
||||||
|
x.PackingSize == y.PackingSize &&
|
||||||
|
x.ClassSize == y.ClassSize &&
|
||||||
|
x.Parent == y.Parent;
|
||||||
|
|
||||||
|
public int GetHashCode(RawClassLayoutRow obj) =>
|
||||||
|
(int)obj.PackingSize +
|
||||||
|
rol(obj.ClassSize, 3) +
|
||||||
|
rol(obj.Parent, 7);
|
||||||
|
|
||||||
|
public bool Equals(RawFieldLayoutRow x, RawFieldLayoutRow y) =>
|
||||||
|
x.OffSet == y.OffSet &&
|
||||||
|
x.Field == y.Field;
|
||||||
|
|
||||||
|
public int GetHashCode(RawFieldLayoutRow obj) =>
|
||||||
|
(int)obj.OffSet +
|
||||||
|
rol(obj.Field, 3);
|
||||||
|
|
||||||
|
public bool Equals(RawStandAloneSigRow x, RawStandAloneSigRow y) => x.Signature == y.Signature;
|
||||||
|
|
||||||
|
public int GetHashCode(RawStandAloneSigRow obj) => (int)obj.Signature;
|
||||||
|
|
||||||
|
public bool Equals(RawEventMapRow x, RawEventMapRow y) =>
|
||||||
|
x.Parent == y.Parent &&
|
||||||
|
x.EventList == y.EventList;
|
||||||
|
|
||||||
|
public int GetHashCode(RawEventMapRow obj) =>
|
||||||
|
(int)obj.Parent +
|
||||||
|
rol(obj.EventList, 3);
|
||||||
|
|
||||||
|
public bool Equals(RawEventPtrRow x, RawEventPtrRow y) => x.Event == y.Event;
|
||||||
|
|
||||||
|
public int GetHashCode(RawEventPtrRow obj) => (int)obj.Event;
|
||||||
|
|
||||||
|
public bool Equals(RawEventRow x, RawEventRow y) =>
|
||||||
|
x.EventFlags == y.EventFlags &&
|
||||||
|
x.Name == y.Name &&
|
||||||
|
x.EventType == y.EventType;
|
||||||
|
|
||||||
|
public int GetHashCode(RawEventRow obj) =>
|
||||||
|
(int)obj.EventFlags +
|
||||||
|
rol(obj.Name, 3) +
|
||||||
|
rol(obj.EventType, 7);
|
||||||
|
|
||||||
|
public bool Equals(RawPropertyMapRow x, RawPropertyMapRow y) =>
|
||||||
|
x.Parent == y.Parent &&
|
||||||
|
x.PropertyList == y.PropertyList;
|
||||||
|
|
||||||
|
public int GetHashCode(RawPropertyMapRow obj) =>
|
||||||
|
(int)obj.Parent +
|
||||||
|
rol(obj.PropertyList, 3);
|
||||||
|
|
||||||
|
public bool Equals(RawPropertyPtrRow x, RawPropertyPtrRow y) => x.Property == y.Property;
|
||||||
|
|
||||||
|
public int GetHashCode(RawPropertyPtrRow obj) => (int)obj.Property;
|
||||||
|
|
||||||
|
public bool Equals(RawPropertyRow x, RawPropertyRow y) =>
|
||||||
|
x.PropFlags == y.PropFlags &&
|
||||||
|
x.Name == y.Name &&
|
||||||
|
x.Type == y.Type;
|
||||||
|
|
||||||
|
public int GetHashCode(RawPropertyRow obj) =>
|
||||||
|
(int)obj.PropFlags +
|
||||||
|
rol(obj.Name, 3) +
|
||||||
|
rol(obj.Type, 7);
|
||||||
|
|
||||||
|
public bool Equals(RawMethodSemanticsRow x, RawMethodSemanticsRow y) =>
|
||||||
|
x.Semantic == y.Semantic &&
|
||||||
|
x.Method == y.Method &&
|
||||||
|
x.Association == y.Association;
|
||||||
|
|
||||||
|
public int GetHashCode(RawMethodSemanticsRow obj) =>
|
||||||
|
(int)obj.Semantic +
|
||||||
|
rol(obj.Method, 3) +
|
||||||
|
rol(obj.Association, 7);
|
||||||
|
|
||||||
|
public bool Equals(RawMethodImplRow x, RawMethodImplRow y) =>
|
||||||
|
x.Class == y.Class &&
|
||||||
|
x.MethodBody == y.MethodBody &&
|
||||||
|
x.MethodDeclaration == y.MethodDeclaration;
|
||||||
|
|
||||||
|
public int GetHashCode(RawMethodImplRow obj) =>
|
||||||
|
(int)obj.Class +
|
||||||
|
rol(obj.MethodBody, 3) +
|
||||||
|
rol(obj.MethodDeclaration, 7);
|
||||||
|
|
||||||
|
public bool Equals(RawModuleRefRow x, RawModuleRefRow y) => x.Name == y.Name;
|
||||||
|
|
||||||
|
public int GetHashCode(RawModuleRefRow obj) => (int)obj.Name;
|
||||||
|
|
||||||
|
public bool Equals(RawTypeSpecRow x, RawTypeSpecRow y) => x.Signature == y.Signature;
|
||||||
|
|
||||||
|
public int GetHashCode(RawTypeSpecRow obj) => (int)obj.Signature;
|
||||||
|
|
||||||
|
public bool Equals(RawImplMapRow x, RawImplMapRow y) =>
|
||||||
|
x.MappingFlags == y.MappingFlags &&
|
||||||
|
x.MemberForwarded == y.MemberForwarded &&
|
||||||
|
x.ImportName == y.ImportName &&
|
||||||
|
x.ImportScope == y.ImportScope;
|
||||||
|
|
||||||
|
public int GetHashCode(RawImplMapRow obj) =>
|
||||||
|
(int)obj.MappingFlags +
|
||||||
|
rol(obj.MemberForwarded, 3) +
|
||||||
|
rol(obj.ImportName, 7) +
|
||||||
|
rol(obj.ImportScope, 11);
|
||||||
|
|
||||||
|
public bool Equals(RawFieldRVARow x, RawFieldRVARow y) =>
|
||||||
|
x.RVA == y.RVA &&
|
||||||
|
x.Field == y.Field;
|
||||||
|
|
||||||
|
public int GetHashCode(RawFieldRVARow obj) =>
|
||||||
|
(int)obj.RVA +
|
||||||
|
rol(obj.Field, 3);
|
||||||
|
|
||||||
|
public bool Equals(RawENCLogRow x, RawENCLogRow y) =>
|
||||||
|
x.Token == y.Token &&
|
||||||
|
x.FuncCode == y.FuncCode;
|
||||||
|
|
||||||
|
public int GetHashCode(RawENCLogRow obj) =>
|
||||||
|
(int)obj.Token +
|
||||||
|
rol(obj.FuncCode, 3);
|
||||||
|
|
||||||
|
public bool Equals(RawENCMapRow x, RawENCMapRow y) => x.Token == y.Token;
|
||||||
|
|
||||||
|
public int GetHashCode(RawENCMapRow obj) => (int)obj.Token;
|
||||||
|
|
||||||
|
public bool Equals(RawAssemblyRow x, RawAssemblyRow y) =>
|
||||||
|
x.HashAlgId == y.HashAlgId &&
|
||||||
|
x.MajorVersion == y.MajorVersion &&
|
||||||
|
x.MinorVersion == y.MinorVersion &&
|
||||||
|
x.BuildNumber == y.BuildNumber &&
|
||||||
|
x.RevisionNumber == y.RevisionNumber &&
|
||||||
|
x.Flags == y.Flags &&
|
||||||
|
x.PublicKey == y.PublicKey &&
|
||||||
|
x.Name == y.Name &&
|
||||||
|
x.Locale == y.Locale;
|
||||||
|
|
||||||
|
public int GetHashCode(RawAssemblyRow obj) =>
|
||||||
|
(int)obj.HashAlgId +
|
||||||
|
rol(obj.MajorVersion, 3) +
|
||||||
|
rol(obj.MinorVersion, 7) +
|
||||||
|
rol(obj.BuildNumber, 11) +
|
||||||
|
rol(obj.RevisionNumber, 15) +
|
||||||
|
rol(obj.Flags, 19) +
|
||||||
|
rol(obj.PublicKey, 23) +
|
||||||
|
rol(obj.Name, 27) +
|
||||||
|
rol(obj.Locale, 31);
|
||||||
|
|
||||||
|
public bool Equals(RawAssemblyProcessorRow x, RawAssemblyProcessorRow y) => x.Processor == y.Processor;
|
||||||
|
|
||||||
|
public int GetHashCode(RawAssemblyProcessorRow obj) => (int)obj.Processor;
|
||||||
|
|
||||||
|
public bool Equals(RawAssemblyOSRow x, RawAssemblyOSRow y) =>
|
||||||
|
x.OSPlatformId == y.OSPlatformId &&
|
||||||
|
x.OSMajorVersion == y.OSMajorVersion &&
|
||||||
|
x.OSMinorVersion == y.OSMinorVersion;
|
||||||
|
|
||||||
|
public int GetHashCode(RawAssemblyOSRow obj) =>
|
||||||
|
(int)obj.OSPlatformId +
|
||||||
|
rol(obj.OSMajorVersion, 3) +
|
||||||
|
rol(obj.OSMinorVersion, 7);
|
||||||
|
|
||||||
|
public bool Equals(RawAssemblyRefRow x, RawAssemblyRefRow y) =>
|
||||||
|
x.MajorVersion == y.MajorVersion &&
|
||||||
|
x.MinorVersion == y.MinorVersion &&
|
||||||
|
x.BuildNumber == y.BuildNumber &&
|
||||||
|
x.RevisionNumber == y.RevisionNumber &&
|
||||||
|
x.Flags == y.Flags &&
|
||||||
|
x.PublicKeyOrToken == y.PublicKeyOrToken &&
|
||||||
|
x.Name == y.Name &&
|
||||||
|
x.Locale == y.Locale &&
|
||||||
|
x.HashValue == y.HashValue;
|
||||||
|
|
||||||
|
public int GetHashCode(RawAssemblyRefRow obj) =>
|
||||||
|
(int)obj.MajorVersion +
|
||||||
|
rol(obj.MinorVersion, 3) +
|
||||||
|
rol(obj.BuildNumber, 7) +
|
||||||
|
rol(obj.RevisionNumber, 11) +
|
||||||
|
rol(obj.Flags, 15) +
|
||||||
|
rol(obj.PublicKeyOrToken, 19) +
|
||||||
|
rol(obj.Name, 23) +
|
||||||
|
rol(obj.Locale, 27) +
|
||||||
|
rol(obj.HashValue, 31);
|
||||||
|
|
||||||
|
public bool Equals(RawAssemblyRefProcessorRow x, RawAssemblyRefProcessorRow y) =>
|
||||||
|
x.Processor == y.Processor &&
|
||||||
|
x.AssemblyRef == y.AssemblyRef;
|
||||||
|
|
||||||
|
public int GetHashCode(RawAssemblyRefProcessorRow obj) =>
|
||||||
|
(int)obj.Processor +
|
||||||
|
rol(obj.AssemblyRef, 3);
|
||||||
|
|
||||||
|
public bool Equals(RawAssemblyRefOSRow x, RawAssemblyRefOSRow y) =>
|
||||||
|
x.OSPlatformId == y.OSPlatformId &&
|
||||||
|
x.OSMajorVersion == y.OSMajorVersion &&
|
||||||
|
x.OSMinorVersion == y.OSMinorVersion &&
|
||||||
|
x.AssemblyRef == y.AssemblyRef;
|
||||||
|
|
||||||
|
public int GetHashCode(RawAssemblyRefOSRow obj) =>
|
||||||
|
(int)obj.OSPlatformId +
|
||||||
|
rol(obj.OSMajorVersion, 3) +
|
||||||
|
rol(obj.OSMinorVersion, 7) +
|
||||||
|
rol(obj.AssemblyRef, 11);
|
||||||
|
|
||||||
|
public bool Equals(RawFileRow x, RawFileRow y) =>
|
||||||
|
x.Flags == y.Flags &&
|
||||||
|
x.Name == y.Name &&
|
||||||
|
x.HashValue == y.HashValue;
|
||||||
|
|
||||||
|
public int GetHashCode(RawFileRow obj) =>
|
||||||
|
(int)obj.Flags +
|
||||||
|
rol(obj.Name, 3) +
|
||||||
|
rol(obj.HashValue, 7);
|
||||||
|
|
||||||
|
public bool Equals(RawExportedTypeRow x, RawExportedTypeRow y) =>
|
||||||
|
x.Flags == y.Flags &&
|
||||||
|
x.TypeDefId == y.TypeDefId &&
|
||||||
|
x.TypeName == y.TypeName &&
|
||||||
|
x.TypeNamespace == y.TypeNamespace &&
|
||||||
|
x.Implementation == y.Implementation;
|
||||||
|
|
||||||
|
public int GetHashCode(RawExportedTypeRow obj) =>
|
||||||
|
(int)obj.Flags +
|
||||||
|
rol(obj.TypeDefId, 3) +
|
||||||
|
rol(obj.TypeName, 7) +
|
||||||
|
rol(obj.TypeNamespace, 11) +
|
||||||
|
rol(obj.Implementation, 15);
|
||||||
|
|
||||||
|
public bool Equals(RawManifestResourceRow x, RawManifestResourceRow y) =>
|
||||||
|
x.Offset == y.Offset &&
|
||||||
|
x.Flags == y.Flags &&
|
||||||
|
x.Name == y.Name &&
|
||||||
|
x.Implementation == y.Implementation;
|
||||||
|
|
||||||
|
public int GetHashCode(RawManifestResourceRow obj) =>
|
||||||
|
(int)obj.Offset +
|
||||||
|
rol(obj.Flags, 3) +
|
||||||
|
rol(obj.Name, 7) +
|
||||||
|
rol(obj.Implementation, 11);
|
||||||
|
|
||||||
|
public bool Equals(RawNestedClassRow x, RawNestedClassRow y) =>
|
||||||
|
x.NestedClass == y.NestedClass &&
|
||||||
|
x.EnclosingClass == y.EnclosingClass;
|
||||||
|
|
||||||
|
public int GetHashCode(RawNestedClassRow obj) =>
|
||||||
|
(int)obj.NestedClass +
|
||||||
|
rol(obj.EnclosingClass, 3);
|
||||||
|
|
||||||
|
public bool Equals(RawGenericParamRow x, RawGenericParamRow y) =>
|
||||||
|
x.Number == y.Number &&
|
||||||
|
x.Flags == y.Flags &&
|
||||||
|
x.Owner == y.Owner &&
|
||||||
|
x.Name == y.Name &&
|
||||||
|
x.Kind == y.Kind;
|
||||||
|
|
||||||
|
public int GetHashCode(RawGenericParamRow obj) =>
|
||||||
|
(int)obj.Number +
|
||||||
|
rol(obj.Flags, 3) +
|
||||||
|
rol(obj.Owner, 7) +
|
||||||
|
rol(obj.Name, 11) +
|
||||||
|
rol(obj.Kind, 15);
|
||||||
|
|
||||||
|
public bool Equals(RawMethodSpecRow x, RawMethodSpecRow y) =>
|
||||||
|
x.Method == y.Method &&
|
||||||
|
x.Instantiation == y.Instantiation;
|
||||||
|
|
||||||
|
public int GetHashCode(RawMethodSpecRow obj) =>
|
||||||
|
(int)obj.Method +
|
||||||
|
rol(obj.Instantiation, 3);
|
||||||
|
|
||||||
|
public bool Equals(RawGenericParamConstraintRow x, RawGenericParamConstraintRow y) =>
|
||||||
|
x.Owner == y.Owner &&
|
||||||
|
x.Constraint == y.Constraint;
|
||||||
|
|
||||||
|
public int GetHashCode(RawGenericParamConstraintRow obj) =>
|
||||||
|
(int)obj.Owner +
|
||||||
|
rol(obj.Constraint, 3);
|
||||||
|
|
||||||
|
public bool Equals(RawDocumentRow x, RawDocumentRow y) =>
|
||||||
|
x.Name == y.Name &&
|
||||||
|
x.HashAlgorithm == y.HashAlgorithm &&
|
||||||
|
x.Hash == y.Hash &&
|
||||||
|
x.Language == y.Language;
|
||||||
|
|
||||||
|
public int GetHashCode(RawDocumentRow obj) =>
|
||||||
|
(int)obj.Name +
|
||||||
|
rol(obj.HashAlgorithm, 3) +
|
||||||
|
rol(obj.Hash, 7) +
|
||||||
|
rol(obj.Language, 11);
|
||||||
|
|
||||||
|
public bool Equals(RawMethodDebugInformationRow x, RawMethodDebugInformationRow y) =>
|
||||||
|
x.Document == y.Document &&
|
||||||
|
x.SequencePoints == y.SequencePoints;
|
||||||
|
|
||||||
|
public int GetHashCode(RawMethodDebugInformationRow obj) =>
|
||||||
|
(int)obj.Document +
|
||||||
|
rol(obj.SequencePoints, 3);
|
||||||
|
|
||||||
|
public bool Equals(RawLocalScopeRow x, RawLocalScopeRow y) =>
|
||||||
|
x.Method == y.Method &&
|
||||||
|
x.ImportScope == y.ImportScope &&
|
||||||
|
x.VariableList == y.VariableList &&
|
||||||
|
x.ConstantList == y.ConstantList &&
|
||||||
|
x.StartOffset == y.StartOffset &&
|
||||||
|
x.Length == y.Length;
|
||||||
|
|
||||||
|
public int GetHashCode(RawLocalScopeRow obj) =>
|
||||||
|
(int)obj.Method +
|
||||||
|
rol(obj.ImportScope, 3) +
|
||||||
|
rol(obj.VariableList, 7) +
|
||||||
|
rol(obj.ConstantList, 11) +
|
||||||
|
rol(obj.StartOffset, 15) +
|
||||||
|
rol(obj.Length, 19);
|
||||||
|
|
||||||
|
public bool Equals(RawLocalVariableRow x, RawLocalVariableRow y) =>
|
||||||
|
x.Attributes == y.Attributes &&
|
||||||
|
x.Index == y.Index &&
|
||||||
|
x.Name == y.Name;
|
||||||
|
|
||||||
|
public int GetHashCode(RawLocalVariableRow obj) =>
|
||||||
|
obj.Attributes +
|
||||||
|
rol(obj.Index, 3) +
|
||||||
|
rol(obj.Name, 7);
|
||||||
|
|
||||||
|
public bool Equals(RawLocalConstantRow x, RawLocalConstantRow y) =>
|
||||||
|
x.Name == y.Name &&
|
||||||
|
x.Signature == y.Signature;
|
||||||
|
|
||||||
|
public int GetHashCode(RawLocalConstantRow obj) =>
|
||||||
|
(int)obj.Name +
|
||||||
|
rol(obj.Signature, 3);
|
||||||
|
|
||||||
|
public bool Equals(RawImportScopeRow x, RawImportScopeRow y) =>
|
||||||
|
x.Parent == y.Parent &&
|
||||||
|
x.Imports == y.Imports;
|
||||||
|
|
||||||
|
public int GetHashCode(RawImportScopeRow obj) =>
|
||||||
|
(int)obj.Parent +
|
||||||
|
rol(obj.Imports, 3);
|
||||||
|
|
||||||
|
public bool Equals(RawStateMachineMethodRow x, RawStateMachineMethodRow y) =>
|
||||||
|
x.MoveNextMethod == y.MoveNextMethod &&
|
||||||
|
x.KickoffMethod == y.KickoffMethod;
|
||||||
|
|
||||||
|
public int GetHashCode(RawStateMachineMethodRow obj) =>
|
||||||
|
(int)obj.MoveNextMethod +
|
||||||
|
rol(obj.KickoffMethod, 3);
|
||||||
|
|
||||||
|
public bool Equals(RawCustomDebugInformationRow x, RawCustomDebugInformationRow y) =>
|
||||||
|
x.Parent == y.Parent &&
|
||||||
|
x.Kind == y.Kind &&
|
||||||
|
x.Value == y.Value;
|
||||||
|
|
||||||
|
public int GetHashCode(RawCustomDebugInformationRow obj) =>
|
||||||
|
(int)obj.Parent +
|
||||||
|
rol(obj.Kind, 3) +
|
||||||
|
rol(obj.Value, 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,141 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Stores a list of rids
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("Count = {Count}")]
|
||||||
|
public readonly struct RidList : IEnumerable<uint> {
|
||||||
|
readonly uint startRid;
|
||||||
|
readonly uint length;
|
||||||
|
readonly IList<uint> rids;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the empty instance
|
||||||
|
/// </summary>
|
||||||
|
public static readonly RidList Empty = Create(0, 0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="startRid"></param>
|
||||||
|
/// <param name="length"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static RidList Create(uint startRid, uint length) => new RidList(startRid, length);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rids">List of valid rids</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static RidList Create(IList<uint> rids) => new RidList(rids);
|
||||||
|
|
||||||
|
RidList(uint startRid, uint length) {
|
||||||
|
this.startRid = startRid;
|
||||||
|
this.length = length;
|
||||||
|
rids = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
RidList(IList<uint> rids) {
|
||||||
|
this.rids = rids ?? throw new ArgumentNullException(nameof(rids));
|
||||||
|
startRid = 0;
|
||||||
|
length = (uint)rids.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <paramref name="index"/>'th rid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">Index. Must be < <see cref="Count"/></param>
|
||||||
|
/// <returns>A rid or 0 if <paramref name="index"/> is invalid</returns>
|
||||||
|
public uint this[int index] {
|
||||||
|
get {
|
||||||
|
if (rids is not null) {
|
||||||
|
if ((uint)index >= (uint)rids.Count)
|
||||||
|
return 0;
|
||||||
|
return rids[index];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ((uint)index >= length)
|
||||||
|
return 0;
|
||||||
|
return startRid + (uint)index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the number of rids it will iterate over
|
||||||
|
/// </summary>
|
||||||
|
public int Count => (int)length;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enumerator
|
||||||
|
/// </summary>
|
||||||
|
public struct Enumerator : IEnumerator<uint> {
|
||||||
|
readonly uint startRid;
|
||||||
|
readonly uint length;
|
||||||
|
readonly IList<uint> rids;
|
||||||
|
uint index;
|
||||||
|
uint current;
|
||||||
|
|
||||||
|
internal Enumerator(in RidList list) {
|
||||||
|
startRid = list.startRid;
|
||||||
|
length = list.length;
|
||||||
|
rids = list.rids;
|
||||||
|
index = 0;
|
||||||
|
current = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current rid
|
||||||
|
/// </summary>
|
||||||
|
public uint Current => current;
|
||||||
|
object IEnumerator.Current => current;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes this instance
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose() { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moves to the next rid
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool MoveNext() {
|
||||||
|
if (rids is null && index < length) {
|
||||||
|
current = startRid + index;
|
||||||
|
index++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return MoveNextOther();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MoveNextOther() {
|
||||||
|
if (index >= length) {
|
||||||
|
current = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (rids is not null)
|
||||||
|
current = rids[(int)index];
|
||||||
|
else
|
||||||
|
current = startRid + index;
|
||||||
|
index++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IEnumerator.Reset() => throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the enumerator
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Enumerator GetEnumerator() => new Enumerator(this);
|
||||||
|
IEnumerator<uint> IEnumerable<uint>.GetEnumerator() => GetEnumerator();
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Storage flags found in the MD header
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum StorageFlags : byte {
|
||||||
|
/// <summary>
|
||||||
|
/// Normal flags
|
||||||
|
/// </summary>
|
||||||
|
Normal = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// More data after the header but before the streams.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The CLR will fail to load the file if this flag (or any other bits) is set.</remarks>
|
||||||
|
ExtraData = 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
using dnlib.IO;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// A metadata stream header
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("O:{offset} L:{streamSize} {name}")]
|
||||||
|
public sealed class StreamHeader : FileSection {
|
||||||
|
readonly uint offset;
|
||||||
|
readonly uint streamSize;
|
||||||
|
readonly string name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The offset of the stream relative to the start of the metadata header
|
||||||
|
/// </summary>
|
||||||
|
public uint Offset => offset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The size of the stream
|
||||||
|
/// </summary>
|
||||||
|
public uint StreamSize => streamSize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the stream
|
||||||
|
/// </summary>
|
||||||
|
public string Name => name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">PE file reader pointing to the start of this section</param>
|
||||||
|
/// <param name="verify">Verify section</param>
|
||||||
|
/// <exception cref="BadImageFormatException">Thrown if verification fails</exception>
|
||||||
|
public StreamHeader(ref DataReader reader, bool verify)
|
||||||
|
: this(ref reader, verify, verify, CLRRuntimeReaderKind.CLR, out _) {
|
||||||
|
}
|
||||||
|
|
||||||
|
internal StreamHeader(ref DataReader reader, bool throwOnError, bool verify, CLRRuntimeReaderKind runtime, out bool failedVerification) {
|
||||||
|
failedVerification = false;
|
||||||
|
SetStartOffset(ref reader);
|
||||||
|
offset = reader.ReadUInt32();
|
||||||
|
streamSize = reader.ReadUInt32();
|
||||||
|
name = ReadString(ref reader, 32, verify, ref failedVerification);
|
||||||
|
SetEndoffset(ref reader);
|
||||||
|
if (runtime == CLRRuntimeReaderKind.Mono) {
|
||||||
|
if (offset > reader.Length)
|
||||||
|
offset = reader.Length;
|
||||||
|
// Mono ignores the size (eg. it can be 0 or max value) so set it to the max possible value
|
||||||
|
streamSize = reader.Length - offset;
|
||||||
|
}
|
||||||
|
if (verify && offset + size < offset)
|
||||||
|
failedVerification = true;
|
||||||
|
if (throwOnError && failedVerification)
|
||||||
|
throw new BadImageFormatException("Invalid stream header");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal StreamHeader(uint offset, uint streamSize, string name) {
|
||||||
|
this.offset = offset;
|
||||||
|
this.streamSize = streamSize;
|
||||||
|
this.name = name ?? throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
static string ReadString(ref DataReader reader, int maxLen, bool verify, ref bool failedVerification) {
|
||||||
|
var origPos = reader.Position;
|
||||||
|
var sb = new StringBuilder(maxLen);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < maxLen; i++) {
|
||||||
|
byte b = reader.ReadByte();
|
||||||
|
if (b == 0)
|
||||||
|
break;
|
||||||
|
sb.Append((char)b);
|
||||||
|
}
|
||||||
|
if (verify && i == maxLen)
|
||||||
|
failedVerification = true;
|
||||||
|
if (i != maxLen)
|
||||||
|
reader.Position = origPos + (((uint)i + 1 + 3) & ~3U);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
// dnlib: See LICENSE.txt for more info
|
||||||
|
|
||||||
|
using dnlib.IO;
|
||||||
|
|
||||||
|
namespace dnlib.DotNet.MD {
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the #Strings stream
|
||||||
|
/// </summary>
|
||||||
|
public sealed class StringsStream : HeapStream {
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public StringsStream() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public StringsStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader)
|
||||||
|
: base(mdReaderFactory, metadataBaseOffset, streamHeader) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="UTF8String"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">Offset of string</param>
|
||||||
|
/// <returns>A <see cref="UTF8String"/> instance or <c>null</c> if invalid offset</returns>
|
||||||
|
public UTF8String Read(uint offset) {
|
||||||
|
if (offset >= StreamLength)
|
||||||
|
return null;
|
||||||
|
byte[] data;
|
||||||
|
var reader = dataReader;
|
||||||
|
reader.Position = offset;
|
||||||
|
data = reader.TryReadBytesUntil(0);
|
||||||
|
if (data is null)
|
||||||
|
return null;
|
||||||
|
return new UTF8String(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a <see cref="UTF8String"/>. The empty string is returned if <paramref name="offset"/>
|
||||||
|
/// is invalid.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">Offset of string</param>
|
||||||
|
/// <returns>A <see cref="UTF8String"/> instance</returns>
|
||||||
|
public UTF8String ReadNoNull(uint offset) => Read(offset) ?? UTF8String.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue