楔子
講一些CLR裏面的內存模型。本篇MethodDesc,意爲函數的描述之意,看下一個函數在CLR裏面是如何被描述的。
MethodDesc結構
這個結構體在CLR裏面高達1600多行,這裏僅截取一些
class MethodDesc
{
friend class EEClass;
friend class MethodTableBuilder;
friend class ArrayClass;
friend class NDirect;
friend class MethodDescChunk;
friend class InstantiatedMethodDesc;
friend class MethodImpl;
friend class CheckAsmOffsets;
friend class ClrDataAccess;
friend class MethodDescCallSite;
#ifdef _DEBUG
LPCUTF8 m_pszDebugMethodName;
LPCUTF8 m_pszDebugClassName;
LPCUTF8 m_pszDebugMethodSignature;
PTR_MethodTable m_pDebugMethodTable;
#endif
PTR_GCCoverageInfo m_GcCover;
UINT16 m_wFlags3AndTokenRemainder;
BYTE m_chunkIndex;
BYTE m_bFlags2;
WORD m_wSlotNumber;
WORD m_wFlags;
};
這裏面可以看到它除了友元類之外,還有一些調試以及非調試的時候所包含的字段。
代碼
看下這個簡單的例子,在MethodDesc字段裏面的表示
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
Console::ReadLine()
}
}
字段
如上例子所示,Program類,以及Main函數在MethodDesc裏面的表示如下
一:
m_pszDebugMethodName = 0x00007ffa973f7dd8 "Main"
0x00007ffa973f7dd8這個地址指向了入口函數Main函數字符串值。
二:
m_pszDebugClassName = 0x00007ffa9739fef0 "ConsoleApp2.Program"
同樣是指向字符串
三:
m_pszDebugMethodSignature = 0x00007ffa973f7e28 "void *(string[])"
四:
m_pDebugMethodTable = 0x00007ffa9739ff28 {[Type Name]= "ConsoleApp2.Program" }
可以看到,在IfDebug模式下,類名,函數名,函數的返回值以及參數,以及類的MethodTable都包含在了MethodDesc裏面。
示例IL
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 )
// 代碼大小 19 (0x13)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Hello, World!"
IL_0006: call void [System.Console]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: call string [System.Console]System.Console::ReadLine()
IL_0011: pop
IL_0012: ret
} // end of method Program::Main
解構
注意了這裏的MethodDesc主要是指函數描述結構,而非函數體。函數描述結構和用IL代碼表達的函數體共同被RyuJIT加載和編譯。MethodDesc主要的作用是通過CLR把它傳入到RyuJIT,然後對MethodDesc描述的函數進行Native Code編譯。
結尾
作者:江湖評談(公衆號同名)