Как генерировать OpCodes.Constrained с OpCodes.Callvirt при условии, что у меня есть необходимые MethodInfo и тип экземпляра под рукой

У меня есть рекурсивная функция emit: Map -> exp -> unit , где 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)
        ...

5
задан Stephen Swensen 30 July 2011 в 20:27
поделиться