VerificationException:操作可能会破坏运行时的稳定性。 EmitCall的麻烦(OpCodes.Call)

时间:2016-12-01 01:37:00

标签: c# cil boxing emit

我正在创建一个系统来在堆上存储值类型(int,byte,structs),以防止对所述值类型进行装箱和取消装箱。这是因为Unity 3D引擎中的所有常量装箱和拆箱都会在我们的大型代码库中创建大的GC CPU峰值。

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);
    }
}

编辑: 下面是我正在尝试创建的方法,它生成了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
*/

1 个答案:

答案 0 :(得分:1)

问题是因为我没有正在使用当地人。我还需要将目标对象转换为正确的类型。

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>));
}