Create weak delegate of Dynamic Method(ref object,object[] arr)

0

Source code:https://www.pastefile.com/4mzhyg

I'm trying to create delegate of format:

    public delegate TReturn MethodCallerR<TTarget, TReturn>(ref TTarget target, object[] args);

 /// <summary>
    /// Generates a strongly-typed open-instance delegate to invoke the specified method
    /// </summary>
    public static MethodCallerR<TTarget, TReturn> DelegateForCallR<TTarget, TReturn>(this MethodInfo method) {

        int key = GetKey<TTarget, TReturn>(method, kMethodCallerName);
        Delegate result;
        if (cache.TryGetValue(key, out result))
            return (MethodCallerR<TTarget, TReturn>)result;

        return GenDelegateForMember<MethodCallerR<TTarget, TReturn>, MethodInfo>(
                method, key, kMethodCallerName, GenMethodInvocationR<TTarget>,
                typeof(TReturn), typeof(TTarget).MakeByRefType(), typeof(object[]));
    }

weak type function:

    public static MethodCallerR<object, object> DelegateForCallR(this MethodInfo method) {
        return DelegateForCallR<object, object>(method);
    }

delegate creator:

   static TDelegate GenDelegateForMember<TDelegate, TMember>(TMember member, int key, string dynMethodName,
            Action<TMember> generator, Type returnType, params Type[] paramTypes)
    where TMember : MemberInfo
    where TDelegate : class {
        var dynMethod = new DynamicMethod(dynMethodName, returnType, paramTypes, true);

        emit.il = dynMethod.GetILGenerator();
        generator(member);

        var result = dynMethod.CreateDelegate(typeof(TDelegate));
        cache[key] = result;
        return (TDelegate)(object)result;
    }

and IL code generator:

 static void GenMethodInvocationR<TTarget>(MethodInfo method) {



        var weaklyTyped = typeof(TTarget) == typeof(object);



        // push arguments in order to call method
        var prams = method.GetParameters();
        var imax = prams.Length;
        for (int i = 0; i < imax; i++) {

            emit.ldarg1()        // stack<= paramsValuesArray[] //push array
            .ldc_i4(i)        // stack<= index push(index)
            .ldelem_ref();    // stack[top]<=paramsValuesArray[i]

            var param = prams[i];
            var dataType = param.ParameterType;

            if (dataType.IsByRef)
                dataType = dataType.GetElementType();

            emit.unbox_any(dataType);

            emit.declocal(dataType);

            emit.stloc(i);

        }



        if (!method.IsStatic)
        {
            var targetType = weaklyTyped ? method.DeclaringType : typeof(TTarget);
            emit.ldarg0();  //stack[top]=target;
            emit.ldind_ref();//stack[top]=ref target;
            if (weaklyTyped)
                emit.unbox_any(targetType); //stack[top]=(TargetType)target;
        }


        //load parms from local 'list' to evaluation 'steak'
        for (int i = 0; i < imax; i++) {
            var param = prams[i];

            emit.ifbyref_ldloca_else_ldloc(i, param.ParameterType);
        }

        // perform the correct call (pushes the result)
        emit.callorvirt(method);


        //check of ref and out params and
        for (int i = 0; i < prams.Length; i++) {

            var paramType = prams[i].ParameterType;
            if (paramType.IsByRef)
            {
                var byRefType = paramType.GetElementType();
                emit.ldarg1() // stack<= paramsValuesArray[]
                .ldc_i4(i) // stack<= i //push(index)
                .ldloc(i); // stack<= list[i] //push local list element at 'i' on steak
                if (byRefType.IsValueType)
                    emit.box(byRefType);   // if ex. stack[top] =(object)stack[top]
                emit.stelem_ref(); //  // paramsValuesArray[i]= pop(stack[top]);
            }
        }

        if (method.ReturnType == typeof(void))
            emit.ldnull();
        else if (weaklyTyped)
            emit.ifvaluetype_box(method.ReturnType);

        emit.ret();


    }

Example for stuct I'm using is Vector3 with Set method:

  public struct Vector3{
    public float x;
    public float y;
    public float z;

    public Vector3(float x,float y,float z){
        this.x=x;
        this.y=y;
        this.z=z;
    }

    public void Set(float x,float y,float z){
        this.x=x;
        this.y=y;
        this.z=z;
    }
}

