Генерация IL для двойных массивов

Я пишу некоторые институты IL для создания массивов int и double с использованием пространства имен System.Reflection.Emit.

Для создания массива int я использую следующий код.

LocalBuilder arr = gen.DeclareLocal(typeof(int));
gen.Emit(OpCodes.Ldc_I4_1);
gen.Emit(OpCodes.Newarr, typeof(int));
gen.Emit(OpCodes.Stloc, arr);
gen.Emit(OpCodes.Ldloc, arr);
gen.Emit(OpCodes.Ldc_I4_0);
gen.Emit(OpCodes.Ldc_I4, 500);
gen.Emit(OpCodes.Stelem_I4);

gen.Emit(OpCodes.Ldloc, arr);
gen.Emit(OpCodes.Ldc_I4_0);
gen.Emit(OpCodes.Ldelem_I4);
gen.Emit(OpCodes.Call,typeof(Console).GetMethod("WriteLine",new Type[]{typeof(int)}));

Он работает, как и ожидалось, и печатает 500 на консоли.

Точно так же я пытался создать массив double, как показано ниже.

LocalBuilder arr = gen.DeclareLocal(typeof(double));
gen.Emit(OpCodes.Ldc_I4_1);
gen.Emit(OpCodes.Newarr, typeof(double));
gen.Emit(OpCodes.Stloc, arr);
gen.Emit(OpCodes.Ldloc, arr);
gen.Emit(OpCodes.Ldc_I4_0);
gen.Emit(OpCodes.Ldc_R8, 500D);
gen.Emit(OpCodes.Stelem_R8);

gen.Emit(OpCodes.Ldloc, arr);
gen.Emit(OpCodes.Ldc_I4_0);
gen.Emit(OpCodes.Ldelem_I8);
gen.Emit(OpCodes.Call,typeof(Console).GetMethod("WriteLine",new Type[]{typeof(double)}));

К сожалению, это не работает, и когда я проверяю сгенерированную сборку с помощью pereview, я получаю следующую ошибку.

Microsoft (R) .NET Framework PE Verifier.  Version  4.0.30319.1
Copyright (c) Microsoft Corporation.  All rights reserved.

[IL]: Error: [C:\temp\Research\Research\bin\Debug\MyMod.exe : Foo::Main][offset 0x00000006][found ref array md
array 'System.Double[]'][expected Double] Unexpected type on the stack.
[IL]: Error: [C:\temp\Research\Research\bin\Debug\MyMod.exe : Foo::Main][offset 0x00000012] Expected single di
mension array.
2 Error(s) Verifying MyMod.exe

Кроме того, я проверил сгенерированную сборку, используя ildasm

.method privatescope static void  Main$PST06000001() cil managed
{
  .entrypoint
  // Code size       28 (0x1c)
  .maxstack  3
  .locals init (float64 V_0)
  IL_0000:  ldc.i4.1
  IL_0001:  newarr     [mscorlib]System.Double
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldc.i4.0
  IL_0009:  ldc.r8     500.
  IL_0012:  stelem.r8
  IL_0013:  ldloc.0
  IL_0014:  ldc.i4.0
  IL_0015:  ldelem.i8
  IL_0016:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_001b:  ret
} // end of method Foo::Main

Есть ли у вас какие-либо идеи?


c# il
person Upul Bandara    schedule 08.06.2011    source источник
comment
Как выглядит сгенерированный C# фрагмент кода?   -  person Ritch Melton    schedule 08.06.2011
comment
Я признаю, что плохо разбираюсь в IL, но разве вы не храните ссылку на массив в локальной переменной объявленного типа (int/double)? Разве этот местный не должен быть какой-то ссылкой?   -  person Damien_The_Unbeliever    schedule 08.06.2011
comment
@Damien_The_Unbeliever: Да, я сделал ошибку и неправильно присвоил ссылку на массив целочисленной переменной.   -  person Upul Bandara    schedule 08.06.2011


Ответы (1)


LocalBuilder arr = gen.DeclareLocal(typeof(int));
gen.Emit(OpCodes.Ldc_I4_1);
gen.Emit(OpCodes.Newarr, typeof(int));
gen.Emit(OpCodes.Stloc, arr);

Почему arr не относится к типу int[]?

Я уверен, что peverify будет жаловаться на обе версии.

Тот факт, что он работает для первой версии, просто «удача»*.

* Причина гораздо сложнее.

person leppie    schedule 08.06.2011
comment
Спасибо, я сделал БОЛЬШУЮ ошибку, тип переменной должен быть двойным [] После этого изменения мой код работает LocalBuilder arr = gen.DeclareLocal(typeof(double[])); К сожалению, пример массива int[] работает с LocalBuilder arr = gen.DeclareLocal(typeof(int)); - person Upul Bandara; 08.06.2011
comment
@Upul: Причина сложна, почему это работает. Система считает int совместимым с типом указателя. Подобно тому, что позволяет вам делать C. - person leppie; 08.06.2011
comment
Я здесь на x86 - я предполагаю, что он упадет на x64? (Кроме того, технически это не ссылка, а указатель) - person Damien_The_Unbeliever; 08.06.2011
comment
@Damien_The_Unbeliever: Не уверен :) - person leppie; 08.06.2011
comment
@Damien_The_Unbeliever Ну, x64 имеет 8-байтовые указатели, x86 4-байтовые * (опять же, более сложные :-), и оба имеют 4-байтовые целые числа, поэтому неявное «приведение» его к массиву целых чисел, вероятно, не получит «out». -за границами'. Тем не менее, JIT'ter для x64 и x86 совершенно разные, и неизвестно, что с ним делает режим оптимизатора/отладки, поэтому в конечном итоге все сводится к удаче. В любом случае, я считаю, что «извлеченный урок» здесь должен быть следующим: всегда используйте PEVerify при создании кода IL. - person atlaste; 13.12.2013