【发布时间】:2016-08-23 08:46:01
【问题描述】:
我正在编写一个输出 .NET 程序集的编译器(使用 Mono.Cecil,尽管我不相信 Cecil 与这个问题有关)。其中一个编译器特性要求一个类有一个编译器生成的嵌套类,并带有一些支持方法;外部类有一个静态字段,因此每个类实际上都有一个引用嵌套类对象的单例。为了初始化它,任何这样的类都有一个类构造函数来创建嵌套类的实例并将其存储在字段中。
问题:当我的外部类是泛型类时,我也将嵌套类设为泛型(因为它需要创建外部类的对象)。生成的 IL 通过 peverify 很好,在我看来 看起来很好,但是创建嵌套类实例的类构造函数在运行时抛出 OutOfMemoryException。
我已经用 ildasm 对程序集进行了反汇编,将其精简到最小的复制(不幸的是仍然有大约 180 行 IL),并验证使用 ilasm 编译 IL 生成的 exe 仍然存在问题。
使用 Visual Studio 或 MDbg 进行调试并没有启发我 - 我只是得到 OutOfMemoryException 而没有指示为什么。我愿意相信我的 IL 在某种程度上是无效的,但 peverify 并不表示有问题。谁能提出问题是什么?
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly extern System
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly Repro1
{
.ver 0:0:0:0
}
.module Repro1
// MVID: {7DA983B6-F5EA-4ACB-8443-C29F25ADDCD4}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x016E0000
.class public abstract auto ansi sealed Repro1
extends [mscorlib]System.Object
{
.method assembly static void '<NemeaProgram>'() cil managed
{
.entrypoint
// Code size 6 (0x6)
.maxstack 0
IL_0000: call void Rep2::Go()
IL_0005: ret
} // end of method Repro1::'<NemeaProgram>'
} // end of class Repro1
.class public abstract auto ansi sealed Rep1
extends [mscorlib]System.Object
{
.class auto ansi nested public TRep
extends [mscorlib]System.Object
{
.class auto ansi nested public '__%NemeaVType'
extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method '__%NemeaVType'::.ctor
.method public newslot virtual instance class Rep1/TRep
Create(string Foo) cil managed
{
// Code size 10 (0xa)
.maxstack 8
IL_0000: ldarg Foo
IL_0004: newobj instance void Rep1/TRep::.ctor(string)
IL_0009: ret
} // end of method '__%NemeaVType'::Create
} // end of class '__%NemeaVType'
.field famorassem string FData
.field public static class Rep1/TRep/'__%NemeaVType' '__%NemeaVTypeI'
.method public hidebysig specialname rtspecialname
instance void .ctor(string Foo) cil managed
{
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ldarg.0
IL_0007: ldarg Foo
IL_000b: stfld string Rep1/TRep::FData
IL_0010: ret
} // end of method TRep::.ctor
.method privatescope specialname rtspecialname static
void '.cctor$PST0600004C'() cil managed
{
// Code size 11 (0xb)
.maxstack 8
IL_0000: newobj instance void Rep1/TRep/'__%NemeaVType'::.ctor()
IL_0005: stsfld class Rep1/TRep/'__%NemeaVType' Rep1/TRep::'__%NemeaVTypeI'
IL_000a: ret
} // end of method TRep::.cctor
} // end of class TRep
.class auto ansi nested public TItem
extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method TItem::.ctor
} // end of class TItem
} // end of class Rep1
.class public abstract auto ansi sealed Rep2
extends [mscorlib]System.Object
{
.class auto ansi nested public TRep<(Rep1/TItem) T>
extends Rep1/TRep
{
.class auto ansi nested public '__%NemeaVType'<(Rep1/TItem) T_vt>
extends Rep1/TRep/'__%NemeaVType'
{
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void Rep1/TRep/'__%NemeaVType'::.ctor()
IL_0006: ret
} // end of method '__%NemeaVType'::.ctor
.method public virtual instance class Rep1/TRep
Create(string Foo) cil managed
{
// Code size 10 (0xa)
.maxstack 8
IL_0000: ldarg Foo
IL_0004: newobj instance void class Rep2/TRep<!T_vt>::.ctor(string)
IL_0009: ret
} // end of method '__%NemeaVType'::Create
} // end of class '__%NemeaVType'
.field public static class Rep2/TRep/'__%NemeaVType'<!T> '__%NemeaVTypeI'
.method public hidebysig specialname rtspecialname
instance void .ctor(string Foo) cil managed
{
// Code size 22 (0x16)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg Foo
IL_0005: call instance void Rep1/TRep::.ctor(string)
IL_0015: ret
} // end of method TRep::.ctor
.method privatescope specialname rtspecialname static
void '.cctor$PST06000055'() cil managed
{
// Code size 11 (0xb)
.maxstack 8
IL_0000: newobj instance void class Rep2/TRep/'__%NemeaVType'<!T>::.ctor()
IL_0005: stsfld class Rep2/TRep/'__%NemeaVType'<!T> Rep2/TRep::'__%NemeaVTypeI'
IL_000a: ret
} // end of method TRep::.cctor
} // end of class TRep
.method public static void Go() cil managed
{
// Code size 29 (0x1d)
.maxstack 1
.locals init ([0] class Rep1/TRep R)
IL_0000: ldstr "Oi"
IL_0005: newobj instance void class Rep2/TRep<class Rep1/TItem>::.ctor(string)
IL_000a: stloc R
IL_001c: ret
} // end of method Rep2::Go
} // end of class Rep2
【问题讨论】:
-
好吧,在这种情况下,我所做的就是创建等效的 C# 代码,编译它,反编译回 MSIL,看看有什么区别 :) 当然,你可能已经找到编写等效的 C# 代码时出现错误,但这只是加分项。
-
但是,在这种情况下,我认为问题在于嵌套类应该与父类具有相同的类型参数。您正在声明一个新类型参数
T_vt而不使用父级的T,这绝对不是您想要的,并且可能是一个可能导致您看到的异常的错误(因为您的嵌套类 必须 使用其父类的所有泛型类型参数)。 -
不,我认为我对嵌套类类型参数所做的事情是正确的。它使用自己的类型参数 T_vt 声明,当外部类创建它的实例时,它使用“newobj instance void class Rep2/TRep/'__%NemeaVType'::.ctor()” -即它将自己的类型参数 作为类型参数传递给嵌套类。因此嵌套类将使用相同的实际类型参数进行实例化。我将尝试在 C# 中进行复制,虽然我正在做一些事情,但我不确定它们将如何映射到 C#。
-
...虽然事实证明你说得很对,但嵌套类确实需要直接使用父类型参数。我怀疑这是答案(尽管 peverify 没有突出显示它很烦人!) - 如果我确认,我会相应地发布答案。谢谢。
-
好吧,AFAIK,PEVerify 只应该确保 IL 代码是托管安全的 - 也就是说,它不允许部分受信任的代码做不安全的事情(这主要意味着防止堆栈等问题不平衡,违反类型安全等)。它的设计并不是为了确保代码正确——即使在理论上也是不可能的。如果它可以调用更多常见错误,那就太好了,但这并不是该工具的真正目的。也许有一些工具可以帮助你,但我不知道有什么:)