#net #const #cil
Скажем, у нас есть некая константа в откомпилированной сборке. Возьмем примера ради класс Math из mscorlib.dll и константу Math.PI Если посмотреть исходник в виде IL-кода, то мы увидим такое вот объявление поля: .field public static literal float64 PI = float64(3.1415926535897931) То есть по сути, PI является публичным статическим полем, помеченным как литерал И именно из-за последнего не будет работать инструкция ldsfld в отношении Math.PI Из-за чего следующий код не будет работоспособным: .method public static float64 GetPi() cil managed { .maxstack 1 ldsfld float64 [mscorlib]System.Math::PI ret } При вызове GetPi() мы получим ошибку времени исполнения: System.MissingFieldException: "Поле не найдено: "System.Math.PI"." В связи с чем у меня и возник вопрос: можно ли вообще средствами CIL вытащить значение литерала из сборки и, если да, то как это сделать? P.S. - я прекрасно понимаю, что при создании IL-кода из инструкций на, скажем, C# значения констант подставляются сразу, так что сие: return a * Math.PI; будет преобразовано в: ldloc a ldc.r8 3.1415926535897931 mul ret Однако все таки стало интересно, есть ли в CIL инструкция, которая все таки извлекала бы значение константного поля
Ответы
Ответ 1
В спецификации CIL достаточно четко указано: I.8.6.1.2 Location signatures The literal constraint promises that the value of the location is actually a fixed value of a built-in type. The value is specified as part of the constraint. Compilers are required to replace all references to the location with its value, and the VES therefore need not allocate space for the location. This constraint, while logically applicable to any location, shall only be placed on static fields of compound types. Fields that are so marked are not permitted to be referenced from CIL (they shall be in-lined to their constant value at compile time), but are available using reflection and tools that directly deal with the metadata. Выделенный кусок Fields that are so marked are not permitted to be referenced from CIL Поля отмеченные таким способом не могут быть доступны из CIL Так что нет, такой инструкции в CIL нет. Однако, как указано в том же пункте: but are available using reflection and tools that directly deal with the metadata. но доступны при использовании рефлексии или инструментов работающих напрямую с метаданными. перевод ответа на вопрос CIL - How do I use a public static literal field?Ответ 2
Дополню ответ @Grundy: Как уже было сказано, штатными средствами CIL получить значение литерала нельзя, так что следует использовать рефлексию Собственно, этим и займемся) Получился такой вот generic-метод: .method public static !!ConstType GetConst(class [mscorlib]System.Type, string) cil managed { .maxstack 3 ldarg.0 // Кладем на стек тип, из которого будем тащить константу ldarg.1 // Кладем на стек название литерала ldc.i4 56 // Кладем на стек значение BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic // т.к. константа всегда является полем статичным и не совсем обязательно публичным) // Достанем FieldInfo callvirt instance class [mscorlib]System.Reflection.FieldInfo [mscorlib]System.Type::GetField(string, valuetype [mscorlib]System.Reflection.BindingFlags) ldnull // Получим его значение callvirt instance object [mscorlib]System.Reflection.FieldInfo::GetValue(object) // Распакуем unbox.any !!ConstType // Вернем ret } Тогда метод GetPi() можно переписать так: .method public static float64 GetPi() cil managed { .maxstack 2 // Получим тип класса System.Math ldtoken [mscorlib]System.Math call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) // Укажем, какую константу хотим из него вытащить ldstr "PI" // И, собственно, вытащим) call !!0 ILConsole.Program::GetConst (class [mscorlib]System.Type, string) ret } Теперь вызов метода не упадет в runtime, а вернет ожидаемое значение System.Math.PI) Еще раз подчеркну, что константы на то и константы, что их неизменяемое значение подставляется сразу в IL-код, так что при написании какого-нибудь компилятора не стоит использовать код выше, а следует просто вручную записать нужную величину куда следует Данный ответ приведен только в, так скажем, образовательных целях)
Комментариев нет:
Отправить комментарий