|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.SymbolStore;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using CultureInfo = System.Globalization.CultureInfo;
namespace System.Reflection.Emit
{
internal sealed class RuntimeMethodBuilder : MethodBuilder
{
#region Private Data Members
// Identity
internal string m_strName; // The name of the method
private int m_token; // The token of this method
private readonly RuntimeModuleBuilder m_module;
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
internal RuntimeTypeBuilder m_containingType;
// IL
private int[]? m_mdMethodFixups; // The location of all of the token fixups. Null means no fixups.
private byte[]? m_localSignature; // Local signature if set explicitly via DefineBody. Null otherwise.
internal LocalSymInfo? m_localSymInfo; // keep track debugging local information
internal RuntimeILGenerator? m_ilGenerator; // Null if not used.
private byte[]? m_ubBody; // The IL for the method
private ExceptionHandler[]? m_exceptions; // Exception handlers or null if there are none.
private const int DefaultMaxStack = 16;
// Flags
internal bool m_bIsBaked;
private bool m_fInitLocals; // indicating if the method stack frame will be zero initialized or not.
// Attributes
private readonly MethodAttributes m_iAttributes;
private readonly CallingConventions m_callingConvention;
private MethodImplAttributes m_dwMethodImplFlags;
// Parameters
private SignatureHelper? m_signature;
internal Type[]? m_parameterTypes;
private Type m_returnType;
private Type[]? m_returnTypeRequiredCustomModifiers;
private Type[]? m_returnTypeOptionalCustomModifiers;
private Type[][]? m_parameterTypeRequiredCustomModifiers;
private Type[][]? m_parameterTypeOptionalCustomModifiers;
// Generics
private RuntimeGenericTypeParameterBuilder[]? m_inst;
private bool m_bIsGenMethDef;
#endregion
#region Constructor
internal RuntimeMethodBuilder(string name, MethodAttributes attributes, CallingConventions callingConvention,
Type? returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers,
Type[]? parameterTypes, Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers,
RuntimeModuleBuilder mod, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] RuntimeTypeBuilder type)
{
ArgumentException.ThrowIfNullOrEmpty(name);
if (name[0] == '\0')
throw new ArgumentException(SR.Argument_IllegalName, nameof(name));
ArgumentNullException.ThrowIfNull(mod);
if (parameterTypes != null)
{
foreach (Type t in parameterTypes)
{
ArgumentNullException.ThrowIfNull(t, nameof(parameterTypes));
}
}
m_strName = name;
m_module = mod;
m_containingType = type;
m_returnType = returnType ?? typeof(void);
if ((attributes & MethodAttributes.Static) == 0)
{
// turn on the has this calling convention
callingConvention |= CallingConventions.HasThis;
}
else if ((attributes & MethodAttributes.Virtual) != 0)
{
// On an interface, the rule is slightly different
if ((attributes & MethodAttributes.Abstract) == 0)
throw new ArgumentException(SR.Arg_NoStaticVirtual);
}
m_callingConvention = callingConvention;
if (parameterTypes != null)
{
m_parameterTypes = new Type[parameterTypes.Length];
Array.Copy(parameterTypes, m_parameterTypes, parameterTypes.Length);
}
else
{
m_parameterTypes = null;
}
m_returnTypeRequiredCustomModifiers = returnTypeRequiredCustomModifiers;
m_returnTypeOptionalCustomModifiers = returnTypeOptionalCustomModifiers;
m_parameterTypeRequiredCustomModifiers = parameterTypeRequiredCustomModifiers;
m_parameterTypeOptionalCustomModifiers = parameterTypeOptionalCustomModifiers;
// m_signature = SignatureHelper.GetMethodSigHelper(mod, callingConvention,
// returnType, returnTypeRequiredCustomModifiers, returnTypeOptionalCustomModifiers,
// parameterTypes, parameterTypeRequiredCustomModifiers, parameterTypeOptionalCustomModifiers);
m_iAttributes = attributes;
m_bIsBaked = false;
m_fInitLocals = true;
m_localSymInfo = new LocalSymInfo();
m_ubBody = null;
m_ilGenerator = null;
// Default is managed IL. Managed IL has bit flag 0x0020 set off
m_dwMethodImplFlags = MethodImplAttributes.IL;
}
#endregion
#region Internal Members
internal void CreateMethodBodyHelper(RuntimeILGenerator il)
{
ArgumentNullException.ThrowIfNull(il);
// Sets the IL of the method. An ILGenerator is passed as an argument and the method
// queries this instance to get all of the information which it needs.
__ExceptionInfo[] excp;
int counter = 0;
int[] filterAddrs;
int[] catchAddrs;
int[] catchEndAddrs;
Type[] catchClass;
int[] type;
int numCatch;
int start, end;
RuntimeModuleBuilder dynMod = m_module;
m_containingType.ThrowIfCreated();
if (m_bIsBaked)
{
throw new InvalidOperationException(SR.InvalidOperation_MethodHasBody);
}
if (il.m_methodBuilder != this && il.m_methodBuilder != null)
{
// you don't need to call DefineBody when you get your ILGenerator
// through MethodBuilder::GetILGenerator.
//
throw new InvalidOperationException(SR.InvalidOperation_BadILGeneratorUsage);
}
ThrowIfShouldNotHaveBody();
if (il.m_ScopeTree.m_iOpenScopeCount != 0)
{
// There are still unclosed local scope
throw new InvalidOperationException(SR.InvalidOperation_OpenLocalVariableScope);
}
m_ubBody = il.BakeByteArray();
m_mdMethodFixups = il.GetTokenFixups();
// Okay, now the fun part. Calculate all of the exceptions.
excp = il.GetExceptions()!;
int numExceptions = CalculateNumberOfExceptions(excp);
if (numExceptions > 0)
{
m_exceptions = new ExceptionHandler[numExceptions];
for (int i = 0; i < excp.Length; i++)
{
filterAddrs = excp[i].GetFilterAddresses();
catchAddrs = excp[i].GetCatchAddresses();
catchEndAddrs = excp[i].GetCatchEndAddresses();
catchClass = excp[i].GetCatchClass();
numCatch = excp[i].GetNumberOfCatches();
start = excp[i].GetStartAddress();
end = excp[i].GetEndAddress();
type = excp[i].GetExceptionTypes();
for (int j = 0; j < numCatch; j++)
{
int tkExceptionClass = 0;
if (catchClass[j] != null)
{
tkExceptionClass = dynMod.GetTypeTokenInternal(catchClass[j]);
}
switch (type[j])
{
case __ExceptionInfo.None:
case __ExceptionInfo.Fault:
case __ExceptionInfo.Filter:
m_exceptions[counter++] = new ExceptionHandler(start, end, filterAddrs[j], catchAddrs[j], catchEndAddrs[j], type[j], tkExceptionClass);
break;
case __ExceptionInfo.Finally:
m_exceptions[counter++] = new ExceptionHandler(start, excp[i].GetFinallyEndAddress(), filterAddrs[j], catchAddrs[j], catchEndAddrs[j], type[j], tkExceptionClass);
break;
}
}
}
}
m_bIsBaked = true;
}
// This is only called from TypeBuilder.CreateType after the method has been created
internal void ReleaseBakedStructures()
{
if (!m_bIsBaked)
{
// We don't need to do anything here if we didn't baked the method body
return;
}
m_ubBody = null;
m_localSymInfo = null;
m_mdMethodFixups = null;
m_localSignature = null;
m_exceptions = null;
}
internal override Type[] GetParameterTypes() => m_parameterTypes ??= Type.EmptyTypes;
internal static Type? GetMethodBaseReturnType(MethodBase? method)
{
if (method is MethodInfo mi)
{
return mi.ReturnType;
}
else if (method is ConstructorInfo ci)
{
return ci.GetReturnType();
}
else
{
Debug.Fail("We should never get here!");
return null;
}
}
internal void SetToken(int token)
{
m_token = token;
}
internal byte[]? GetBody()
{
// Returns the il bytes of this method.
// This il is not valid until somebody has called BakeByteArray
return m_ubBody;
}
internal int[]? GetTokenFixups()
{
return m_mdMethodFixups;
}
internal SignatureHelper GetMethodSignature()
{
m_parameterTypes ??= Type.EmptyTypes;
m_signature = SignatureHelper.GetMethodSigHelper(m_module, m_callingConvention, m_inst != null ? m_inst.Length : 0,
m_returnType, m_returnTypeRequiredCustomModifiers, m_returnTypeOptionalCustomModifiers,
m_parameterTypes, m_parameterTypeRequiredCustomModifiers, m_parameterTypeOptionalCustomModifiers);
return m_signature;
}
// Returns a buffer whose initial signatureLength bytes contain encoded local signature.
internal byte[] GetLocalSignature(out int signatureLength)
{
if (m_localSignature != null)
{
signatureLength = m_localSignature.Length;
return m_localSignature;
}
if (m_ilGenerator != null)
{
if (m_ilGenerator.m_localCount != 0)
{
// If user is using ILGenerator::DeclareLocal, then get local signaturefrom there.
return m_ilGenerator.m_localSignature.InternalGetSignature(out signatureLength);
}
}
return SignatureHelper.GetLocalVarSigHelper(m_module).InternalGetSignature(out signatureLength);
}
internal int GetMaxStack()
{
if (m_ilGenerator != null)
{
return m_ilGenerator.GetMaxStackSize();
}
else
{
// this is the case when client provide an array of IL byte stream rather than going through ILGenerator.
return DefaultMaxStack;
}
}
internal ExceptionHandler[]? GetExceptionHandlers()
{
return m_exceptions;
}
internal static int CalculateNumberOfExceptions(__ExceptionInfo[]? excp)
{
int num = 0;
if (excp == null)
{
return 0;
}
for (int i = 0; i < excp.Length; i++)
{
num += excp[i].GetNumberOfCatches();
}
return num;
}
internal bool IsTypeCreated()
{
return m_containingType != null && m_containingType.IsCreated();
}
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
internal RuntimeTypeBuilder GetTypeBuilder()
{
return m_containingType;
}
internal RuntimeModuleBuilder GetModuleBuilder()
{
return m_module;
}
#endregion
#region Object Overrides
public override bool Equals(object? obj)
{
if (obj is not RuntimeMethodBuilder rmBuilder)
{
return false;
}
if (!m_strName.Equals(rmBuilder.m_strName))
{
return false;
}
if (m_iAttributes != rmBuilder.m_iAttributes)
{
return false;
}
SignatureHelper thatSig = rmBuilder.GetMethodSignature();
if (thatSig.Equals(GetMethodSignature()))
{
return true;
}
return false;
}
public override int GetHashCode()
{
return m_strName.GetHashCode();
}
public override string ToString()
{
StringBuilder sb = new StringBuilder(1000);
sb.Append("Name: ").Append(m_strName).AppendLine(" ");
sb.Append("Attributes: ").Append((int)m_iAttributes).AppendLine();
sb.Append("Method Signature: ").Append(GetMethodSignature()).AppendLine();
sb.AppendLine();
return sb.ToString();
}
#endregion
#region MemberInfo Overrides
public override string Name => m_strName;
public override int MetadataToken => GetToken();
public override Module Module => m_containingType.Module;
public override Type? DeclaringType
{
get
{
if (m_containingType.m_isHiddenGlobalType)
return null;
return m_containingType;
}
}
public override ICustomAttributeProvider ReturnTypeCustomAttributes => new EmptyCAHolder();
public override Type? ReflectedType => DeclaringType;
#endregion
#region MethodBase Overrides
public override object Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture)
{
throw new NotSupportedException(SR.NotSupported_DynamicModule);
}
public override MethodImplAttributes GetMethodImplementationFlags()
{
return m_dwMethodImplFlags;
}
public override MethodAttributes Attributes => m_iAttributes;
public override CallingConventions CallingConvention => m_callingConvention;
public override RuntimeMethodHandle MethodHandle => throw new NotSupportedException(SR.NotSupported_DynamicModule);
public override bool IsSecurityCritical => true;
public override bool IsSecuritySafeCritical => false;
public override bool IsSecurityTransparent => false;
#endregion
#region MethodInfo Overrides
public override MethodInfo GetBaseDefinition()
{
return this;
}
public override Type ReturnType => m_returnType;
public override ParameterInfo[] GetParameters()
{
if (!m_bIsBaked || m_containingType == null || m_containingType.BakedRuntimeType == null)
throw new NotSupportedException(SR.InvalidOperation_TypeNotCreated);
MethodInfo rmi = m_containingType.GetMethod(m_strName, m_parameterTypes!)!;
return rmi.GetParameters();
}
public override ParameterInfo ReturnParameter
{
get
{
if (!m_bIsBaked || m_containingType == null || m_containingType.BakedRuntimeType == null)
throw new InvalidOperationException(SR.InvalidOperation_TypeNotCreated);
MethodInfo rmi = m_containingType.GetMethod(m_strName, m_parameterTypes!)!;
return rmi.ReturnParameter;
}
}
#endregion
#region ICustomAttributeProvider Implementation
public override object[] GetCustomAttributes(bool inherit)
{
throw new NotSupportedException(SR.NotSupported_DynamicModule);
}
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
{
throw new NotSupportedException(SR.NotSupported_DynamicModule);
}
public override bool IsDefined(Type attributeType, bool inherit)
{
throw new NotSupportedException(SR.NotSupported_DynamicModule);
}
#endregion
#region Generic Members
public override bool IsGenericMethodDefinition => m_bIsGenMethDef;
public override bool ContainsGenericParameters => throw new NotSupportedException();
public override MethodInfo GetGenericMethodDefinition() { if (!IsGenericMethod) throw new InvalidOperationException(); return this; }
public override bool IsGenericMethod => m_inst != null;
public override Type[] GetGenericArguments() => m_inst ?? Type.EmptyTypes;
[RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")]
[RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")]
public override MethodInfo MakeGenericMethod(params Type[] typeArguments)
{
return MethodBuilderInstantiation.MakeGenericMethod(this, typeArguments);
}
protected override GenericTypeParameterBuilder[] DefineGenericParametersCore(params string[] names)
{
if (m_inst != null)
throw new InvalidOperationException(SR.InvalidOperation_GenericParametersAlreadySet);
if (m_token != 0)
throw new InvalidOperationException(SR.InvalidOperation_MethodBuilderBaked);
m_inst = new RuntimeGenericTypeParameterBuilder[names.Length];
for (int i = 0; i < names.Length; i++)
{
string name = names[i];
ArgumentNullException.ThrowIfNull(name, nameof(names));
m_inst[i] = new RuntimeGenericTypeParameterBuilder(new RuntimeTypeBuilder(name, i, this));
}
m_bIsGenMethDef = true;
return m_inst;
}
internal void ThrowIfGeneric() { if (IsGenericMethod && !IsGenericMethodDefinition) throw new InvalidOperationException(); }
#endregion
#region Private Members
private int GetToken()
{
// We used to always "tokenize" a MethodBuilder when it is constructed. After change list 709498
// we only "tokenize" a method when requested. But the order in which the methods are tokenized
// didn't change: the same order the MethodBuilders are constructed. The recursion introduced
// will overflow the stack when there are many methods on the same type (10000 in my experiment).
// The change also introduced race conditions. Before the code change GetToken is called from
// the MethodBuilder .ctor which is protected by lock(ModuleBuilder.SyncRoot). Now it
// could be called more than once on the same method introducing duplicate (invalid) tokens.
// I don't fully understand this change. So I will keep the logic and only fix the recursion and
// the race condition.
if (m_token != 0)
{
return m_token;
}
RuntimeMethodBuilder? currentMethod = null;
int currentToken = 0;
int i;
// We need to lock here to prevent a method from being "tokenized" twice.
// We don't need to synchronize this with Type.DefineMethod because it only appends newly
// constructed MethodBuilders to the end of m_listMethods
lock (m_containingType.m_listMethods!)
{
if (m_token != 0)
{
return m_token;
}
// If m_tkMethod is still 0 when we obtain the lock, m_lastTokenizedMethod must be smaller
// than the index of the current method.
for (i = m_containingType.m_lastTokenizedMethod + 1; i < m_containingType.m_listMethods.Count; ++i)
{
currentMethod = m_containingType.m_listMethods[i];
currentToken = currentMethod.GetTokenNoLock();
if (currentMethod == this)
break;
}
m_containingType.m_lastTokenizedMethod = i;
}
Debug.Assert(currentMethod == this, "We should have found this method in m_containingType.m_listMethods");
Debug.Assert(currentToken != 0, "The token should not be 0");
return currentToken;
}
private int GetTokenNoLock()
{
Debug.Assert(m_token == 0, "m_token should not have been initialized");
byte[] sigBytes = GetMethodSignature().InternalGetSignature(out int sigLength);
RuntimeModuleBuilder module = m_module;
int token = RuntimeTypeBuilder.DefineMethod(new QCallModule(ref module), m_containingType.MetadataToken, m_strName, sigBytes, sigLength, Attributes);
m_token = token;
if (m_inst != null)
foreach (RuntimeGenericTypeParameterBuilder tb in m_inst)
if (!tb.m_type.IsCreated()) tb.m_type.CreateType();
RuntimeTypeBuilder.SetMethodImpl(new QCallModule(ref module), token, m_dwMethodImplFlags);
return m_token;
}
#endregion
#region Protected Members Overrides
protected override void SetSignatureCore(
Type? returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers,
Type[]? parameterTypes, Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers)
{
// We should throw InvalidOperation_MethodBuilderBaked here if the method signature has been baked.
// But we cannot because that would be a breaking change from V2.
if (m_token != 0)
return;
ThrowIfGeneric();
if (returnType != null)
{
m_returnType = returnType;
}
if (parameterTypes != null)
{
m_parameterTypes = new Type[parameterTypes.Length];
Array.Copy(parameterTypes, m_parameterTypes, parameterTypes.Length);
}
m_returnTypeRequiredCustomModifiers = returnTypeRequiredCustomModifiers;
m_returnTypeOptionalCustomModifiers = returnTypeOptionalCustomModifiers;
m_parameterTypeRequiredCustomModifiers = parameterTypeRequiredCustomModifiers;
m_parameterTypeOptionalCustomModifiers = parameterTypeOptionalCustomModifiers;
}
protected override ParameterBuilder DefineParameterCore(int position, ParameterAttributes attributes, string? strParamName)
{
ThrowIfGeneric();
m_containingType.ThrowIfCreated();
if (position > 0 && (m_parameterTypes == null || position > m_parameterTypes.Length))
throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_ParamSequence);
attributes &= ~ParameterAttributes.ReservedMask;
return new RuntimeParameterBuilder(this, position, attributes, strParamName);
}
protected override void SetImplementationFlagsCore(MethodImplAttributes attributes)
{
ThrowIfGeneric();
m_containingType.ThrowIfCreated();
m_dwMethodImplFlags = attributes;
m_canBeRuntimeImpl = true;
RuntimeModuleBuilder module = m_module;
RuntimeTypeBuilder.SetMethodImpl(new QCallModule(ref module), MetadataToken, attributes);
}
protected override ILGenerator GetILGeneratorCore(int size)
{
ThrowIfGeneric();
ThrowIfShouldNotHaveBody();
return m_ilGenerator ??= new RuntimeILGenerator(this, size);
}
private void ThrowIfShouldNotHaveBody()
{
if ((m_dwMethodImplFlags & MethodImplAttributes.CodeTypeMask) != MethodImplAttributes.IL ||
(m_dwMethodImplFlags & MethodImplAttributes.Unmanaged) != 0 ||
(m_iAttributes & MethodAttributes.PinvokeImpl) != 0 ||
m_isDllImport)
{
// cannot attach method body if methodimpl is marked not marked as managed IL
//
throw new InvalidOperationException(SR.InvalidOperation_ShouldNotHaveMethodBody);
}
}
protected override bool InitLocalsCore
{
// Property is set to true if user wishes to have zero initialized stack frame for this method. Default to false.
get { ThrowIfGeneric(); return m_fInitLocals; }
set { ThrowIfGeneric(); m_fInitLocals = value; }
}
internal Module GetModule()
{
return GetModuleBuilder();
}
protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
{
ThrowIfGeneric();
RuntimeTypeBuilder.DefineCustomAttribute(m_module, MetadataToken,
m_module.GetMethodMetadataToken(con),
binaryAttribute);
if (IsKnownCA(con))
ParseCA(con);
}
// this method should return true for any and every ca that requires more work
// than just setting the ca
private static bool IsKnownCA(ConstructorInfo con)
{
Type? caType = con.DeclaringType;
return caType == typeof(MethodImplAttribute) || caType == typeof(DllImportAttribute);
}
private void ParseCA(ConstructorInfo con)
{
Type? caType = con.DeclaringType;
if (caType == typeof(MethodImplAttribute))
{
// dig through the blob looking for the MethodImplAttributes flag
// that must be in the MethodCodeType field
// for now we simply set a flag that relaxes the check when saving and
// allows this method to have no body when any kind of MethodImplAttribute is present
m_canBeRuntimeImpl = true;
}
else if (caType == typeof(DllImportAttribute))
{
m_canBeRuntimeImpl = true;
m_isDllImport = true;
}
}
internal bool m_canBeRuntimeImpl;
internal bool m_isDllImport;
#endregion
}
internal sealed class LocalSymInfo
{
// This class tracks the local variable's debugging information
// and namespace information with a given active lexical scope.
#region Internal Data Members
internal string[] m_strName = null!; // All these arrys initialized in helper method
internal byte[][] m_ubSignature = null!;
internal int[] m_iLocalSlot = null!;
internal int[] m_iStartOffset = null!;
internal int[] m_iEndOffset = null!;
internal int m_iLocalSymCount; // how many entries in the arrays are occupied
internal string[] m_namespace = null!;
internal int m_iNameSpaceCount;
internal const int InitialSize = 16;
#endregion
#region Constructor
internal LocalSymInfo()
{
// initialize data variables
m_iLocalSymCount = 0;
m_iNameSpaceCount = 0;
}
#endregion
#region Private Members
private void EnsureCapacityNamespace()
{
if (m_iNameSpaceCount == 0)
{
m_namespace = new string[InitialSize];
}
else if (m_iNameSpaceCount == m_namespace.Length)
{
string[] strTemp = new string[checked(m_iNameSpaceCount * 2)];
Array.Copy(m_namespace, strTemp, m_iNameSpaceCount);
m_namespace = strTemp;
}
}
private void EnsureCapacity()
{
if (m_iLocalSymCount == 0)
{
// First time. Allocate the arrays.
m_strName = new string[InitialSize];
m_ubSignature = new byte[InitialSize][];
m_iLocalSlot = new int[InitialSize];
m_iStartOffset = new int[InitialSize];
m_iEndOffset = new int[InitialSize];
}
else if (m_iLocalSymCount == m_strName.Length)
{
// the arrays are full. Enlarge the arrays
// why aren't we just using lists here?
int newSize = checked(m_iLocalSymCount * 2);
int[] temp = new int[newSize];
Array.Copy(m_iLocalSlot, temp, m_iLocalSymCount);
m_iLocalSlot = temp;
temp = new int[newSize];
Array.Copy(m_iStartOffset, temp, m_iLocalSymCount);
m_iStartOffset = temp;
temp = new int[newSize];
Array.Copy(m_iEndOffset, temp, m_iLocalSymCount);
m_iEndOffset = temp;
string[] strTemp = new string[newSize];
Array.Copy(m_strName, strTemp, m_iLocalSymCount);
m_strName = strTemp;
byte[][] ubTemp = new byte[newSize][];
Array.Copy(m_ubSignature, ubTemp, m_iLocalSymCount);
m_ubSignature = ubTemp;
}
}
#endregion
#region Internal Members
internal void AddLocalSymInfo(string strName, byte[] signature, int slot, int startOffset, int endOffset)
{
// make sure that arrays are large enough to hold addition info
EnsureCapacity();
m_iStartOffset[m_iLocalSymCount] = startOffset;
m_iEndOffset[m_iLocalSymCount] = endOffset;
m_iLocalSlot[m_iLocalSymCount] = slot;
m_strName[m_iLocalSymCount] = strName;
m_ubSignature[m_iLocalSymCount] = signature;
checked { m_iLocalSymCount++; }
}
internal void AddUsingNamespace(string strNamespace)
{
EnsureCapacityNamespace();
m_namespace[m_iNameSpaceCount] = strNamespace;
checked { m_iNameSpaceCount++; }
}
#endregion
}
/// <summary>
/// Describes exception handler in a method body.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal readonly struct ExceptionHandler : IEquatable<ExceptionHandler>
{
// Keep in sync with unmanged structure.
internal readonly int m_exceptionClass;
internal readonly int m_tryStartOffset;
internal readonly int m_tryEndOffset;
internal readonly int m_filterOffset;
internal readonly int m_handlerStartOffset;
internal readonly int m_handlerEndOffset;
internal readonly ExceptionHandlingClauseOptions m_kind;
#region Constructors
internal ExceptionHandler(int tryStartOffset, int tryEndOffset, int filterOffset, int handlerStartOffset, int handlerEndOffset,
int kind, int exceptionTypeToken)
{
Debug.Assert(tryStartOffset >= 0);
Debug.Assert(tryEndOffset >= 0);
Debug.Assert(filterOffset >= 0);
Debug.Assert(handlerStartOffset >= 0);
Debug.Assert(handlerEndOffset >= 0);
Debug.Assert(IsValidKind((ExceptionHandlingClauseOptions)kind));
Debug.Assert(kind != (int)ExceptionHandlingClauseOptions.Clause || (exceptionTypeToken & 0x00FFFFFF) != 0);
m_tryStartOffset = tryStartOffset;
m_tryEndOffset = tryEndOffset;
m_filterOffset = filterOffset;
m_handlerStartOffset = handlerStartOffset;
m_handlerEndOffset = handlerEndOffset;
m_kind = (ExceptionHandlingClauseOptions)kind;
m_exceptionClass = exceptionTypeToken;
}
private static bool IsValidKind(ExceptionHandlingClauseOptions kind) =>
kind is
ExceptionHandlingClauseOptions.Clause or
ExceptionHandlingClauseOptions.Filter or
ExceptionHandlingClauseOptions.Finally or
ExceptionHandlingClauseOptions.Fault;
#endregion
#region Equality
public override int GetHashCode()
{
return m_exceptionClass ^ m_tryStartOffset ^ m_tryEndOffset ^ m_filterOffset ^ m_handlerStartOffset ^ m_handlerEndOffset ^ (int)m_kind;
}
public override bool Equals(object? obj)
{
return obj is ExceptionHandler && Equals((ExceptionHandler)obj);
}
public bool Equals(ExceptionHandler other)
{
return
other.m_exceptionClass == m_exceptionClass &&
other.m_tryStartOffset == m_tryStartOffset &&
other.m_tryEndOffset == m_tryEndOffset &&
other.m_filterOffset == m_filterOffset &&
other.m_handlerStartOffset == m_handlerStartOffset &&
other.m_handlerEndOffset == m_handlerEndOffset &&
other.m_kind == m_kind;
}
public static bool operator ==(ExceptionHandler left, ExceptionHandler right) => left.Equals(right);
public static bool operator !=(ExceptionHandler left, ExceptionHandler right) => !left.Equals(right);
#endregion
}
}
|