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