How to fix OpCodes.Constrained with OpCodes.Callvirt, given that I have the required method and instance type on hand

I have a recursive function emit : Map<string,LocalBuilder> -> exp -> unit, where it il : ILGeneratoris global for the function, and expthis is a discriminant union representing the type being analyzed with an argument InstanceCall of exp * MethodInfo * exp list * Typeand Typeis a property on exprepresenting the type of expression.

In the following snippet, I try to emit IL code codes to invoke an instance where it instance.Typemay or may not be ValueType. Therefore, I understand that I can use OpCodes.Constrainedflexible and efficiently create virtual calls by types of links, values ​​and enumerations. I am new to Reflection.Emit and machine languages ​​in general, so understanding the related documentation OpCodes.Constrainedis not very strong for me.

Here is my attempt, but this leads to VerificationException, "Operation can destabilize runtime."

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)
    ...

Looking at the docs, I think that the key could be "A managed pointer, ptr, is pushed onto the stack. The ptr type must be a managed pointer (&) to this type. Note that this is different from the case of an unprepared callvirt statement that expects a link to this type. "

Update

@Tomas @desco, , OpCodes.Constrained (instance.Type ValueType, methodInfo.DeclaringType ).

, , : 6 , , ( DLR , , ilasm.exe #, ).

:

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
2

, , , . , OpCodes.Constrained ( ), , Microsoft: -).

, :

// Emit arguments
List<WriteBack> wb = EmitArguments(mi, args);

// Emit the actual call
OpCode callOp = UseVirtual(mi) ? OpCodes.Callvirt : OpCodes.Call;
if (callOp == OpCodes.Callvirt && objectType.IsValueType) {
    // This automatically boxes value types if necessary.
    _ilg.Emit(OpCodes.Constrained, objectType);
}
// The method call can be a tail call if [...]
if ((flags & CompilationFlags.EmitAsTailCallMask) == CompilationFlags.EmitAsTail && 
    !MethodHasByRefParameter(mi)) {
    _ilg.Emit(OpCodes.Tailcall);
}
if (mi.CallingConvention == CallingConventions.VarArgs) {
    _ilg.EmitCall(callOp, mi, args.Map(a => a.Type));
} else {
    _ilg.Emit(callOp, mi);
}

// Emit writebacks for properties passed as "ref" arguments
EmitWriteBack(wb);

, , , - , constrained . , , , () .

+1

: , .

:

IL , ptr . , thisType , , ....

. , , System.Object, IL. , V Object.ToString(), V.ToString(); , box Object.ToString() callvirt. , , , .

( , F # ):

using System;
using System.Reflection;
using System.Reflection.Emit;

public struct EvilMutableStruct
{
    int i;
    public override string ToString()
    {
            i++;
            return i.ToString();
    }
}

class Program
{
    public static void Main()
    {
            var intToString = Make<int>();
            var stringToString = Make<string>();
            var structToString = Make<EvilMutableStruct>();
            Console.WriteLine(intToString(5));
            Console.WriteLine(stringToString("!!!"));   
            Console.WriteLine(structToString (new EvilMutableStruct())); 
    }

    static MethodInfo ToStringMethod = new Func<string>(new object().ToString).Method;
    static MethodInfo ConcatMethod = new Func<string, string, string>(String.Concat).Method;

    // x => x.ToString() + x.ToString()
    private static Func<T, string> Make<T>()
    {
            var dynamicMethod = new DynamicMethod("ToString", typeof(string), new[] {typeof(T)});
            var il = dynamicMethod.GetILGenerator();

            il.Emit(OpCodes.Ldarga_S, 0);
            il.Emit(OpCodes.Constrained, typeof(T));
            il.Emit(OpCodes.Callvirt, ToStringMethod);

            il.Emit(OpCodes.Ldarga_S, 0);
            il.Emit(OpCodes.Constrained, typeof(T));
            il.Emit(OpCodes.Callvirt, ToStringMethod);

            il.Emit(OpCodes.Call, ConcatMethod);

            il.Emit(OpCodes.Ret);
            return (Func<T, string>)dynamicMethod.CreateDelegate(typeof(Func<T, string>));
     }
}

:

55
!!!!!!
12
+3

All Articles