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