xeno_by (xeno_by) wrote,
xeno_by
xeno_by

Нашел баг в рефлекшн-эмите

Воспроизводится вот так: http://code.google.com/p/xenogears/source/browse/trunk/XenoGears.Playground/XenoGears/Reflection/Emit/ReflectionEmitTests.cs?r142 - первый тест прокатывает, а второй нет. Юзаю дотнет 3.5 с первым сервис-паком. Впрочем, в бете-2 четвертого фреймворка та же хрень. Ничего выдающегося, но он тупо не работает, и для моего конкретного случая это важно.

Кодярник, воспроизводящий баг
Иксепшн, вылетающий при выполнении теста

Причина бага

Проблема заключается в том, что рефлекшн-эмит, а именно SignatureHelper, пишет неожиданную для CLR (а может и не соответствующую стандартам) сигнатуру в таблицу MemberRef. В момент JIT-компиляции рантайм, прочитав MemberRef, не может по нему разрезолвить MethodDef, что и приводит к выбросу MissingMethodException.

Смотрим детали: (красивенькие байтики и их расшифровка получены при помощи ILDASM по команде View > MetaInfo > More HEX, View > MetaInfo > Show!)
Рабочий MemberRef (сгенерирован компилятором VS 2008 SP1) Нерабочий MemberRef (сгенерирован рефлекшн-эмитом .NET 3.5 SP1)
Member: (0a00012b) Dim:
CallCnvntn: [DEFAULT]
generic
Type Arity:1
ReturnType: I4
2 Arguments
Argument #1: MDArray MVar!!0 2 0 2 0 0
Argument #2: I4
Signature : 10 01 02 08 14 1e 00 02 00 02 00 00 08
Member: (0a000002) Dim:
CallCnvntn: [DEFAULT]
generic
Type Arity:1
ReturnType: I4
2 Arguments
Argument #1: MDArray MVar!!0 2 0 0
Argument #2: I4
Signature : 10 01 02 08 14 1e 00 02 00 00 08

Ниже приведена побайтовая расшифровка сигнатур. Хардкорные знания целиком и полностью взяты из спеки Standard ECMA-335: Common Language Infrastructure (CLI), 4th edition (June 2006) (раздел II, пункты 23.1.16 Element types used in signatures, 23.2 Blobs and signatures, 23.2.1 MethodDefSig, 23.2.10 Param, 23.2.11 RetType, 23.2.12 Type, 23.2.13 ArrayShape).
  * 0x10 => метод является GENERIC, а также имеет DEFAULT calling convention.
  * 0x01 => у метода 1 генерик аргумент.
  * 0х02 => у метода 2 параметра.
  * 0x08 => возвращаемое значение метода
    * 0х08 => тип возвращаемого значения - ELEMENT_TYPE_I4, т.е. int.
  * 0x14 0x1e 0x00 0x02 0x00, а дальше расхождения: в рабочем варианте - 0x02 0х00 0х00, а в падающем - 0x00 => первый параметр
    * 0x14 => тип первого параметра - ELEMENT_TYPE_ARRAY, т.е. многомерный массив (одномерный обычно кодируется как 0x1e ELEMENT_TYPE_SZARRAY); на этом инфа о первом параметре не заканчивается - дальше идет спека массива в соответствии с 23.2.13 ArrayShape.
    * 0x1e 0x00 => тип элемента массива - ELEMENT_TYPE_MVAR, т.е. генерик параметр метода, следующий после этого байт говорит об индексе этого параметра.
    * 0x02 => ранг массива равен 2
    * 0х00 => заданы 0 размеров измерений массива
    * 0x02 0х00 0х00 => заданы 2 минимальные границы измерений массива, обе равны нулю
    * 0x00 => это то, что сгенерил рефлекшн-эмит. Он кагбэ хочет сказать, что наряду с измерениями массива он ничего не знает и про минимальные границы. Так вот почему Рефлектор на рабочий вариант писал "L_0009: call int32 [XenoGears]XenoGears.Array2DExtensions::Dim(!!0[0...,0...], int32)", а на падающий - "L_0008: call int32 [XenoGears]XenoGears.Array2DExtensions::Dim(!!0[,], int32)"!
  * 0х08 => второй параметр
    * 0х08 => тип второго параметра - ELEMENT_TYPE_I4, т.е. int.

Устранение

Ноги бага растут из метода "internal virtual int GetMethodToken(MethodBase method, Type[] optionalParameterTypes)", который принадлежит классу ILGenerator и вызывается всякий раз, когда кто-то эмиттит MethodInfo. При необходимости импорта референса на генерик метод вызывается "handle = this.GetMemberRefToken(genericMethodDefinition, null)", который ведет к методу ModuleBuilder "internal int GetMemberRefToken(MethodBase method, Type[] optionalParameterTypes)". Ну а в нем вызывается метод SignatureHelper, который создает неожиданную сигнатуру. Переписав руками методы выше и пропатчив результаты работы хелпера, можно пофиксить этот неприятный баг. Этим, собственно, я завтра и займусь. А на сегодня - спокойной ночи.
Tags: dotnet
Subscribe
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic
  • 0 comments