Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

[OpenSource] Fast.Reflection: delegates for lightning fast metadata reflection!

Discussion in 'Assets and Asset Store' started by vexe, Mar 1, 2015.

  1. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Huh, what? Assembly Broswer is still there in 5.9.6! Where did you get that from?
     
  2. winxalex

    winxalex

    Joined:
    Jun 29, 2014
    Posts:
    166
    I'm using dynamic Delegate creation in Editor and worked fine. When converted in .cpp(IL2CPP set) and run on IOS I get error
    NullReferenceException: A null value was found where an object instance was required.

    at System.Linq.Expressions.Expression.CreateExpressionOf (System.Type type, System.Linq.Expressions.Expression body, System.Collections.ObjectModel.ReadOnlyCollection`1 parameters) [0x00000] in <filename unknown>:0

    at System.Linq.Expressions.Expression.Lambda (System.Type delegateType, System.Linq.Expressions.Expression body, IEnumerable`1 parameters) [0x00000] in <filename unknown>:0

    at System.Linq.Expressions.Expression.Lambda (System.Type delegateType, System.Linq.Expressions.Expression body, System.Linq.Expressions.ParameterExpression[] parameters) [0x00000] in <filename unknown>:0

    at ws.winx.csharp.extensions.ReflectionExtension.GetGetFieldDelegate[TSource,TValue] (System.Reflection.FieldInfo fieldInfo) [0x00000] in <filename unknown>:0

    at ws.winx.unity.UnityEventEx`1[T1].get_InvokableCallUnityAction () [0x00000] in <filename unknown>:0

    at ws.winx.unity.UnityEventEx`1[T1].InvokeOnTarget (UnityEngine.GameObject target, .T1 arg0) [0x00000] in <filename unknown>:0

    Doesn't say unsupported or something.





    Code (CSharp):
    1.  
    2. static FieldInfo DelegateFieldInfo{
    3. get{
    4. if(_DelegateFieldInfo==null){
    5. TypetypeMain=Type.GetType("UnityEngine.Events.InvokableCall`1,UnityEngine");
    6.  
    7. _DelegateFieldInfo=typeMain.MakeGenericType(newType[]{typeof(T1)}).GetField("Delegate",BindingFlags.Instance | BindingFlags.NonPublic);
    8.  
    9. }
    10.  
    11. return_DelegateFieldInfo;
    12. }
    13.  
    14. }
    15.  
    16.  
    17.  
    18.  
    19. DelegateFieldInfo.GetGetFieldDelegate<object,UnityAction<T1>> ()


    Code (CSharp):
    1.     public static Func<TSource, TValue> GetGetFieldDelegate<TSource, TValue> (this FieldInfo fieldInfo)
    2.                 {
    3.                         if (fieldInfo == null)
    4.                                 throw new ArgumentNullException ("fieldInfo");
    5.            
    6.                         Type fieldDeclaringType = fieldInfo.DeclaringType;
    7.            
    8.                         ParameterExpression sourceParameter =
    9.                 Expression.Parameter (typeof(TSource), "source");
    10.            
    11.            
    12.                         Expression sourceExpression = GetCastOrConvertExpression (
    13.                 sourceParameter, fieldDeclaringType);
    14.            
    15.                         MemberExpression fieldExpression = Expression.Field (sourceExpression, fieldInfo);
    16.            
    17.                         Expression resultExpression = GetCastOrConvertExpression (
    18.                 fieldExpression, typeof(TValue));
    19.            
    20.                         LambdaExpression lambda =
    21.                 Expression.Lambda (typeof(Func<TSource, TValue>), resultExpression, sourceParameter);
    22.            
    23.                         return  (Func<TSource, TValue>)lambda.Compile ();
    24.            
    25.                 }
    26. public static Expression GetCastOrConvertExpression(thisExpressionexpression,TypetargetType)
    27. {
    28. Expressionresult;
    29. TypeexpressionType=expression.Type;
    30.  
    31. //Checkifacastorconversionisrequired.
    32. //if(targetType.IsAssignableFrom(expressionType))
    33. //object.IsAssignableForm(System.Single)givestrue;
    34. if(targetType !=typeof(object)&&targetType.IsAssignableFrom(expressionType)){
    35. result=expression;
    36. }else{
    37. //Checkifwecanusetheasoperatorforcastingorifwemustusetheconvert method
    38. if(targetType.IsValueType&& !IsNullableType(targetType)){
    39. result=Expression.Convert(expression,targetType);
    40. }else{
    41. result=Expression.TypeAs(expression,targetType);
    42. }
    43. }
    44.  
    45. returnresult;
    46. }
    47.        
    48.  
    49.  
    50.  
    51.  
    @vexe u advice I generate Delegate in .cs code or even manually for testing???

    Not sure if the problem is of IOS not support or something not being initialized before/correctly.

    When I switch to Mono.2x there is Delegate.CreateDelegate error.

    I'll add manual Create to see what will happen
     
  3. winxalex

    winxalex

    Joined:
    Jun 29, 2014
    Posts:
    166
    Yes it is but not decompiled sources can be seen on Mac only definitions like in VS
     
  4. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    You mean native sources? o_O
    Did it ever do that? Didn't know of it lol;)
     
  5. winxalex

    winxalex

    Joined:
    Jun 29, 2014
    Posts:
    166
    Only way we learn how things work under.... ;)
     
  6. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    I've actually never made use of it at all and still got some things to work. I thought you would need an external tool... which I didn't want to use, I've found all the informations I want either by analysing the outputs of the native code or similar :| Good to know, I understand that's bad that they took it out:(
     
  7. winxalex

    winxalex

    Joined:
    Jun 29, 2014
    Posts:
    166
    It is great. 2 things I noticed dough that I can improve.
    1) for out params ex. RaycastHit& in ordinery Delegate you can send null e.q new object[]{somehtin1, null<-- this is out,somehting}. Fast reflect to init new object[]{somehtin1, new RaycastHit,....
    2) if you send for example Rigidbody2.MovePosition( Vector3<--- instead of Vector2... implicit conversion doesnt' work cos of course IL know boxing/unboxing the methodCaller would blow vs normal function call??
    3) I now I now I said 2, but don't know how this will work with Extension methods???

    What you think?


    THx alot for this. You are one of the few. DEF.
     
  8. NFMynster

    NFMynster

    Joined:
    Jul 1, 2013
    Posts:
    66
    Did you get it working with IL2CPP? @winxalex
     
  9. winxalex

    winxalex

    Joined:
    Jun 29, 2014
    Posts:
    166
    never tried
     
  10. winxalex

    winxalex

    Joined:
    Jun 29, 2014
    Posts:
    166
    @vexe Thx for great library.Fast reflect doesn't keep ref to instance of type struct and make copy of it. Example if you make MemberSetter of Vector3 struct let say "y".Note my system is dynamic(don't know the types before runtime) so I use weak versions.
    Code (CSharp):
    1. var vec=new Vector(1,2,3);
    2. MemberSetter<object,object> setter=typof(Vector3).GetField("y");
    3. setter(ref vec,5); //inside will create copy of 'vec" and assign 5
    4. Debug.Log(vec.y); won't be 5
    field.



    I made changes to fix it.
    Code (CSharp):
    1.      
    2. in
    3. GenMemberSetter method
    4. //by @winxalex
    5.             // we're a value-type
    6.             // handle boxing/unboxing for the user so he doesn't have to do it himself
    7.             // here's what we're basically generating (remember, we're weakly typed, so
    8.             // the target argument is of type object here):
    9.             // (TargetType)target;  = (MemberFieldType)value;        // set member value
    10.  
    11.  
    12.  
    13.             emit.ldarg0()
    14.             .ldind_ref()
    15.             .unbox(targetType)
    16.             .ldarg1()
    17.             .unbox_any(memberType)//you do need unboxing only of value types maybe check if memberType.IsValueType
    18.             .perform(set, member)
    19.             .ret();
    20.  
    Code (CSharp):
    1.    
    2.             //original Vexe code
    3.             // we're a value-type
    4.             // handle boxing/unboxing for the user so he doesn't have to do it himself
    5.             // here's what we're basically generating (remember, we're weakly typed, so
    6.             // the target argument is of type object here):
    7.             // TargetType tmp = (TargetType)target; // unbox
    8.             // tmp.member = (MemberFieldType)value;        // set member value
    9.             // target = tmp;                        // box back
    10.  
    11.             emit.declocal(targetType);
    12.             emit.ldarg0()
    13.             .ldind_ref()
    14.             .unbox_any(targetType)
    15.             .stloc0()
    16.             .ldloca(0)
    17.             .ldarg1()
    18.             .unbox_any(memberType)
    19.             .perform(set, member)
    20.             .ldarg0()
    21.             .ldloc0()
    22.             .box(targetType)
    23.             .stind_ref()
    24.             .ret();
     
    Last edited: May 20, 2019
  11. winxalex

    winxalex

    Joined:
    Jun 29, 2014
    Posts:
    166
    @vexe for cashing why you don't use simple MemberInfo as key?
     
  12. winxalex

    winxalex

    Joined:
    Jun 29, 2014
    Posts:
    166
    I wanted to create delegate of type
    Code (CSharp):
    1.  public delegate TReturn MethodCallerR<TTarget, TReturn>(ref TTarget target, object[] args);
    so I can pass ref of struct, ex Vector3 and can call function Set(x,y,z) for example, so it apply on the reference of the vector3, not to apply to a copy of the structure as in original @vexe code.
    So

    Code (CSharp):
    1.  static void GenMethodInvocationR<TTarget>(MethodInfo method) {
    2.  
    3.  
    4.  
    5.             var weaklyTyped = typeof(TTarget) == typeof(object);
    6.  
    7.  
    8.  
    9.             // push arguments in order to call method
    10.             var prams = method.GetParameters();
    11.             var imax = prams.Length;
    12.             for (int i = 0; i < imax; i++) {
    13.  
    14.                 emit.ldarg1()        // stack<= paramsValuesArray[] //push array
    15.                 .ldc_i4(i)        // stack<= index push(index)
    16.                 .ldelem_ref();    // stack[top]<=paramsValuesArray[i]
    17.  
    18.                 var param = prams[i];
    19.                 var dataType = param.ParameterType;
    20.  
    21.                 if (dataType.IsByRef)
    22.                     dataType = dataType.GetElementType();
    23.  
    24.                 emit.unbox_any(dataType);
    25.  
    26.                 emit.declocal(dataType);
    27.  
    28.                 emit.stloc(i);
    29.  
    30.             }
    31.  
    32.  
    33.  
    34.             if (!method.IsStatic)
    35.             {
    36.                 var targetType = weaklyTyped ? method.DeclaringType : typeof(TTarget);
    37.                 emit.ldarg0();  //stack[top]=target;
    38.                 emit.ldind_ref();//stack[top]=ref target;
    39.                 if (weaklyTyped)
    40.                     emit.unbox_any(targetType); //stack[top]=(TargetType)target;
    41.             }
    42.  
    43.  
    44.             //load parms from local 'list' to evaluation 'steak'
    45.             for (int i = 0; i < imax; i++) {
    46.                 var param = prams[i];
    47.  
    48.                 emit.ifbyref_ldloca_else_ldloc(i, param.ParameterType);
    49.             }
    50.  
    51.             // perform the correct call (pushes the result)
    52.             emit.callorvirt(method);
    53.  
    54.  
    55.             //check of ref and out params and
    56.             for (int i = 0; i < prams.Length; i++) {
    57.  
    58.                 var paramType = prams[i].ParameterType;
    59.                 if (paramType.IsByRef)
    60.                 {
    61.                     var byRefType = paramType.GetElementType();
    62.                     emit.ldarg1() // stack<= paramsValuesArray[]
    63.                     .ldc_i4(i) // stack<= i //push(index)
    64.                     .ldloc(i); // stack<= list[i] //push local list element at 'i' on steak
    65.                     if (byRefType.IsValueType)
    66.                         emit.box(byRefType);   // if ex. stack[top] =(object)stack[top]
    67.                     emit.stelem_ref(); //  // paramsValuesArray[i]= pop(stack[top]);
    68.                 }
    69.             }
    70.  
    71.             if (method.ReturnType == typeof(void))
    72.                 emit.ldnull();
    73.             else if (weaklyTyped)
    74.                 emit.ifvaluetype_box(method.ReturnType);
    75.  
    76.             emit.ret();
    77.  
    78.  
    79.         }

    would create something like
    Code (CSharp):
    1.       public object MyMethod(ref object target,object[] array){
    2.  
    3.             if(target==null) return null;
    4.  
    5.             float num = (float)array[0];
    6.             float num2 = (float)array[1];
    7.             float num3 = (float)array[2];
    8.             ((Vector3)target).Set(num, num2, num3);
    9.             return null;
    10.         }
     
  13. winxalex

    winxalex

    Joined:
    Jun 29, 2014
    Posts:
    166
    This is final result which doesn't throw IL error.
    Code (CSharp):
    1. public static object MethodCallerR(ref object ptr, object[] array)
    2. {
    3.     float num = (float)array[0];
    4.     float num2 = (float)array[1];
    5.     float num3 = (float)array[2];
    6.     Vector3 vector = (Vector3)ptr;
    7.     vector.Set(num, num2, num3);
    8.     ptr = vector;
    9.     return null;
    10. }
    From IL code:
    Code (CSharp):
    1.  static void GenMethodInvocationR<TTarget>(MethodInfo method) {
    2.  
    3.  
    4.  
    5.  
    6.  
    7.         //this version winxalex generates more optimized code
    8.         //Generated
    9.         //            public static object MethodCallerR(ref object ptr, object[] array)
    10.         //            {
    11.         //                float num = (float)array[0];
    12.         //                float num2 = (float)array[1];
    13.         //                float num3 = (float)array[2];
    14.         //                Vector3 vector = (Vector3)ptr;
    15.         //                vector.Set(num, num2, num3);
    16.         //                ptr = vector;
    17.         //                return null;
    18.         //            }
    19.         //
    20.         var weaklyTyped = typeof(TTarget) == typeof(object);
    21.         var targetType = weaklyTyped ? method.DeclaringType : typeof(TTarget);
    22.         var isNotStatic = !method.IsStatic;
    23.         LocalBuilder targetLocal = null;
    24.  
    25.         // push arguments in order to call method
    26.         var prams = method.GetParameters();
    27.         var imax = prams.Length;
    28.         for (int i = 0; i < imax; i++) {
    29.  
    30.             emit.ldarg1()        // stack<= paramsValuesArray[] //push array
    31.             .ldc_i4(i)        // stack<= index push(index)
    32.             .ldelem_ref();    // stack[top]<=paramsValuesArray[i]
    33.  
    34.             var param = prams[i];
    35.             var dataType = param.ParameterType;
    36.  
    37.             if (dataType.IsByRef)
    38.                 dataType = dataType.GetElementType();
    39.  
    40.             emit.unbox_any(dataType);
    41.  
    42.             emit.declocal(dataType);
    43.  
    44.             emit.stloc(i);
    45.  
    46.         }
    47.  
    48.  
    49.  
    50.         if (isNotStatic)
    51.         {
    52.  
    53.             emit.ldarg0();  //stack[top]=target;
    54.             emit.ldind_ref();//stack[top]=ref target;
    55.             if (weaklyTyped)
    56.                 emit.unbox_any(targetType); //stack[top]=(TargetType)target;
    57.  
    58.             targetLocal = emit.declocal(targetType); //TargetType tmpTarget; list[0]=tmpTarget;
    59.  
    60.             emit.stloc(targetLocal)     //list[0]=stack.pop();
    61.             .ifclass_ldloc_else_ldloca(targetLocal, targetType);
    62.         }
    63.  
    64.  
    65.         //load parms from local 'list' to evaluation 'steak'
    66.         for (int i = 0; i < imax; i++) {
    67.             var param = prams[i];
    68.  
    69.             emit.ifbyref_ldloca_else_ldloc(i, param.ParameterType);
    70.         }
    71.  
    72.         // perform the correct call (pushes the result)
    73.         emit.callorvirt(method);
    74.  
    75.  
    76.         if (isNotStatic && targetType.IsValueType) {
    77.             emit.ldarg0().ldloc(targetLocal).box(targetType).stind_ref();
    78.         }
    79.  
    80.  
    81.         //check of ref and out params and
    82.         for (int i = 0; i < prams.Length; i++) {
    83.  
    84.             var paramType = prams[i].ParameterType;
    85.             if (paramType.IsByRef)
    86.             {
    87.                 var byRefType = paramType.GetElementType();
    88.                 emit.ldarg1() // stack<= paramsValuesArray[]
    89.                 .ldc_i4(i) // stack<= i //push(index)
    90.                 .ldloc(i); // stack<= list[i] //push local list element at 'i' on steak
    91.  
    92.                 if (byRefType.IsValueType)
    93.                     emit.box(byRefType);   // if ex. stack[top] =(object)stack[top]
    94.                 emit.stelem_ref(); //  // paramsValuesArray[i]= pop(stack[top]);
    95.             }
    96.         }
    97.  
    98.         if (method.ReturnType == typeof(void))
    99.             emit.ldnull();
    100.         else if (weaklyTyped)
    101.             emit.ifvaluetype_box(method.ReturnType);
    102.  
    103.         emit.ret();
    104.  
    105.  
    106.  
    107.         //vexe orignial modified by winxalex to use reference of object (ref obj) and to return value to reference
    108.  
    109.         //GENERATES
    110.         //            public static object MethodCallerR(ref object ptr, object[] array)
    111.         //            {
    112.         //                Vector3 vector = (Vector3)ptr;
    113.         //                float num = (float)array[0];
    114.         //                float arg_56_1 = num;
    115.         //                float num2 = (float)array[1];
    116.         //                float arg_56_2 = num2;
    117.         //                float num3 = (float)array[2];
    118.         //                vector.Set(arg_56_1, arg_56_2, num3);
    119.         //                ptr = vector;
    120.         //                return null;
    121.         //            }
    122.  
    123.  
    124.         //            var weaklyTyped = typeof(TTarget) == typeof(object);
    125.         //            var targetType = weaklyTyped ? method.DeclaringType : typeof(TTarget);
    126.         //            // push target if not static (instance-method. in that case first arg is always 'this')
    127.         //            if (!method.IsStatic)
    128.         //            {
    129.         //
    130.         //                emit.declocal(targetType); //TargetType tmpTarget; list[0]=tmpTarget;
    131.         //                emit.ldarg0();  //stack[0]=target;
    132.         //                emit.ldind_ref();//stack[top]=ref target;
    133.         //                if (weaklyTyped)
    134.         //                    emit.unbox_any(targetType); //stack[0]=(TargetType)target;
    135.         //                emit.stloc0()     //list[0]=stack.pop();
    136.         //                .ifclass_ldloc_else_ldloca(0, targetType);
    137.         //                // if (type.IsValueType) stack[0]=list[0].address, else stack[0]=list[0];
    138.         //                // if (type.IsValueType) emit.ldloca(idx); else emit.ldloc(idx); return this;
    139.         //            }
    140.         //
    141.         //            // if method wasn't static that means we declared a temp local to load the target
    142.         //            // that means our local variables index for the arguments start from 1
    143.         //            int localVarStart = method.IsStatic ? 0 : 1;
    144.         //
    145.         //            // push arguments in order to call method
    146.         //            var prams = method.GetParameters();
    147.         //            for (int i = 0, imax = prams.Length; i < imax; i++) {
    148.         //                emit.ldarg1()        // stack<= paramsValuesArray //push array
    149.         //                .ldc_i4(i)        // stack<= index push index
    150.         //                .ldelem_ref();    // pop array, index and push array[index]
    151.         //
    152.         //                var param = prams[i];
    153.         //                var dataType = param.ParameterType;
    154.         //
    155.         //                if (dataType.IsByRef)
    156.         //                    dataType = dataType.GetElementType();
    157.         //
    158.         //                var tmp = emit.declocal(dataType);
    159.         //                emit.unbox_any(dataType)
    160.         //                .stloc(tmp)
    161.         //                .ifbyref_ldloca_else_ldloc(tmp, param.ParameterType);
    162.         //
    163.         ////v2
    164.         //
    165.         //
    166.         ////                emit.unbox_any(dataType);
    167.         ////
    168.         ////                emit.declocal(dataType);
    169.         ////                emit.stloc(i+localVarStart)
    170.         ////                .ifbyref_ldloca_else_ldloc(i+localVarStart, param.ParameterType);
    171.         //
    172.         //
    173.         //            }
    174.         //
    175.         //            // perform the correct call (pushes the result)
    176.         //            emit.callorvirt(method);
    177.         //
    178.         //
    179.         //            if (!method.IsStatic && targetType.IsValueType)
    180.         //                emit.ldarg0().ldloc0().box(targetType).stind_ref();
    181.         //
    182.         //
    183.         //            for (int i = 0; i < prams.Length; i++) {
    184.         //                var paramType = prams[i].ParameterType;
    185.         //                if (paramType.IsByRef)
    186.         //                {
    187.         //                    var byRefType = paramType.GetElementType();
    188.         //                    emit.ldarg1() // stack<= params[]
    189.         //                    .ldc_i4(i) // stack<= i
    190.         //                    .ldloc(i + localVarStart); // stack<= list[i+localVarStart]
    191.         //                    if (byRefType.IsValueType)
    192.         //                        emit.box(byRefType);   // if ex. stack =(object)stack[top]
    193.         //                    emit.stelem_ref(); //  // stack:paramsValuesArray[i]= list[i+localVarStart];
    194.         //                }
    195.         //            }
    196.         //
    197.         //            if (method.ReturnType == typeof(void))
    198.         //                emit.ldnull();
    199.         //            else if (weaklyTyped)
    200.         //                emit.ifvaluetype_box(method.ReturnType);
    201.         //
    202.         //            emit.ret();
    203.  
    204.  
    205.     }
    @vexe are you alive :D.
     
  14. danUnity

    danUnity

    Joined:
    Apr 28, 2015
    Posts:
    229
    Is it still a usable asset in 2020? I'm surprised there are not more people thanking the author for bringing this to life!