Я играл с использованием PowerShell для передачи CIL в DynamicMethod, а затем запуска его, и основные операции работают, так что я уверен, что подход в порядке.Я могу поместить значения int и string в стек и распечатать их, что включает в себя поиск правильных перегрузок [console]::WriteLine([string])
и [console]::WriteLine([int32])
- то есть получение всех перегрузок и фильтрацию по их типам параметров.
Теперь я пытаюсь вызвать [String]::Join()
, но это универсальный метод, и его необходимо набрать, чтобы объединить массив [int32[]]
.Разборка AC # показывает, что инструкции 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)
Это выдвигает разделитель, загружает ссылку на массив, вызывает соединение, (сохраняет / загружает строку результата), вызывает 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 вне границ).И опкоды, которые я посылаю, выглядят как разборка C # для хранения значений в массиве.
Итак, каков правильный код PowerShell для передачи вызова String.Join, набранного в int32, как видно из разборки C #?