2016-12-01 3 views
0

Je crée un système pour stocker les types de valeur (int, byte, structs) sur le tas et pour empêcher la boxe et le déballage des types de valeurs. En effet, la boxe et le déballage constants dans le moteur Unity 3D créent de grands pics d'UC dans notre vaste base de code. VerificationException: une opération peut déstabiliser l'exécution.VerificationException: une opération peut déstabiliser l'exécution. Problèmes avec EmitCall (OpCodes.Call)

L'exception ci-dessus est levée lorsque j'essaie d'appeler la méthode dynamique. La trace de la pile se termine juste avant qu'elle ne passe dans la méthode dynamique et il n'est pas possible de casser l'exécution. Plus d'informations sont fournies dans l'exemple ci-dessous.

void Main() 
{ 
    var fieldInfo = typeof(MyClass).GetMember("Number")[0] as FieldInfo; 
    var pointerSetFunc = CreatePointerFieldSetMethod(fieldInfo); 
    object myClass = new MyClass(); 
    // The exception occurs when invoking the dynamic method. 
    pointerSetFunc(myClass, 0); 
} 

public class MyClass 
{ 
    public byte Number; 
} 

public static Action<object, int> CreatePointerFieldSetMethod(FieldInfo field) 
{ 
    var setMethod = new DynamicMethod("SetFieldFromPointer", typeof(void), new[] { typeof(object), typeof(int) }, true); 
    ILGenerator generator = setMethod.GetILGenerator(); 

    // This returns the correct value. byte CustomBox<byte>.Unbox(Int32 index); 
    var unboxFunc = typeof(CustomBox<>).MakeGenericType(field.FieldType).GetMethod("Unbox", BindingFlags.Static | BindingFlags.Public); 

    // Somewhere in the below code the exception occurs. 
    generator.Emit(OpCodes.Ldarg_1); // This should be the index or "pointer" to pass into the CustomBox.Unbox function. 
    generator.EmitCall(OpCodes.Call, unboxFunc, null); 
    generator.Emit(OpCodes.Stloc_0); // This should be the result of unboxing. 

    // This code does not get called. 
    generator.Emit(OpCodes.Ldarg_0); // This should be the object MyClass. 
    generator.Emit(OpCodes.Ldloc_0); // This should be the value received from the CustomBox.Unbox function. 
    generator.Emit(OpCodes.Stfld, field); // Set the MyClass.Number field. 

    generator.Emit(OpCodes.Ret); 
    return (Action<object, int>)setMethod.CreateDelegate(typeof(Action<object, int>)); 
} 

// The point of this class is to store values types (int, byte, struct, etc..) in an array already on the heap to avoid boxing. 
// Boxing has become an issue on our application. 
public struct CustomBox<T> where T : struct 
{ 
    public static T Unbox(int index) 
    { 
     // TODO: Actually make the unbox code. 
     return default(T); 
    } 
} 

Edit: Heres la méthode que je suis en train de créer et il est généré IL:

private static void SetFieldUsingIndex(object myClass, int index) 
{ 
    byte number = Values<byte>.Unbox(index); 
    ((MyClass)myClass).Number = number; 
} 

/* Generated IL for above method. 
    IL_0000: nop 
    IL_0001: ldarg.1 
    IL_0002: call !0 class CPURaceTest.Values`1<uint8>::Unbox(int32) 
    IL_0007: stloc.0 
    IL_0008: ldarg.0 
    IL_0009: castclass CPURaceTest.MyClass 
    IL_000e: ldloc.0 
    IL_000f: stfld uint8 CPURaceTest.MyClass::Number 
    IL_0014: ret 
*/ 
+0

Vous utilisez un fichier local, mais vous ne l'avez pas défini; et je suis assez sûr que le champ passé n'était pas défini sur 'object', vous devriez lancer l'instance cible sur le type approprié avant d'essayer de définir un champ dessus. –

+0

@BrianReichle C'est bizarre parce que quand j'écris du code et le décompile, l'IL généré ne déclare pas un local. Est-ce que OpCodes.Stloc_0 ne gère pas ça pour moi? Je spécifie de le stocker dans le 0 local qui est déjà défini. –

+0

Vous n'avez pas seulement à déclarer un local si vous utilisez OpCodes.Stloc mais j'ai utilisé OpCodes.Stloc_0. –

Répondre

0

Le problème est que je ne delcaring le j'utilisais local. J'ai également eu besoin de lancer l'objet cible au bon type.

public static Action<object, int> CreatePointerFieldSetMethod(FieldInfo field) 
{ 
    var setMethod = new DynamicMethod("SetFieldFromPointer", typeof(void), new[] { typeof(object), typeof(int) }, true); 
    ILGenerator generator = setMethod.GetILGenerator(); 

    var unboxFunc = typeof(CustomBox<>).MakeGenericType(field.FieldType).GetMethod("Unbox", BindingFlags.Static | BindingFlags.Public); 

    var local = generator.DeclareLocal(field.FieldType); // Delcare a local. 

    generator.Emit(OpCodes.Ldarg_1); 
    generator.EmitCall(OpCodes.Call, unboxFunc, null); 
    generator.Emit(OpCodes.Stloc, local); // Use the declared local. 

    generator.Emit(OpCodes.Ldarg_0); 
    generator.Emit(OpCodes.Castclass, field.DeclaringType); // Added this cast. 
    generator.Emit(OpCodes.Ldloc, local); // Use the declared local. 
    generator.Emit(OpCodes.Stfld, field); 

    generator.Emit(OpCodes.Ret); 
    return (Action<object, int>)setMethod.CreateDelegate(typeof(Action<object, int>)); 
} 
+0

La classe CustomBox est une classe qui crée simplement un tableau sur le tas du type de valeur spécifié. Il a alors des fonctions pour obtenir et définir des valeurs vers et depuis le tas sans boxe et unboxing. Il a le coût d'allocation initial sur le tas, mais ne crée pas de poubelle chaque fois qu'une valeur est stockée. –