so I'm doing:

   object vector3Obj=new Vector3(4,5,6);
    MethodInfo method=typeof(Vector3).GetMethod("Set");
      MethodCallerR<object,object> m = methodInfo.DelegateForCallR();
             m(ref vector3Obj,new object[]{1f,2f,3f});
    Console.Write(vector3.x);

Never get to the executing delegate, but it blows when it calls Delegate.CreateDelegate:

see line: dynMethod.CreateDelegate(typeof(TDelegate));

with error:

InvalidProgramException: Invalid IL code in (wrapper dynamic-method) object:MC<> (object&,object[]): IL_004f: call 0x00000009 refereeing that actual IL code has error at emit.call(method), but when I use helper function:

    FastReflection.GenDebugAssembly<object>("my.dll",null,null,methodInfo,vector3Obj.GetType(),new Type[]{typeof(float),typeof(float),typeof(float)});

which will generate my.dll and open with ILSpy I can see that method from same IL code is generated just fine.

    public static object MethodCallerR(ref object ptr, object[] array)
{
    float num = (float)array[0];
    float num2 = (float)array[1];
    float num3 = (float)array[2];
    ((Vector3)ptr).Set(num, num2, num3);
    return null;
}
c#
reflection
delegates
dynamicmethod
asked on Stack Overflow May 22, 2019 by user999913 • edited May 28, 2019 by user999913

1 Answer

0

I found solution. First although generated method was correct wasn't setting reference passed. So I tried to generated another method.

public static object MethodCallerR(ref object ptr, object[] array)
{
    float num = (float)array[0];
    float num2 = (float)array[1];
    float num3 = (float)array[2];
    Vector3 vector = (Vector3)ptr;
    vector.Set(num, num2, num3);
    ptr = vector;
    return null;
}

