У меня есть рекурсивная функция emit: Map
, где il: ILGenerator
является глобальным для функции, а exp
- дискриминантное объединение, представляющее анализируемый язык с проверкой типа с case InstanceCall exp * MethodInfo * exp list * Тип
и Тип
- это свойство на exp
, представляющее Тип выражения.
В следующем фрагменте я пытаюсь испустить коды операций IL для экземпляра вызовите where instance.Type
может быть или не быть ValueType
. Итак, я понимаю, что могу использовать OpCodes.Constrained
, чтобы гибко и эффективно выполнять виртуальные вызовы для типов ссылок, значений и перечислений. Я новичок в Reflection.Emit и машинных языках в целом, поэтому понимание связанной документации для OpCodes.Constrained
не является сильным для меня.
Вот моя попытка, но она приводит к VerificationException
, «Операция может дестабилизировать среду выполнения»:
let rec emit lenv ast =
match ast with
...
| InstanceCall(instance,methodInfo,args,_) ->
instance::args |> List.iter (emit lenv)
il.Emit(OpCodes.Constrained, instance.Type)
il.Emit(OpCodes.Callvirt, methodInfo)
...
Глядя на документы, я думаю, что ключ может быть следующим: «Управляемый указатель ptr помещается в стек. Тип ptr должен быть управляемым указателем. (&) на thisType.Обратите внимание, что это отличается от случая с инструкцией callvirt без префикса, которая ожидает ссылку на thisType. "
Обновление
Спасибо @Tomas и @desco, теперь я понимаю, когда использовать OpCodes.Constrained
( instance.Type
- это ValueType, но methodInfo.DeclaringType
- ссылочный тип).
Но, оказывается, мне пока не нужно рассматривать этот случай , и моей реальной проблемой был аргумент экземпляра в стеке: мне потребовалось всего 6 часов, чтобы узнать, что ему нужен адрес вместо значения (просмотр исходного кода DLR дал мне подсказки, а затем использование ilasm.exe на простом C # программа прояснила).
Вот моя последняя рабочая версия:
let rec emit lenv ast =
match ast with
| Int32(x,_) ->
il.Emit(OpCodes.Ldc_I4, x)
...
| InstanceCall(instance,methodInfo,args,_) ->
emit lenv instance
//if value type, pop, put in field, then load the field address
if instance.Type.IsValueType then
let loc = il.DeclareLocal(instance.Type)
il.Emit(OpCodes.Stloc, loc)
il.Emit(OpCodes.Ldloca, loc)
for arg in args do emit lenv arg
if instance.Type.IsValueType then
il.Emit(OpCodes.Call, methodInfo)
else
il.Emit(OpCodes.Callvirt, methodInfo)
...