【问题标题】:How in CIL (MSIL) "call instance void valuetype [..type]" return or save value? (Common Intermediate Language)如何在 CIL (MSIL)“调用实例 void valuetype [..type]”中返回或保存值? (通用中间语言)
【发布时间】:2020-07-29 19:16:25
【问题描述】:

我正在创建一个 .Net 中间代码仿真器,它在指令之后执行 CIL 指令。 我无法更精确地模拟 call instance void valuetype 并保存其结果

我有 C# 代码:DateTime? startDate = DateTime.Now;

编译为:

IL_010d: ldloca.s     startDate
IL_010f: call         valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Now()
IL_0114: call         instance void valuetype [mscorlib]System.Nullable`1<valuetype [mscorlib]System.DateTime>::.ctor(!0/*valuetype [mscorlib]System.DateTime*/)
  • IL_010d - ldloca 上推送对局部变量startDate 的堆栈引用
  • IL_010f - call 上推送堆栈值类型 DateTime==Now

我的第一个问题是:IL_0114 发生了什么?执行 call 方法后堆栈应该是什么样子?如何将DateTime.Now 值输入到本地startData 变量中?在什么时候输入?

如果我将 C# 代码更改为 DateTime startDate = DateTime.Now;(不带?) 编译为:

IL_010d: call         valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Now()
IL_0112: stloc.2      // startDate
  • IL_010d - call 将值压入堆栈
  • IL_0112 - stloc 从堆栈中弹出值并保存在局部变量中

在这种情况下,一切对我来说都很清楚。即执行块后,栈上没有对象,DateTime.Now已写入局部变量startDate

我的第二个问题是这两个调用有什么区别? call instance void valuetype(IL_0114) 与 call valuetype(IL_010d)

【问题讨论】:

    标签: c# cil mono.cecil


    【解决方案1】:

    没有“call instance void valuetype”。只有call,其余都是方法引用。

    对于更简单的代码,valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Now() 是方法引用,但 valuetype [mscorlib]System.DateTime 只是返回类型的一部分(valuetype 表示该类型的未装箱形式,而不是 boxedclass)。

    在调用get_Now 之后,值进入堆栈。但是,当您将其转换为 Nullable&lt;DateTime&gt; 类型的变量时,您实际上调用了它的构造函数,该构造函数负责正确初始化该值。

    这里调用的方法是instance void valuetype [mscorlib]System.Nullable`1&lt;valuetype [mscorlib]System.DateTime&gt;::.ctor(!0),即构造函数。每个构造函数实际上都返回void,所以没有任何特殊指令,调用后栈是空的,而且由于方法也是instance,所以需要一个this的引用。同样valuetype 在这里表示您正在对该类型的未装箱值调用该方法。

    这是在值类型已分配存储位置时初始化值类型的常用方法之一。您只需获取其地址并就地调用构造函数,而不是使用newobj。同样,initobj 指令将其初始化为零,就像调用了默认构造函数一样。

    【讨论】:

      【解决方案2】:

      主要区别在于,通过将符号 ? 添加到变量声明中,您将其类型设置为 Nullable<T>,而不是简单的 System.DateTime。下面的两个声明基本上是等价的:

      DateTime? d1 = DateTime.Now;
      System.Nullable<DateTime> d1 = DateTime.Now;
      

      所以:

      IL_010d 将局部变量(startDate)的地址压入堆栈

      IL_010fDateTime.Now 的结果压入堆栈

      IL_0114 调用Nullable&lt;DateTime&gt; 构造函数(使用startDate 的地址作为this)传递DateTime.Now 的结果(初始化startDate

      阿德里亚诺

      【讨论】:

      • 所以在 IL_0114 中——我从堆栈中得到两个数据——第一个是这个,它由 IL_010d 中的值“启动”(不需要 newobj)。第二个是日期时间,其值为 Now。调用值变量的构造函数会改变变量本身的值吗?
      • 不需要 newobj) :完全正确。在这种情况下,值类型被分配在堆栈中,而不是在堆中,因此不需要使用 newobj 指令。 “为值变量调用构造函数会更改变量本身的值”:ctor(构造函数)是一种方法,它也可能像任何其他方法一样具有副作用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-03
      • 1970-01-01
      • 2011-04-02
      • 2020-01-18
      • 2020-03-08
      • 1970-01-01
      相关资源
      最近更新 更多