如何找到public extern bool Equals(String value)的實現代碼?[C#, C++, BCL, CLR]
Written by Allen Lee
Q:在微軟提供的Rotor原始碼中,我發現String.Equals(String value)的代碼只有下面兩行:
// Code #01
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern bool Equals(String value);
那麼,在哪裡可以找到該方法的實現代碼呢?
A:開啟Rotor原始碼的sscli\clr\src\vm\ecall.cpp檔案,搜尋“Equals”關鍵字,你將找到如下所示的數群組成員:
// Code #02
static ECFunc gStringFuncs[] = {
// Other members here
{FCFuncElement("Equals", &gsig_IM_Str_RetBool, (LPVOID)COMString::EqualsString)},
// Other members here
};
從該數群組成員中,我們可以得知我們要尋找的就是COMString::EqualsString函數。那麼,ComString::EqualsString函數的實現代碼又在哪裡呢?
開啟Rotor原始碼的sscli\clr\src\vm\comstring.cpp檔案,搜尋“COMString::EqualsString”關鍵字,怎麼樣,找到實現代碼了嗎?
Q:是的,我找到了:
// Code #03
FCIMPL2(INT32, COMString::EqualsString, StringObject* thisStr, StringObject* valueStr)
{
VALIDATEOBJECTREF(thisStr);
VALIDATEOBJECTREF(valueStr);
INT32 ret = false;
if (NULL==thisStr)
FCThrow(kNullReferenceException);
if (!valueStr)
{
FC_GC_POLL_RET();
return ret;
}
ret = WcharCompareHelper (STRINGREF(thisStr), STRINGREF(valueStr));
FC_GC_POLL_RET();
return ret;
}
FCIMPLEND
你能否簡單說明一下?
A:Code #03的函數的定義使用了宏(Macro)。在sscli\clr\src\vm\fcall.h檔案中,你會找到如下語句:
// Code #04
#define FCDECL2(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(a1, a2)
這就是該函數的宏定義。於是,上面你所找到的C++代碼就可以展開為:
// Code #05
INT32 F_CALL_CONV COMString::EqualsString(StringObject* thisStr, StringObject* valueStr)
{
//
}
Code #04所示的宏是對應著x86體系的,對於非x86體系,該函數的宏定義為:
// Code #06
#define FCIMPL2(rettype, funcname, a1, a2) rettype funcname(a1, a2) { FCIMPL_PROLOG(funcname)
另外,該函數其實把真正的比較工作交給了WcharCompareHelper函數,你可以在同一個檔案(comstring.cpp)中找到它的真身:
// Code #07
bool WcharCompareHelper (STRINGREF thisStr, STRINGREF valueStr)
{
DWORD *thisChars, *valueChars;
int thisLength, valueLength;
//Get all of our required data.
RefInterpretGetStringValuesDangerousForGC(thisStr, (WCHAR**)&thisChars, &thisLength);
RefInterpretGetStringValuesDangerousForGC(valueStr, (WCHAR**)&valueChars, &valueLength);
//If they're different lengths, they're not an exact match.
if (thisLength!=valueLength) {
return false;
}
// Loop comparing a DWORD (2 WCHARs) at a time.
while ((thisLength -= 2) >= 0)
{
if (*thisChars != *valueChars)
return false;
++thisChars;
++valueChars;
}
// Handle an extra WCHAR.
if (thisLength == -1)
return (*((WCHAR *) thisChars) == *((WCHAR *) valueChars));
return true;
}
至此,你已經找到Rotor的String.Equals(String value)的實現演算法了。
Q:ecall.cpp檔案是用來幹什麼的?
A:該檔案包含著為數眾多的數組,這些數組實質上充當一個表的角色,用於把標記了[MethodImplAttribute(MethodImplOptions.InternalCall)]屬性的方法映射為非託管的C++實現。其中,數組的成員實際上也使用了宏。Code #02中的
// Code #08
FCFuncElement("Equals", &gsig_IM_Str_RetBool, (LPVOID)COMString::EqualsString)
就對應著Code #09的宏定義:
// Code #09
#define FCFuncElement(A,B,C) A, B, C, NULL, CORINFO_INTRINSIC_Illegal
Q:fcall.h檔案又是用來幹什麼的?
A:該檔案也定義了為數眾多的宏,這些宏充當著“函數模板”的角色,用於把簽名相類似的函數組織起來,並以統一的方式展開。該檔案開頭還留有相關的注釋,用於說明這些宏的作用、工作原理以及相關注意事項。
Q:什麼情況下,我們可以使用這種方法來尋找Rotor BCL中沒有給出具體實現的方法代碼?
A:我們知道,沒有給出具體實現代碼的方法,需要添加extern以表明該實現在別的某處可以找到,一般情況下,該修飾符與DllImportAttribute結合使用:
// Code #10
[DllImport("uxtheme.dll")]
static public extern int SetWindowTheme(IntPtr hWnd, StringBuilder AppID, StringBuilder ClassID);
static public void DisableWindowsXPTheme(IntPtr hWnd)
{
// Disable using the Window XP Theme for the Window handle
// passed as a parameter
StringBuilder applicationName = new StringBuilder(" ", 1);
StringBuilder classIDs = new StringBuilder(" " , 1);
Win32.SetWindowTheme(hWnd, applicationName, classIDs);
}
這樣,.NET就會自動從uxtheme.dll那裡尋找對應的實現代碼,這種調用方式叫做PInvoke。當然,這種情況下,你是無法擷取具體的實現代碼了。
然而,BCL中有相當一部分方法使用extern和MethodImplAttribute來修飾的,此時,只要給MethodImplAttribute的建構函式傳遞的參數是MethodImplOptions.InternalCall枚舉,就代表著你可以使用我介紹給你的方法來尋找實現代碼了。底線是你擁有這些底層非託管原始碼讓你尋找!
Q:MethodImplAttribute用來幹什麼的?
A:該Attribute位於System.Runtime.CompilerServices命名空間,結合MethodImplOptions枚舉來使用,用於描述方法或者構造器的實現方式。Code #01中的[MethodImplAttribute(MethodImplOptions.InternalCall)]則用於說明該方法的具體實現可以從CLR內部找到。該屬性實質上是一種偽屬性(pseudo-attribute),有別於普通的自訂屬性,它是以位的形式儲存在中繼資料表(metadata table)中的,並且只能通過MethodBase.GetMethodImplementationFlags來擷取相關的資訊。
Q:從上面的討論,我們可以瞭解到EqualsString函數是屬於一個叫ComString的類,那麼這個ComString類與BCL中的String類有什麼關係?
A:Good question!Rotor BCL中的String.cs中我們可以找到這樣一句話:
Actual implementations are in String.cpp
事實上,這個String.cpp就是comstring.cpp;同樣,在comstring.cpp中,我們也可以看到這樣一句話:
Purpose: The implementation of the String class.
我們發現String.cs中很多方法都沒有提供具體的實現代碼,而這些方法的真身其實就隱藏在comstring.cpp中。
Q:最後,對於探索.NET內部原理,你有什麼好推薦?
A:其實微軟所提供的Rotor原始碼以及Mono這個開源項目裡面已經有很豐富的注釋以供學習之用。當然,我還是要為大家介紹幾個有用的串連:
- Rotor Source code:該網站以網頁的的形式把Rotor BCL的代碼展示出來,相關的代碼使用C#語言。
- Rotor VM Documentation:該網站以網頁的形式把Rotor VM的代碼展示出來,相關的代碼使用C++語言。
- Jason Whittington, Shared Source CLI Provides Source Code for a FreeBSD Implementation of .NET。
希望這些資料能夠協助大家更好的探索.NET的內部原理。