У меня есть рекурсивная функция emit : Map<string,LocalBuilder> -> exp -> unit
, где il : ILGenerator
является глобальной функцией, а exp
- это дискриминантный союз, представляющий язык синтаксического анализа с проверкой типа, с регистром InstanceCall of exp * MethodInfo * exp list * Type
и Type
- свойством * 1006.* представляющий тип выражения.
В следующем фрагменте я пытаюсь генерировать коды операций IL для вызова экземпляра, где 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 должен быть управляемым указателем (&) to 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)
...