值 0 (int32) 的 CIL 指令“isinst [System.Runtime]System.Int32” - 将返回什么?

问题描述

我有一个简单的 C# 函数

public bool Isinst_intSimple(object value)
{
    return value is int;
}

正如预期的那样 Isinst_intSimple(0) 返回 true

反编译后的函数如下:

IL_0000: nop
IL_0001: ldarg.1      // 'value'
IL_0002: isinst       [System.Runtime]system.int32
IL_0007: ldnull
IL_0008: cgt.un
IL_000a: stloc.0      // V_0
IL_000b: br.s         IL_000d

// [196 9 - 196 10]
IL_000d: ldloc.0      // V_0
IL_000e: ret

IL_0002 isinst [System.Runtime]system.int32 指令在执行后会推送到计算堆栈上什么(输入将为 0(int32))?

根据 MS 文档,Isinst 指令应该返回一个对象引用 - The result (either an object reference or a null reference) is pushed onto the stack.

但是对 0 的引用是什么?而如果。指令 IL_0008: cgt.un 应该做什么?

我的解释

在我对所有 IL 指令的解释中 - 函数 retrun 0。而且我找不到返回 true方法

以下是我对函数执行的解释:

 IL_0000: nop
IL_0001: ldarg.1      // push 0 on evaluation stack – stack after {0}
IL_0002: isinst       [System.Runtime]system.int32    //pop and object reference is pushed onto the stack. - stack after { 0* or reference to 0 – Boxing? }
IL_0007: ldnull //– stack after {null,0*}
IL_0008: cgt.un //– 0* is not greater than null so its return 0 -  stack after {0}
IL_000a: stloc.0      // V_0 – pop to V_0 – stack after {}
IL_000b: br.s         IL_000d // – stack after {}

// [196 9 - 196 10]
IL_000d: ldloc.0      // V_0 //– push V_0 – stack after {0}
IL_000e: ret    //return {0}

我认为问题出在 IL_0002: isinstIL_0008: cgt.un,但我找不到。

背景

我正在开发一个模拟 C# dll 的虚拟机 - 一条指令(在“虚拟机”中执行 C#),但我在模拟测试函数 Isinst_intSimple(0) 时遇到问题。我的版本返回 0 但它应该返回 true

解决方法

ECMA-335 specification 定义了 IL 字节码,内容如下:

I.12.1.6.2.6 castclassisinst 关于值类型

不允许在值类型实例之间进行转换(等效操作是装箱和拆箱)。但是,在装箱时,可以使用 isinst 指令查看 System.Object 类型的值是否是特定类的装箱表示。

III.4.6 - isinst

测试 obj 是否是 typeTok 的实例,返回 null 或该类或接口的实例。

堆栈转换:...,obj => ...,结果

描述typeTok 是元数据标记(typeref、typedef 或 typespec),指示所需的类。如果 typeTok 是不可为空的值类型或泛型参数类型,则将其解释为“装箱”typeTok

可验证性:验证将 result 的类型跟踪为 typeTok

因此 isinst 在值类型(结构)上的结果是该值的装箱 ObjectRef。

II.1.5 操作数类型表

cgt.un 在 ObjectRefs (O) 上是允许且可验证的。这通常用于比较 ObjectRef 与 null(没有“比较不相等”指令,否则这是一个更明显的解决方案)

所以 isinst ldnull cgt.un 会告诉你一个对象是否属于特定类型