位置: 编程技术 - 正文

[置顶] (八)Unity5.0新特性------IL2CPP Internals: 生成的代码之旅(置顶聊天折叠怎么开启)

编辑:rootadmin


推荐整理分享[置顶] (八)Unity5.0新特性------IL2CPP Internals: 生成的代码之旅(置顶聊天折叠怎么开启),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:置顶的朋友圈是所有人可见吗,置顶pyq会被自己屏蔽的人看到吗,置顶聊天的人怎么不见了,置顶聊天的人怎么不见了,置顶语录怎么设置,置顶的朋友圈是所有人可见吗,置顶朋友圈,置顶通知要开启吗,内容如对您有帮助,希望把文章链接给更多的朋友!

孙广东 .5.

转载请注明出处吧

这是 IL2CPP Internals系列中的第二个博客文章。在这篇文章,我们将探讨由 il2cpp.exe 生成的 c + + 代码。一路走来,我们将看到托管的类型怎么样表示在本机代码中,看看运行时检查用来支持.NET 虚拟机,请参阅如何循环生成的更多 ! 我们会遇到一些非常特定于版本的代码,更高版本的Unity一定会改变。尽管如此,但概念将保持不变。示例项目:我会为此示例使用Unity5.0.1p1 最新版本。在本系列的第一篇,我会从空项目开始,并添加一个脚本文件。这一次,它具有下列内容:using UnityEngine;public class HelloWorld : MonoBehaviour { private class Important { public static int ClassIdentifier = ; 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,建立这一项目。我选择Development Player选项在Build Settings中,这样我们可以得到相对不错的名字生成的 c + + 代码中。我也已经设置Enable Exceptions选项在 WebGL Player Settings中值为Full。

生成的代码的概述: WebGL build完成后,生成的 c + + 代码是在我的项目目录中的 TempStagingAreaDatail2cppOutput 目录中。一旦关闭了编辑器,将删除此目录。只要编辑器是打开的此目录将保持不变,所以我们可以检查它。

Il2cpp.exe 实用程序生成的文件的数目,甚至小项目。个 头文件和 c + + 源代码文件, 若要获得此代码的所有句柄,我喜欢使用一个文本编辑器 Exuberant CTags。CTags 通常会迅速产生为这段代码的标签文件,这使得它易于导航。

最初,你可以看到很多生成的 c + + 文件,而不是从简单的脚本代码,但相反的转换版本代码在标准库,如 mscorlib.dll 中的代码。在本系列中的第一篇文章中提到,IL2CPP 脚本后端使用相同的标准库代码作为Mono的脚本后端。注意: 我们转换 mscorlib.dll 和其他标准库程序集,每个时间 il2cpp.exe 运行中的代码。这可能似乎不必要的因为该代码不会改变。

然而,IL2CPP 脚本后端始终使用字节代码剥离减小该可执行文件的大小。因此,即使在脚本代码中微小的变化可以引发许多不同的部分要使用标准库代码,根据具体情况。因此,我们需要每次转换将 mscorlib.dll 程序集。我们正在研究如何更好地做增量生成,但我们还没有任何好的解决方案。

如何将托管的代码映射到生成的 c + + 代码:

对于在托管代码中的每个类型,il2cpp.exe 将生成一个 c + +的头文件 中定义类型 和类型的方法声明在另一个头文件。例如,让我们看看转换后的 UnityEngine.Vector3 类型的内容。头文件的类型是命名为 UnityEngine_UnityEngine_Vector3.h。创建了一种基于程序集名称,UnityEngine.dll 紧跟的命名空间和类型的名称的名称。代码如下所示:// UnityEngine.Vector3struct Vector3_t { // 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_m (Vector3_t * __this, MethodInfo* method) IL2CPP_METHOD_ATTR 注意注释,指示此本机声明表示的托管的方法。我经常发现搜索文件中输出托管方法在此格式中有用,尤其是对于以常见的名称如 tostring。通知都由 il2cpp.exe 转换的方法是有趣的事情:

· 在c++中这些不是成员函数。所有方法都是free functions,其中的第一个参数是"this"指针。关于托管代码中的静态函数,IL2CPP 总是让这第一个参数this设为 NULL 值。通过始终声明具有"this"指针作为第一个参数的方法,我们简化了il2cpp.exe生成代码方法和我们使其他方法 (如委托) 的调用方法生成的代码更简单。

· 每个方法有一个额外的参数的类型是MethodInfo *,其中包括用于类的虚拟方法调用的有关方法的元数据。Mono的脚本后端使用特定于平台的trampolines传递此元数据。关于IL2CPP,我们已经决定避免使用trampolines,有助于可移植性。

· 所有方法都声明 extern "C",以便 il2cpp.exe 有时可以对 c++ 编译器撒谎和对待所有方法,因为如果他们有相同的类型。

· 以"_t"后缀命名的类型。以"_m"后缀命名的方法。命名冲突解决的每个名称后附加一个唯一的数字。如果任何用户脚本代码中发生更改这些数字将会改变,所以你不能在build时指望他们。

前两个点暗示每个方法有至少两个参数,是"this"指针和 MethodInfo 指针。这些额外的参数会导致不必要的开销吗?尽管他们会增加开销,到目前为止我们从来没有见过那些额外的参数会导致性能问题。尽管它看起来他们可能会导致,分析表明性能的差异是不可以衡量太小了。

我们可以使用 Ctags 工具跳转到这个 ToString 方法的定义。它是在 Bulk_UnityEngine_0.cpp 文件中。代码中该方法的定义看起来不太像 C# 代码中的 Vector3::ToString() 方法。然而,如果你使用像 ILSpy 这样的工具来反射 Vector3::ToString() 方法的代码,您将看到生成的 c + + 代码看起来非常类似于 IL 代码。

为什么 il2cpp.exe 不会为每个类型的一样的方法声明分别产生一个单独的 c + + 文件,这个 Bulk_UnityEngine_0.cpp 文件是相当大,其实, 行 !我们发现我们正在使用的 c + + 编译器有大量的源代码文件的麻烦。编译四千个.cpp 文件时间远远多于 个.cpp相同的源代码 文件编译。所以 il2cpp.exe 类型分组的批方法定义,并每个组生成一个 c + + 文件,。

现在跳回方法声明的头文件,并注意到该文件的顶部附近的这行:#include "codegen/il2cpp-codegen.h"

il2cpp-codegen.h文件包含生成的代码用来访问 libil2cpp 运行时服务的接口。我们将讨论一些运行时使用的方法生成的代码。

[置顶]
        (八)Unity5.0新特性------IL2CPP Internals: 生成的代码之旅(置顶聊天折叠怎么开启)

Method prologues 让我们看看 Vector3::ToString() 方法的定义。具体说来,它具有共同的prologue部分,由 il2cpp.exe emitted的所有方法。

StackTraceSentry _stackTraceSentry(&Vector3_ToString_m_MethodInfo);static bool Vector3_ToString_m_init;if (!Vector3_ToString_m_init){ ObjectU5BU5D_t4_il2cpp_TypeInfo_var = il2cpp_codegen_class_from_type(&ObjectU5BU5D_t4_0_0_0); Vector3_ToString_m_init = true;}

这样的prologue的第一行创建一个局部变量的类型 StackTraceSentry。此变量用于跟踪托管的调用堆栈,因此,IL2CPP 可以报告它在调用像 Environment.StackTrace。此条目的代码生成是实际上是可选的并在这种情况下启用的--启用栈跟踪选项传递给 il2cpp.exe (因为我在WebGL Player Settings中设置Enable Exceptions选项为Full)。对于小函数,我们发现此变量的开销对性能有负面的影响。所以对于 iOS 和其他平台,在那里我们可以使用特定于平台的堆栈跟踪信息,我们永远不会发出这条线到生成的代码。WebGL,我们没有特定于平台的堆栈跟踪支持,因此有必要允许托管的代码异常才能正常工作。

prologue的第二部分没有延迟初始化的数组或在方法体中使用的泛型类型的类型元数据。所以名称 ObjectU5BU5D_t4 是类型名为 System.Object [] 。prologue的这一部分只执行一次,并经常做什么如果类型已初始化在其他地方,所以我们还没有看到任何负面性能影响从生成的代码。

可是此代码线程安全吗?如果两个线程同时调用 Vector3::ToString()?其实,此代码并不成问题,因为所有的 libil2cpp 运行时用于类型中的代码初始化是安全的从多个线程中调用。它是可能 (甚至可能) 会不止一次,调用 il2cpp_codegen_class_from_type 函数,但它的实际工作才会有一次,发生在一个线程上。方法执行不会继续,直到初始化已完成。所以这方法开场白是线程安全的。

Runtime checks运行时检查 该方法的下一部分创建一个对象数组、 Vector3的 x 字段的值存储在本地,然后盒当地和将其添加到索引从零开始的数组。下面是生成的 c + + 代码 (用一些注释功能): // Create a new single-dimension, zero-based object array ObjectU5BU5D_t4* L_0 = ((ObjectU5BU5D_t4*)SZArrayNew(ObjectU5BU5D_t4_il2cpp_TypeInfo_var, 3)); // Store the Vector3::x field in a local float L_1 = (__this->___x_1); float L_2 = L_1; // Box the float instance, since it is a value type. Object_t * L_3 = Box(InitializedTypeInfo(&Single_t_il2cpp_TypeInfo), &L_2); // Here are three important runtime checks NullCheck(L_0); IL2CPP_ARRAY_BOUNDS_CHECK(L_0, 0); ArrayElementTypeCheck (L_0, L_3); // Store the boxed value in the array at index 0 *((Object_t **)(Object_t **)SZArrayLdElema(L_0, 0)) = (Object_t *)L_3;

三个运行时检查不存在 IL 代码中,但反而被由 il2cpp.exe注入。• 该 NullCheck 代码将引发NullReferenceException,如果数组的值为 null。• 该 IL2CPP_ARRAY_BOUNDS_CHECK 代码将引发 IndexOutOfRangeException,如果数组索引不正确。• 该 ArrayElementTypeCheck 代码会引发的 ArrayTypeMismatchException,如果被添加到该数组中元素的类型不正确。

这些三个运行时检查是由.NET 虚拟机提供的所有保证。而不是注入代码,Mono脚本后端使用平台特定的信号转导机制来处理这些相同的运行时检查。对于 IL2CPP,我们想要更多的平台得到不可知论的和支持的平台,像 WebGL,那里有没有特定于平台的信号转导机制,所以 il2cpp.exe 注入这些检查。 做这些运行时检查会导致性能问题吗?在大多数情况下,在性能上,我们没看到任何不利的影响,他们提供的好和.NET 虚拟机所需的安全。不过,在几个特定的情况下我们看到这些检查,导致性能下降,尤其是在紧凑循环中。我们正在做现在允许托管的代码进行注释以移除这些运行时检查,当 il2cpp.exe 生成 c + + 代码。敬请关注这一方面。 Static Fields静态字段 现在,我们已经看到如何实例字段(Vector3 类型),让我们看到了静态字段转换和访问。找到的 HelloWorld_Start_m3 方法定义,是在我生成的 Bulk_Assembly CSharp_0.cpp 文件中定义。从那里,跳转到 Important_t1 类型 (在 theAssemblyU2DCSharp_HelloWorld_Important.h 文件中):

struct Important_t1 : public Object_t{ // System.Int HelloWorld/Important::InstanceIdentifier int_t ___InstanceIdentifier_1;};struct Important_t1_StaticFields{ // System.Int HelloWorld/Important::ClassIdentifier int_t ___ClassIdentifier_0;};

Notice that il2Notice that il2cpp.exe has generated a separate C++ struct to hold the static field for this type, since the static field is shared between all instances of this type. So at runtime, there will be one instance of the Important_t1_StaticFields type created, and all of the instances of the Important_t1 type will share that instance of the static fields type. In generated code, the static field is accessed like this:请注意,il2cpp.exe 已对此类型的静态字段生成一个单独的 c + + 结构体,因为该静态字段这种类型的所有实例之间要共享。所以在运行时,会有创建,Important_t1_StaticFields 类型的一个实例,所有 Important_t1 类型的实例将共享该实例的静态字段的类型。生成的代码中访问静态字段时像这样:

int_t L_1 = (((Important_t1_StaticFields*)InitializedTypeInfo(&Important_t1_il2cpp_TypeInfo)->static_fields)->___ClassIdentifier_0);

Important_t1 的类型元数据握着一个指针,指向的 Important_t1_StaticFields 类型的一个实例,该实例用于获取静态字段的值。

Exceptions例外情况

托管异常被il2cpp.exe转换为c + +异常。我们选择了这条道路,以再次避免特定于平台的解决办法。当 il2cpp.exe 需要emit代码引发的托管的异常时,它将调用 il2cpp_codegen_raise_exception 函数。 在我们的 HelloWorld_Start_m3 方法来引发和捕捉托管的异常的代码如下所示:try{ // begin try (depth: 1) InvalidOperationException_t7 * L_ = (InvalidOperationException_t7 *)il2cpp_codegen_object_new (InitializedTypeInfo(&InvalidOperationException_t7_il2cpp_TypeInfo)); InvalidOperationException__ctor_m8(L_, (String_t*) &_stringLiteral5, /*hidden argument*/&InvalidOperationException__ctor_m8_MethodInfo); il2cpp_codegen_raise_exception(L_); // IL_: leave IL_a8 goto IL_a8;} // end try (depth: 1)catch(Il2CppExceptionWrapper& e){ __exception_local = (Exception_t8 *)e.ex; if(il2cpp_codegen_class_is_assignable_from (&InvalidOperationException_t7_il2cpp_TypeInfo, e.ex->object.klass)) goto IL_; throw e;}IL_:{ // begin catch(System.InvalidOperationException) V_1 = ((InvalidOperationException_t7 *)__exception_local); NullCheck(V_1); String_t* L_ = (String_t*)VirtFuncInvoker0< String_t* >::Invoke(&Exception_get_Message_m9_MethodInfo, V_1); Debug_Log_m6(NULL /*static, unused*/, L_, /*hidden argument*/&Debug_Log_m6_MethodInfo);// IL_a3: leave IL_a8 goto IL_a8;} // end catch (depth: 1)

所有托管的异常将包装在 c + + 的Il2CppExceptionWrapper 类型内。当生成的代码捕获该类型的异常时,它解包 (其类型 Exception_t8) 的托管异常的 c + + 表示。我们期待在这种情况下,只为能反转,所以如果我们找不到该类型的异常的 c + + 异常的副本又扔了回来。如果我们找到正确的类型,该代码跳转到的 catch 处理程序,执行并写出的异常消息。

Goto!?! 这段代码提出了一个有趣的点。这些标签和 goto 语句在那里做什么?这些构造是不必要的结构化编程 !然而,IL 没有结构化编程概念,如循环及 if/then 语句。因为它是较低级别,il2cpp.exe 遵循低级别概念生成的代码中。例如,让我们看看 for 循环在 HelloWorld_Start_m3 中的方法: IL_a8:{ V_2 = 0; goto IL_cc;}IL_af:{ ObjectU5BU5D_t4* L_ = ((ObjectU5BU5D_t4*)SZArrayNew(ObjectU5BU5D_t4_il2cpp_TypeInfo_var, 1)); int_t L_ = V_2; Object_t * L_ =Box(InitializedTypeInfo(&Int_t5_il2cpp_TypeInfo), &L_); NullCheck(L_); IL2CPP_ARRAY_BOUNDS_CHECK(L_, 0); ArrayElementTypeCheck (L_, L_);*((Object_t **)(Object_t **)SZArrayLdElema(L_, 0)) = (Object_t *)L_; Debug_LogFormat_m7(NULL /*static, unused*/, (String_t*) &_stringLiteral6, L_, /*hidden argument*/&Debug_LogFormat_m7_MethodInfo); V_2 = ((int_t)(V_2+1));}IL_cc:{ if ((((int_t)V_2) < ((int_t)3))) { goto IL_af; }} 这里的 V_2 变量是循环索引。是开始的一个值为 0,然后递增下面这一行中的循环: V_2 = ((int_t)(V_2+1));

然后在这里检查循环的结束条件: if ((((int_t)V_2) < ((int_t)3)))

只要 V_2 是小于 3,goto 语句跳转到 IL_af 标签,这是循环体的顶部。你可能能够猜出那 il2cpp.exe 器当前正在生成 c + + 代码直接从 IL,而无需使用中间的抽象语法树表示形式。如果您猜到这,你是正确的。你可能已经还注意到在运行时检查上述一些生成的代码看起来像这样: float L_1 = (__this->___x_1); float L_2 = L_1; 显然,采用 L_2 变量在这里不是必要的。大多数 c + + 编译器可以优化掉这额外的任务,但是我们想要避免emitting它在所有。我们目前正在研究使用 AST 来更好地理解 IL 代码和生成更好的 c + + 代码涉及本地变量的情况下,for 循环,其中的可能性。

Conclusion结论 我们只是抓到一个非常简单的项目的 IL2CPP 脚本后端所生成的 c + + 代码的表面。如果你没这么做过,我鼓励你来到您的项目中生成的代码。当你在探索,请牢记我们正在不断努力提高构建和运行时性能的 IL2CPP 脚本后端所生成的 c + + 代码将看上去不同的,未来版本中。

通过将 IL 代码转换为 c + + 中,我们已经能够获得很好的平衡,便携式和高性能代码之间。我们可以有很多不错的开发人员友好功能的托管代码中,同时仍获得 c + + 编译器提供各种平台的质量机器代码的好处。

在将来职位,我们会探索更多生成的代码,包括方法调用、 分享的方法实现和调用到本机库的包装。但下一次我们将调试一些为使用 Xcode iOS 位内部版本生成的代码。

文章的源地址:

Unity3d 物理碰撞de那点事(不是触发) 还是那句话大神勿喷不喜勿喷若有不对的地方欢迎指正欢迎拍砖。。。OK!!其余的废话就不多说的了直接进入正题。那么,今天要讲碰撞,碰撞碰撞,

一起来学u3d之圣典.7 一起来学u3d之圣典.7废话不多说,一针见血,go!一起来看API其中不懂的大家互相帮助哈英文部分版权属©Unity公司所有,中文部分©Unity圣典版权所有。

《Unity网络多玩家游戏开发教程(上册)》 欢迎大家到我们团队的官网上查看此书的详细介绍,下面是链接《Unity网络多玩家游戏开发教程(上册)》我们的宗旨是:我们只做最专业的技术传播者!

标签: 置顶聊天折叠怎么开启

本文链接地址:https://www.jiuchutong.com/biancheng/375684.html 转载请保留说明!

上一篇:[置顶] uGUI元素显示在角色的头顶上([置顶]从lv2开始开挂的原勇者候悠闲的异世界生活)

下一篇:Unity3d 物理碰撞de那点事(不是触发)(unity3d物体碰撞)

  • 支付城镇土地使用税会计科目
  • 印花税科目会计分录
  • 生产设备保险费会计分录
  • 购进固定资产的安装费计入原值吗?
  • 资产负债表的固定资产怎么计算
  • 库存商品在贷方怎么调整
  • 交通费怎么抵扣进项税
  • 房产出租增值税税目
  • 工伤私了赔偿
  • 免征增值税企业进项税怎么处理
  • 股权变更印花税申报表怎么填写
  • 劳动法相关法规
  • 企业所得税税前扣除和不扣除的区别
  • 厂部管理人员工资属于什么会计科目
  • 增值税纳税申报表怎么填
  • 营改增后房地产企业如何开票
  • 跨年度取得增值税发票
  • 企业为一般纳税人税率多少
  • 因为买房子
  • 小规模自开专票怎么交税
  • 电子发票已缴税怎么查
  • 个人买卖黄金如何缴税
  • 未立项进行建设
  • 某房产开发公司向银行借款
  • 增值税专用发票查询系统官方网站
  • 回收站已损坏 是否清空该驱动,点鼠标无法操作
  • 机票的保险费能开发票吗
  • mac双系统切换键
  • 长期待摊会计分录
  • php新手入门
  • os x yosemite wifi断线怎么办?yosemite wifi掉线解决详细步骤
  • 入库前的准备
  • php readdir函数
  • 税收滞纳金可以抵税吗
  • php __destruct
  • 工程款清欠管理办法
  • 纳税申报方式的税务申报工作常规流程图
  • 最有艺术气质的动物
  • 潘塔纳尔湿地位于巴拉圭盆地
  • 增值税专用发票丢了怎么补救
  • 主营业务收入月末需要结转吗
  • 前端作业做一个网站
  • 【机器学习】前置知识:矩阵的表示与定义 | Identity 身份矩阵 | 逆矩阵和转置 | 标量乘法
  • 公司发放工作服账务处理
  • {dede:channel type='son'}无栏目调用同级栏目
  • 资产负债表怎么算
  • SQL Server 2008 数据库有哪些版本?
  • 短期借款利息怎么做账
  • 一般纳税人简易征收的适用范围
  • 固定资产相关业务
  • 机票退票费计入什么科目
  • 怎么把应付账款调成应收
  • 行政单位的财务报表包括哪些
  • 支付兼职人员工资会计处理
  • 房地产企业资产负债率的正常范围
  • mysql批量删除数据库死锁
  • 经典sql查询语句50条
  • windows找不到文件请确定文件名是否正确
  • win8旗舰版和专业版区别
  • Win10预览版桌面图标和任务栏不翼而飞怎么办?
  • 如何提高windows7运行速度
  • mac安装git客户端
  • raid主要使用三种技术
  • win8系统崩溃无法开机
  • win8更改账户
  • 正版win10怎么用
  • windows 8.1安装教程
  • shell脚本用法
  • jqgrid设置宽度
  • 使用node.js实现用IP地址查询天气情况
  • 输出语句的执行过程
  • 浅谈双减背景下的高效课堂
  • python接口编写
  • JavaScript基本语法与页面对象的应用
  • 用javascript
  • js合并两个数组并排序
  • ruby元编程第二版
  • 北京国税电子税务局
  • 云南烟草税收是多少
  • 土地招拍挂土地出让金
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设