此問題是關於自定義C#IL代碼的靜態堆棧分析以及如何設計操作碼以滿足編譯器。C#IL代碼修改 - 保持堆棧完好
我有代碼修改現有的C#方法通過附加我自己的代碼。爲了避免原始方法在執行代碼之前返回,它會將所有RET操作碼替換爲BR endlabel,並將該標籤添加到原始代碼的末尾。然後我在那裏添加更多的代碼,最後添加一個RET。
這一切工作正常,但在某些方法上失敗。下面是一個簡單的例子:
public static string SomeMethod(int val)
{
switch (val)
{
case 0:
return "string1".convert();
case 1:
return "string2".convert();
case 2:
return "string3".convert();
// ...
}
return "";
}
這是由該IL代碼表示:
.method public hidebysig static string SomeMethod(int32 val) cil managed
{
.maxstack 1
.locals val ([0] int32 num)
L_0000: ldarg.0
L_0001: stloc.0
L_0002: ldloc.0
L_0003: switch (L_002e, L_004f, L_0044, ...)
L_002c: br.s L_0091
L_002e: ldstr "string1"
L_0033: call string Foo::convert(string)
L_0038: ret
L_0039: ldstr "string2"
L_003e: call string Foo::convert(string)
L_0043: ret
L_0044: ldstr "string3"
L_0049: call string Foo::convert(string)
L_004e: ret
...
L_0091: ldstr ""
L_0096: ret
}
我的程序修改了它之後,代碼如下:
.method public hidebysig static string SomeMethod(int32 val) cil managed
{
.maxstack 1
.locals val ([0] int32 num)
L_0000: ldarg.0
L_0001: stloc.0
L_0002: ldloc.0
L_0003: switch (L_002e, L_004f, L_0044, ...)
L_002c: br.s L_0091
L_002e: ldstr "string1"
L_0033: call string Foo::convert(string)
L_0038: br L_009b // was ret
L_0039: ldstr "string2"
L_003e: call string Foo::convert(string)
L_0043: br L_009b // was ret
L_0044: ldstr "string3"
L_0049: call string Foo::convert(string)
L_004e: br L_009b // was ret
...
L_0091: ldstr ""
L_0096: br L_009b // was ret
L_009b: my code here
...
L_0200: ret
}
和我編譯錯誤:
Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for FooBar ---> System.InvalidProgramException: Invalid IL code in (wrapper dynamic-method) Foo:SomeMethod (int): IL_0000: ldnull
是否有任何簡單的方法以通用方式替換RET並保持靜態分析器的快樂?
解決了這個問題。用BR代替RET會增加代碼長度,短跳可能會變得非法。解決方案是用長跳躍替換它們。經過測試和工作。 –
你也可以使用try-finally子句;這將避免你所有的麻煩。當然,如果你想總是執行那些代碼,它也會執行一個異常。根據你注入的代碼,這可能是一件好事或一件壞事。 – Luaan
對你自己的錯誤有很好的理解:)你應該自己回答 –