我想將sql日誌記錄注入到幾個方法中。基本上,我想改造重寫IL以注入try-finally方法調用
public static object IDbCommandTest_ExecuteScalar(IDbCommand command)
{
// .. do stuff
command.CommandText = "SELECT ...";
var obj = command.ExecuteScalar();
Console.WriteLine("SQL returned " + obj);
// do other stuff
return obj;
}
到
public static object IDbCommandTest_ExecuteScalar_Transformed(IDbCommand command)
{
// .. do stuff
command.CommandText = "SELECT ...";
object obj;
using (SqlLogger.Enter(command, "IDbCommand", "ExecuteScalar"))
{
obj = command.ExecuteScalar();
}
Console.WriteLine("SQL returned " + obj);
// do other stuff
return obj;
}
我得到了簡單的情況下工作,我目前面臨的問題是,進入.try
當堆棧必須爲空。
我的基本假設是,IDbCommand
本身調用ExecuteScalar
之前直接加載,所以我搜索callvirt然後用Instruction.Previous
作爲.try
的開始。
但如果ExecuteScalar
返回值後使用,編譯器會生成以下IL:
IL_004d: ldarg.0
IL_004e: ldloc.1
IL_004f: callvirt instance object [System.Data]System.Data.IDbCommand::ExecuteScalar()
IL_0054: call string Class_which_uses_obj::DoStuff(object)
隨着我primitivy算法,這是轉化成
IL_0050: ldarg.0
.try
{
IL_0051: ldloc.1
IL_0052: dup
IL_0053: ldstr "IDbCommand"
IL_0058: ldstr "get_FileName"
IL_005d: call class [mscorlib]System.IDisposable SqlLogger::Enter(class [System.Data]System.Data.IDbCommand, string, string)
IL_0062: stloc.3
IL_0063: callvirt instance object [System.Data]System.Data.IDbCommand::ExecuteScalar()
IL_0068: stloc.s 4
IL_006a: leave.s IL_0077
} // end .try
finally
{
IL_006c: nop
IL_006d: ldloc.3
IL_006e: brfalse.s IL_0076
IL_0070: ldloc.3
IL_0071: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0076: endfinally
} // end handler
IL_0077: nop
IL_0078: ldloc.s 4
IL_007a: call string IL_0050: ldarg.0
.try
{
IL_0051: ldloc.1
IL_0052: dup
IL_0053: ldstr "IDbCommand"
IL_0058: ldstr "ExecuteScalar"
IL_005d: call class [mscorlib]System.IDisposable Nemetschek.Allready.SqlLogger::Enter(class [System.Data]System.Data.IDbCommand, string, string)
IL_0062: stloc.3
IL_0063: callvirt instance object [System.Data]System.Data.IDbCommand::ExecuteScalar()
IL_0068: stloc.s 4
IL_006a: leave.s IL_0077
} // end .try
finally
{
IL_006c: nop
IL_006d: ldloc.3
IL_006e: brfalse.s IL_0076
IL_0070: ldloc.3
IL_0071: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0076: endfinally
} // end handler
IL_0077: nop
IL_0078: ldloc.s 4
IL_007a: call string Nemetschek.Allready.Logistics.DbTools.CDbTools::GetSafeStringEmpty(object)(object)
然後PEVERIFY抱怨我輸入具有非空棧的.try。
是否有一種簡單的方法來將try-finally注入ExecuteScalar,還是需要對整個方法進行完整的流分析,計算任意點的堆棧深度,然後在/之前/之後加載/恢復值試試/最後?
編輯:
我已經能夠得到它通過掃描向上/向下,直到我找到兩個點堆棧深度爲0。在我有限的測試工作,這似乎工作,但我仍然對「乾淨」的實施感興趣,而不是盲目地掃描IL。