Я сделал ошибку в этом IL, которого я не вижу?
Я работаю над компилятором, используя System.Reflection.Emit
, и я получаю ошибки ограничения JIT, я не могу понять. Проблема возникает в моей реализации дескрипторов функций. Т.е. генерация кода для
function foo() { }
f = foo;
f();
Из-за не зависящих от меня спецификаций язык динамически типизирован, поэтому я не знаю, сколько аргументов f
будет ожидать во время компиляции. Чтобы противостоять этому, а не испускать Ldftn
за foo
Я генерирую новый метод, λfoo
, который принимает массив аргументов, заданных в выражении вызова, и помещает их в стек eval для foo
, Это разрешено в CLR?
Сейчас я получаю исключение "JIT обнаружил внутреннее ограничение" (или "CLR обнаружил недопустимую программу", если я сохраняю сборку и запускаю ее вместо вызова из памяти) с трассировкой стека, показывающей, что это происходит в λfoo
, Это IL, который я генерирую.
.method private instance class [MylibInterop]MylibInterop.MylibValue
'λfoo'(class [MylibInterop]MylibInterop.MylibValue[] A_1) cil managed
{
// Code size 90 (0x5a)
.maxstack 10
.locals init (int32 V_0,
int32 V_1)
IL_0000: ldarg.1
IL_0001: call instance int32 [mscorlib]System.Array::get_Length()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4 0x0
IL_000d: ble IL_001d
IL_0012: ldstr "Too many arguments to lambda call"
IL_0017: newobj instance void [mscorlib]System.Exception::.ctor(string)
IL_001c: throw
IL_001d: ldarg.0
IL_001e: ldc.i4.0
IL_001f: stloc.1
IL_0020: ldloc.0
IL_0021: newobj instance void [MylibInterop]MylibInterop.MylibValue::.ctor(int32)
IL_0026: ldloc.1
IL_0027: ldloc.0
IL_0028: bge IL_003d
IL_002d: ldarg.1
IL_002e: ldloc.1
IL_002f: ldelem [MylibInterop]MylibInterop.MylibValue
IL_0034: ldloc.1
IL_0035: ldc.i4.1
IL_0036: add
IL_0037: stloc.1
IL_0038: br IL_0026
IL_003d: ldloc.0
IL_003e: stloc.1
IL_003f: ldloc.1
IL_0040: ldc.i4 0x0
IL_0045: bge IL_0054
IL_004a: ldnull
IL_004b: ldloc.1
IL_004c: ldc.i4.1
IL_004d: add
IL_004e: stloc.1
IL_004f: br IL_003f
IL_0054: call instance class [MylibInterop]MylibInterop.MylibValue debug.Program::foo(class [MylibInterop]MylibInterop.MylibValue)
IL_0059: ret
} // end of method Program::'λfoo'
1 ответ
@leppie получил это в комментариях: стек должен быть детерминированным; его нет в коде, который я генерировал (даже если я знаю, что он выдвигает правильное количество аргументов). Я смог обойти это, потому что у компилятора было достаточно информации, чтобы развернуть цикл (отсюда и константы в сгенерированном IL) и, таким образом, создать детерминированный стек.