我一直在使用PowerShell将CIL发出到DynamicMethod中,然后运行它,并且基本的操作都可以正常工作,因此我相信这种方法是可以的。我可以将int和字符串值压入堆栈并进行打印,这涉及找到[console]::WriteLine([string])
和[console]::WriteLine([int32])
的正确重载-即获取所有重载并按其参数类型进行过滤。
现在我正在尝试调用[String]::Join()
,但这是一个泛型方法,需要键入它才能加入[int32[]]
数组。 C#反汇编显示CIL指令为:
IL_0018: ldstr ", "
IL_001d: ldloc.2
IL_001e: call string [mscorlib]System.String::Join<int32>(string,
class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
IL_0023: stloc.3
IL_0024: ldloc.3
IL_0025: call void [mscorlib]System.Console::WriteLine(string)
这将推动分隔符,加载数组引用,调用join,(存储/加载结果字符串),调用WriteLine。
我的PowerShell(很多代码,因为它在CIL中设置了数组,但是它很小,我可以制作一个完整的可运行示例),如下所示:
using namespace System.Reflection.Emit
# new dynamic method
$methodInfo = new-object Reflection.Emit.DynamicMethod -ArgumentList @('Test', [int], @())
$IL = $methodInfo.GetILGenerator()
# new array of [int32[]] items, stored in a local variable
$arrayVar = $IL.DeclareLocal([int32[]])
$IL.Emit([OpCodes]::Ldc_I4, 2)
$IL.Emit([OpCodes]::Newarr, [int32])
$IL.Emit([OpCodes]::Stloc, $arrayVar)
# Store 4 into index 0
$IL.Emit([OpCodes]::Ldloc, $arrayVar)
$IL.Emit([OpCodes]::Ldc_I4, 0)
$IL.Emit([OpCodes]::Ldc_I4_4)
$IL.Emit([OpCodes]::Stelem, [int32])
# Store 5 into index 1
$IL.Emit([OpCodes]::Ldloc, $arrayVar)
$IL.Emit([OpCodes]::Ldc_I4, 1)
$IL.Emit([OpCodes]::Ldc_I4_5)
$IL.Emit([OpCodes]::Stelem, [int32])
#
# *** question here ***
#
# The bit I can't get to work - [String]::Join('; ', $array)
# Push separator string onto the stack, then load the array, then call the Join method
$IL.Emit([OpCodes]::Ldstr, '; ')
$IL.Emit([OpCodes]::Ldloc, $arrayVar)
$IL.EmitCall([opcodes]::Call, [string].GetDeclaredMethods('Join').where{$_.IsGenericMethod}[0].MakeGenericMethod([int32[]]), $null)
# Should leave a string on the stack; print it.
$IL.EmitCall([OpCodes]::Call, [console].GetDeclaredMethods('WriteLine').where{$p = $_.GetParameters(); $p.Count -eq 1 -and $p.ParameterType -eq [string]}[0], $null)
# return 0
$IL.Emit([OpCodes]::Ldc_I4_0)
$IL.Emit([OpCodes]::Ret)
# run code
$Test = $methodInfo.CreateDelegate([System.Func[[int]]])
$Test.Invoke()
它抛出:
Exception calling "Invoke" with "0" argument(s): "Operation could destabilize the runtime."
如果注释掉加载数组并调用Join的两行,则代码运行良好,仅打印分隔符字符串;
。因此,该部分似乎正常。我相当确定数组设置代码也可以,但是不能完全确定。至少没有正确地获得值和索引(将Index超出范围抛出异常)。而且我发出的OpCode看起来像C#反汇编,用于将值存储在数组中。
那么,什么是正确的PowerShell代码才能发出对String.Join的调用,如在C#反汇编中看到的那样,输入类型为int32?
答案 0 :(得分:0)
需要将其键入数组[int]
中的项目,而不是整个数组[int[]]
。现在,更改我的PowerShell以删除多余的[]
即可:
$IL.EmitCall([opcodes]::Call, [string].GetDeclaredMethods('Join').where{
$_.IsGenericMethod
}[0].MakeGenericMethod([int32]), $null)
我以this article on C-SharpCorner为指导,将DynamicMethod另存为.exe
,然后在其上使用ILDasm将生成的代码与C#版本进行了比较。
我需要的位置:
string [mscorlib]System.String::Join<int32>(string,
class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
我正在得到这个:
string [mscorlib]System.String::Join<int32[]>(string,
class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
特别是Join<int32[]>
而不是Join<int32>
。