1

I'm writing a compiler that outputs .NET assemblies (using Mono.Cecil, although I don't believe Cecil is relevant to this problem). One of the compiler features requires that a class will have a compiler-generated nested class with some support methods; the outer class has a static field so each class effectively has a singleton referencing an object of the nested class. To initialise this, any such class has a class constructor to create an instance of the nested class and store it in the field.

Problem: When my outer class is a generic class, I make the nested class generic too (since it needs to create objects of the outer class). The generated IL passes through peverify fine, and looks fine to my eye, but the class constructor creating an instance of the nested class throws OutOfMemoryException at runtime.

I've disassembled the assembly with ildasm, slimmed it down to a minimal reproduction (unfortunately still ~180 lines of IL), and verifying that compiling the IL with ilasm produces an exe that still exhibits the problem.

Debugging with Visual Studio or MDbg hasn't enlightened me - I just get OutOfMemoryException without indication why. I'm willing to believe my IL is invalid somehow, but peverify doesn't indicate a problem. Can anyone suggest what the issue is?

// 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
4

1 回答 1

2

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

问题很简单

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

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

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

解决所有问题。

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

于 2016-08-23T11:34:14.290 回答