Я создаю систему для хранения типов значений (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
*/
Вы используете локальную версию, но не определяете ее; и я уверен, что поле, которое было передано, не было определено в 'object', вы должны направить целевой экземпляр на соответствующий тип, прежде чем пытаться установить на нем поле. –
@BrianReichle Это странно, потому что, когда я пишу код и декомпилирую его, сгенерированный IL не объявляет локальный. Не работает ли OpCodes.Stloc_0 для меня? Я указываю, чтобы сохранить его в локальном 0, который уже определен. –
Не нужно только объявлять локальное, если вы используете OpCodes.Stloc, но я использовал OpCodes.Stloc_0. –