2016-12-01 2 views
0

Я создаю систему для хранения типов значений (int, byte, structs) в куче и для предотвращения бокса и распаковки указанных типов значений. Это связано с тем, что весь постоянный бокс и unboxing в движке Unity 3D создает большие всплески центрального процессора в нашей большой базе кода.VerificationException: операция может дестабилизировать время выполнения. Проблемы с EmitCall (OpCodes.Call)

VerificationException: Операция может дестабилизировать время выполнения.

Вышеупомянутое исключение бросается, когда я пытаюсь вызвать динамический метод. Трассировка стека заканчивается как раз перед тем, как она переходит в динамический метод, и невозможно отменить выполнение. Более подробная информация приведена в приведенном ниже примере.

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 метод, который я пытаюсь создать и генерируется 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

Вы используете локальную версию, но не определяете ее; и я уверен, что поле, которое было передано, не было определено в 'object', вы должны направить целевой экземпляр на соответствующий тип, прежде чем пытаться установить на нем поле. –

+0

@BrianReichle Это странно, потому что, когда я пишу код и декомпилирую его, сгенерированный IL не объявляет локальный. Не работает ли OpCodes.Stloc_0 для меня? Я указываю, чтобы сохранить его в локальном 0, который уже определен. –

+0

Не нужно только объявлять локальное, если вы используете OpCodes.Stloc, но я использовал OpCodes.Stloc_0. –

ответ

0

Проблема заключалась в том, что я не был delcaring местный, я использовал. Мне также нужно было привести целевой объект к соответствующему типу.

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

Класс CustomBox - это класс, который просто создает массив в куче указанного типа значения. Затем он имеет функции для получения и установки значений в кучу и без нее без бокса и распаковки. Он имеет начальную стоимость распределения в куче, но не создает мусор каждый раз, когда значение сохраняется. –

Смежные вопросы