So I changed IL code to:

      static void GenMethodInvocationR<TTarget>(MethodInfo method) {





        //this version winxalex generates more optimized code
        //Generated
        //            public static object MethodCallerR(ref object ptr, object[] array)
        //            {
        //                float num = (float)array[0];
        //                float num2 = (float)array[1];
        //                float num3 = (float)array[2];
        //                Vector3 vector = (Vector3)ptr;
        //                vector.Set(num, num2, num3);
        //                ptr = vector;
        //                return null;
        //            }
        //
        var weaklyTyped = typeof(TTarget) == typeof(object);
        var targetType = weaklyTyped ? method.DeclaringType : typeof(TTarget);
        var isNotStatic = !method.IsStatic;
        LocalBuilder targetLocal = null;

        // push arguments in order to call method
        var prams = method.GetParameters();
        var imax = prams.Length;
        for (int i = 0; i < imax; i++) {

            emit.ldarg1()        // stack<= paramsValuesArray[] //push array
            .ldc_i4(i)        // stack<= index push(index)
            .ldelem_ref();    // stack[top]<=paramsValuesArray[i]

            var param = prams[i];
            var dataType = param.ParameterType;

            if (dataType.IsByRef)
                dataType = dataType.GetElementType();

            emit.unbox_any(dataType);

            emit.declocal(dataType);

            emit.stloc(i);

        }



        if (isNotStatic)
        {

            emit.ldarg0();  //stack[top]=target;
            emit.ldind_ref();//stack[top]=ref target;
            if (weaklyTyped)
                emit.unbox_any(targetType); //stack[top]=(TargetType)target;

            targetLocal = emit.declocal(targetType); //TargetType tmpTarget; list[0]=tmpTarget;

            emit.stloc(targetLocal)     //list[0]=stack.pop();
            .ifclass_ldloc_else_ldloca(targetLocal, targetType);
        }


        //load parms from local 'list' to evaluation 'steak'
        for (int i = 0; i < imax; i++) {
            var param = prams[i];

            emit.ifbyref_ldloca_else_ldloc(i, param.ParameterType);
        }

        // perform the correct call (pushes the result)
        emit.callorvirt(method);


        if (isNotStatic && targetType.IsValueType) {
            emit.ldarg0().ldloc(targetLocal).box(targetType).stind_ref();
        }


        //check of ref and out params and
        for (int i = 0; i < prams.Length; i++) {

            var paramType = prams[i].ParameterType;
            if (paramType.IsByRef)
            {
                var byRefType = paramType.GetElementType();
                emit.ldarg1() // stack<= paramsValuesArray[]
                .ldc_i4(i) // stack<= i //push(index)
                .ldloc(i); // stack<= list[i] //push local list element at 'i' on steak

                if (byRefType.IsValueType)
                    emit.box(byRefType);   // if ex. stack[top] =(object)stack[top]
                emit.stelem_ref(); //  // paramsValuesArray[i]= pop(stack[top]);
            }
        }

        if (method.ReturnType == typeof(void))
            emit.ldnull();
        else if (weaklyTyped)
            emit.ifvaluetype_box(method.ReturnType);

        emit.ret();



        //vexe orignial modified by winxalex to use reference of object (ref obj) and to return value to reference

        //GENERATES
        //            public static object MethodCallerR(ref object ptr, object[] array)
        //            {
        //                Vector3 vector = (Vector3)ptr;
        //                float num = (float)array[0];
        //                float arg_56_1 = num;
        //                float num2 = (float)array[1];
        //                float arg_56_2 = num2;
        //                float num3 = (float)array[2];
        //                vector.Set(arg_56_1, arg_56_2, num3);
        //                ptr = vector;
        //                return null;
        //            }


        //            var weaklyTyped = typeof(TTarget) == typeof(object);
        //            var targetType = weaklyTyped ? method.DeclaringType : typeof(TTarget);
        //            // push target if not static (instance-method. in that case first arg is always 'this')
        //            if (!method.IsStatic)
        //            {
        //
        //                emit.declocal(targetType); //TargetType tmpTarget; list[0]=tmpTarget;
        //                emit.ldarg0();  //stack[0]=target;
        //                emit.ldind_ref();//stack[top]=ref target;
        //                if (weaklyTyped)
        //                    emit.unbox_any(targetType); //stack[0]=(TargetType)target;
        //                emit.stloc0()     //list[0]=stack.pop();
        //                .ifclass_ldloc_else_ldloca(0, targetType);
        //                // if (type.IsValueType) stack[0]=list[0].address, else stack[0]=list[0];
        //                // if (type.IsValueType) emit.ldloca(idx); else emit.ldloc(idx); return this;
        //            }
        //
        //            // if method wasn't static that means we declared a temp local to load the target
        //            // that means our local variables index for the arguments start from 1
        //            int localVarStart = method.IsStatic ? 0 : 1;
        //
        //            // push arguments in order to call method
        //            var prams = method.GetParameters();
        //            for (int i = 0, imax = prams.Length; i < imax; i++) {
        //                emit.ldarg1()        // stack<= paramsValuesArray //push array
        //                .ldc_i4(i)        // stack<= index push index
        //                .ldelem_ref();    // pop array, index and push array[index]
        //
        //                var param = prams[i];
        //                var dataType = param.ParameterType;
        //
        //                if (dataType.IsByRef)
        //                    dataType = dataType.GetElementType();
        //
        //                var tmp = emit.declocal(dataType);
        //                emit.unbox_any(dataType)
        //                .stloc(tmp)
        //                .ifbyref_ldloca_else_ldloc(tmp, param.ParameterType);
        //
        ////v2
        //
        //
        ////                emit.unbox_any(dataType);
        ////
        ////                emit.declocal(dataType);
        ////                emit.stloc(i+localVarStart)
        ////                .ifbyref_ldloca_else_ldloc(i+localVarStart, param.ParameterType);
        //
        //
        //            }
        //
        //            // perform the correct call (pushes the result)
        //            emit.callorvirt(method);
        //
        //
        //            if (!method.IsStatic && targetType.IsValueType)
        //                emit.ldarg0().ldloc0().box(targetType).stind_ref();
        //
        //
        //            for (int i = 0; i < prams.Length; i++) {
        //                var paramType = prams[i].ParameterType;
        //                if (paramType.IsByRef)
        //                {
        //                    var byRefType = paramType.GetElementType();
        //                    emit.ldarg1() // stack<= params[]
        //                    .ldc_i4(i) // stack<= i
        //                    .ldloc(i + localVarStart); // stack<= list[i+localVarStart]
        //                    if (byRefType.IsValueType)
        //                        emit.box(byRefType);   // if ex. stack =(object)stack[top]
        //                    emit.stelem_ref(); //  // stack:paramsValuesArray[i]= list[i+localVarStart];
        //                }
        //            }
        //
        //            if (method.ReturnType == typeof(void))
        //                emit.ldnull();
        //            else if (weaklyTyped)
        //                emit.ifvaluetype_box(method.ReturnType);
        //
        //            emit.ret();


    }
answered on Stack Overflow May 28, 2019 by user999913

User contributions licensed under CC BY-SA 3.0