【问题标题】:MSIL assembly: Unexpected OutOfMemoryException in class constructorMSIL 程序集:类构造函数中出现意外的 OutOfMemoryException
【发布时间】: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 代码是托管安全的 - 也就是说,它不允许部分受信任的代码做不安全的事情(这主要意味着防止堆栈等问题不平衡,违反类型安全等)。它的设计并不是为了确保代码正确——即使在理论上也是不可能的。如果它可以调用更多常见错误,那就太好了,但这并不是该工具的真正目的。也许有一些工具可以帮助你,但我不知道有什么:)

标签: .net cil ilasm


【解决方案1】:

所以事实证明问题与泛型声明无关 - 嵌套类的声明很好。泛型参数的名称与外部类的泛型参数不匹配,但这只是 C# 编译器(可以理解)在将泛型参数传播到嵌套类时遵循的约定。

问题很简单

 IL_0005:  stsfld     class Rep2/TRep/'__%NemeaVType'<!T> Rep2/TRep::'__%NemeaVTypeI'

line - 它无效,因为它试图访问 Rep2/TRep 类上的一个字段,该字段是通用的,而不提供任何类型参数。将其更改为

 IL_0005:  stsfld     class Rep2/TRep/'__%NemeaVType'<!0> Rep2/TRep<!T>::'__%NemeaVTypeI'

解决所有问题。

我仍然认为 peverify 可以 突出显示了这一点,因为它并不是真正有效的——它不可能正确执行,因为它不是有效的字段引用。执行代码时得到 OutOfMemoryException 也有点烦人,而不是任何有更多细节的东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-02
    • 1970-01-01
    相关资源
    最近更新 更多