mercredi 22 mai 2019

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

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:

   Vector3 vector3=new Vector3(4,5,6);
    MethodInfo method=typeof(Vector3).GetMethod("Set");
      MethodCallerR<object,object> m = methodInfo.DelegateForCallR();
             m(ref vector3,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,this.__anyObject.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;
}





Aucun commentaire:

Enregistrer un commentaire