反编译器如何识别编译常量?
How is a decompiler able to recognize a compiled constant?
我正在使用 ILSpy 反编译 .Net 程序集并查看代码。当我浏览 WindowsBase.dll
中 System.Windows.Vector.AngleBetween(Vector, Vector)
的代码时,我偶然发现了一些奇怪的东西。
这是函数的完整代码:
public static double AngleBetween(Vector vector1, Vector vector2)
{
double y = vector1._x * vector2._y - vector2._x * vector1._y;
double x = vector1._x * vector2._x + vector1._y * vector2._y;
return Math.Atan2(y, x) * (180.0 / Math.PI);
}
显然 ILSpy 可以识别 Math.PI
,这是一个常量。
这就是 Microsoft Docs 关于 C# 中常量的说法:
In fact, when the compiler encounters a constant identifier in C# source code, it substitutes the literal value directly into the intermediate language (IL) code that it produces.
基于此,ILSpy 所做的似乎是不可能的。
注意:即使 "Use variable names from debug symbols, if available" 和 "Show info from debug symbols, if available"
设置中的选项未选中。
LINQPad 中的快速测试显示生成的 IL 代码存在一些差异(从 here 复制的 PI 常量值)。
来源:
void Main()
{
double a = Math.PI;
double b = 3.14159265358979;
}
IL:
IL_0000: nop
IL_0001: ldc.r8 18 2D 44 54 FB 21 09 40
IL_000A: stloc.0 // a
IL_000B: ldc.r8 11 2D 44 54 FB 21 09 40
IL_0014: stloc.1 // b
IL_0015: ret
在常量和文字值之间生成的 IL 代码中似乎确实存在细微差别,但我还不确定它到底意味着什么。
以上值来自MSDN documentation seem to contradict information from the .NET Reference Source(见评论)。从源代码调整后,IL 是相同的。
来源:
void Main()
{
var a = Math.PI;
var b = 3.14159265358979323846;
}
IL:
IL_0000: nop
IL_0001: ldc.r8 18 2D 44 54 FB 21 09 40
IL_000A: stloc.0 // a
IL_000B: ldc.r8 18 2D 44 54 FB 21 09 40
IL_0014: stloc.1 // b
IL_0015: ret
顺便说一句,似乎有一个 open issue 来解决文档/源代码不一致的问题。
如您在此 ILSpy issue and the corresponding pull request 中所见,这是专门针对 Math.PI
.
等众所周知的值实施(硬编码)的
来自 GitHub 问题:
I suppose to calculate pi coefficient by following way:
c = Math.PI / constant
. If we getting "good" value (equal exactly to 1.0, 2.0, 0.5, 1/180 and so on), we simply replacing it with symbolic expression (Math.PI, Math.PI * 2, Math.PI / 2, Math.PI / 180 and so on).
我正在使用 ILSpy 反编译 .Net 程序集并查看代码。当我浏览 WindowsBase.dll
中 System.Windows.Vector.AngleBetween(Vector, Vector)
的代码时,我偶然发现了一些奇怪的东西。
这是函数的完整代码:
public static double AngleBetween(Vector vector1, Vector vector2)
{
double y = vector1._x * vector2._y - vector2._x * vector1._y;
double x = vector1._x * vector2._x + vector1._y * vector2._y;
return Math.Atan2(y, x) * (180.0 / Math.PI);
}
显然 ILSpy 可以识别 Math.PI
,这是一个常量。
这就是 Microsoft Docs 关于 C# 中常量的说法:
In fact, when the compiler encounters a constant identifier in C# source code, it substitutes the literal value directly into the intermediate language (IL) code that it produces.
基于此,ILSpy 所做的似乎是不可能的。
注意:即使 "Use variable names from debug symbols, if available" 和 "Show info from debug symbols, if available" 设置中的选项未选中。
LINQPad 中的快速测试显示生成的 IL 代码存在一些差异(从 here 复制的 PI 常量值)。
来源:
void Main()
{
double a = Math.PI;
double b = 3.14159265358979;
}
IL:
IL_0000: nop
IL_0001: ldc.r8 18 2D 44 54 FB 21 09 40
IL_000A: stloc.0 // a
IL_000B: ldc.r8 11 2D 44 54 FB 21 09 40
IL_0014: stloc.1 // b
IL_0015: ret
在常量和文字值之间生成的 IL 代码中似乎确实存在细微差别,但我还不确定它到底意味着什么。
以上值来自MSDN documentation seem to contradict information from the .NET Reference Source(见评论)。从源代码调整后,IL 是相同的。
来源:
void Main()
{
var a = Math.PI;
var b = 3.14159265358979323846;
}
IL:
IL_0000: nop
IL_0001: ldc.r8 18 2D 44 54 FB 21 09 40
IL_000A: stloc.0 // a
IL_000B: ldc.r8 18 2D 44 54 FB 21 09 40
IL_0014: stloc.1 // b
IL_0015: ret
顺便说一句,似乎有一个 open issue 来解决文档/源代码不一致的问题。
如您在此 ILSpy issue and the corresponding pull request 中所见,这是专门针对 Math.PI
.
来自 GitHub 问题:
I suppose to calculate pi coefficient by following way:
c = Math.PI / constant
. If we getting "good" value (equal exactly to 1.0, 2.0, 0.5, 1/180 and so on), we simply replacing it with symbolic expression (Math.PI, Math.PI * 2, Math.PI / 2, Math.PI / 180 and so on).