2016-08-23 17 views
1

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

Проблема: Когда мой внешний класс является общим классом, я также делаю вложенный класс обобщенным (поскольку ему необходимо создать объекты внешнего класса). Сгенерированный IL проходит через peverify fine, и отлично выглядит, но конструктор класса, создающий экземпляр вложенного класса, выдает 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#, скомпилировать его, декомпилировать обратно в MSIL и посмотреть, в чем разница :) Конечно, вы, возможно, уже нашли ошибку при записи эквивалентного кода C#, но это только бонусные баллы. – Luaan

+0

Однако в этом случае я считаю, что проблема заключается в том, что вложенный класс должен иметь тот же аргумент типа, что и родительский. Вы объявляете новый аргумент типа 'T_vt', не используя родительский' T', который определенно не тот, который вы хотите, и, вероятно, ошибка, которая может вызвать вызываемое вами исключение (поскольку ваш вложенный класс * должен * использовать все аргументы типового типа его родительского класса). – Luaan

+0

Нет, я * думаю *, что я делаю с аргументами вложенного типа типа, является правильным. Он объявляется с собственным аргументом типа T_vt, и когда внешний класс создает экземпляр его, он делает это, используя «newobj instance void class Rep2/TRep/'__% NemeaVType» ::. Ctor() »- т.е. он проходит это собственный аргумент типа как аргумент типа вложенному классу.Таким образом, вложенный класс будет создан с использованием того же аргумента фактического типа. Я попытаюсь воспроизвести на C#, хотя некоторые из вещей, которые я делаю, я не уверен, как они будут отображаться на C#. –

ответ

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, а не что-либо более подробное.