parent
e10a42f8dd
commit
f9435d39ff
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