Сборка MSIL: неожиданное исключение OutOfMemoryException в конструкторе класса

Я пишу компилятор, который выводит сборки.NET (используя Mono.Cecil, хотя я не верю, что Сесил имеет отношение к этой проблеме). Одна из функций компилятора требует, чтобы класс имел сгенерированный компилятором вложенный класс с некоторыми вспомогательными методами; внешний класс имеет статическое поле, поэтому у каждого класса есть одноэлементная ссылка на объект вложенного класса. Чтобы инициализировать это, любой такой класс имеет конструктор класса, чтобы создать экземпляр вложенного класса и сохранить его в поле.

Проблема: Когда мой внешний класс является универсальным классом, я делаю вложенный класс также универсальным (так как он должен создавать объекты внешнего класса). Сгенерированный IL прекрасно проходит через peverify и выглядит хорошо на мой взгляд, но конструктор класса, создающий экземпляр вложенного класса, выдает OutOfMemoryException во время выполнения.

Я разобрал сборку с помощью ildasm, уменьшил ее до минимального воспроизведения (к сожалению, все еще ~180 строк IL) и проверил, что компиляция IL с ilasm создает 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

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 при выполнении кода, а не что-либо с более подробной информацией.

Другие вопросы по тегам