原文链接:https://blog.unity.com/engine-platform/il2cpp-internals-a-tour-of-generated-code
这是 IL2CPP Internals 系列的第二篇博文。在这篇文章中,我们将研究由 il2cpp.exe 生成的 C++ 代码。在此过程中,我们将了解托管类型在本地代码中的表现形式,查看用于支持 .NET 虚拟机的运行时检查,了解循环的生成过程等!
我们将讨论一些特定版本的代码,这些代码在 Unity 的后续版本中肯定会有所变化。不过,这些概念将保持不变。
在本例中,我将使用 Unity 的最新版本 5.0.1p1。与本系列第一篇文章一样,我将从一个空项目开始,并添加一个脚本文件。这次的内容如下:
using UnityEngine;
public class HelloWorld : MonoBehaviour {
private class Important {
public static int ClassIdentifier = 42;
public int InstanceIdentifier;
}
void Start () {
Debug.Log("Hello, IL2CPP!");
Debug.LogFormat("Static field: {0}", Important.ClassIdentifier);
var importantData = new [] {
new Important { InstanceIdentifier = 0 },
new Important { InstanceIdentifier = 1 } };
Debug.LogFormat("First value: {0}", importantData[0].InstanceIdentifier);
Debug.LogFormat("Second value: {0}", importantData[1].InstanceIdentifier);
try {
throw new InvalidOperationException("Don't panic");
}
catch (InvalidOperationException e) {
Debug.Log(e.Message);
}
for (var i = 0; i < 3; ++i) {
Debug.LogFormat("Loop iteration: {0}", i);
}
}
}
我将在 Windows 上运行 Unity 编辑器,为 WebGL 构建此项目。我在 "构建设置"(Build Settings)中选择了 "Development Player”选项,这样我们就能在生成的 C++ 代码中获得相对漂亮的名称。我还将 "WebGL 播放器设置 "中的 "启用异常 "选项设置为 "完全"。
WebGL 生成完成后,生成的 C++ 代码会出现在我的项目目录下的 Temp\StagingArea\Data\il2cppOutput 目录中。编辑器关闭后,该目录将被删除。不过只要编辑器是打开的,这个目录就不会改变,所以我们可以检查它。
即使是这个小项目,il2cpp.exe 工具也生成了大量文件。我看到了 4625 个头文件和 89 个 C++ 源代码文件。为了处理所有这些代码,我喜欢使用与 Exuberant CTags 兼容的文本编辑器。CTags 通常会为这些代码快速生成一个标签文件,这样就更容易浏览了。
最初,您可以看到生成的许多 C++ 文件并非来自简单的脚本代码,而是标准库中代码的转换版本,如 mscorlib.dll。正如本系列第一篇文章所述,IL2CPP 脚本后台与 Mono 脚本后台使用相同的标准库代码。请注意,每次运行 il2cpp.exe 时,我们都会转换 mscorlib.dll 和其他标准库程序集中的代码。这似乎没有必要,因为这些代码不会改变。
然而,IL2CPP 脚本后台总是使用字节码剥离来减小可执行文件的大小。因此,即使是脚本代码中的微小改动,也会导致标准库代码的许多不同部分被使用或不被使用,这取决于具体情况。因此,我们每次都需要转换 mscorlib.dll 程序集。我们正在研究更好的增量构建方法,但目前还没有任何好的解决方案。
对于托管代码中的每个类型,il2cpp.exe 将生成一个头文件,用于该类型的 C++ 定义,另一个头文件用于该类型的方法声明。例如,让我们看看转换后的 UnityEngine.Vector3 类型的内容。该类型的头文件名为 UnityEngine_UnityEngine_Vector3.h。该文件名是根据程序集 UnityEngine.dll 的名称创建的,后面跟有命名空间和类型名称。代码如下:
// UnityEngine.Vector3
struct Vector3_t78
{
// System.Single UnityEngine.Vector3::x
float ___x_1;
// System.Single UnityEngine.Vector3::y
float ___y_2;
// System.Single UnityEngine.Vector3::z
float ___z_3;
};
il2cpp.exe 实用程序对三个实例字段进行了转换,并对名称进行了一些处理,以避免冲突和保留字。通过使用前导下划线,我们使用了 C++ 中的一些保留字,但到目前为止,我们还没有发现与 C++ 标准库代码有任何冲突。
UnityEngine_UnityEngine_Vector3MethodDeclarations.h 文件包含 Vector3 中所有方法的方法声明。例如,Vector3 覆盖了 Object.ToString 方法:
// System.String UnityEngine.Vector3::ToString()
extern "C" String_t* Vector3_ToString_m2315 (Vector3_t78 * __this, MethodInfo* method) IL2CPP_METHOD_ATTR
请注意注释,它指出了该本地声明所代表的托管方法。我经常发现,在输出文件中搜索这种格式的托管方法名称非常有用,尤其是对于名称常见的方法,如 ToString。
请注意 il2cpp.exe 转换的所有方法中的几个有趣之处: