Search code examples
c#cilmono.cecil

CIL instruction "isinst [System.Runtime]System.Int32" for value 0 (int32) - what will return?


I have a simple C# function

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

As expected Isinst_intSimple(0) returns true

After decompiling, the function looks like:

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

What will the IL_0002 isinst [System.Runtime]System.Int32 instruction push on the evaluation stack after execution (the input will be 0(int32))?

According to the MS documentation, the Isinst instruction should return an object reference - The result (either an object reference or a null reference) is pushed onto the stack.

But what is the reference to 0? and if. What should the instruction IL_0008: cgt.un do?

My interpretation

In my interpretation of all the IL instruction - the function retrun 0. And I can't find a way to return true.

Below is my interpretation of the function execution:

 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}

I think the problem is in IL_0002: isinst or IL_0008: cgt.un, but I can't find it.

Background

I'm working on a Virtual Machine that emulates C# dll - instruction by instruction (executing C# in "virtual machine") but I have problem simulating a test function Isinst_intSimple(0). My version return 0 but it should return true


Solution

  • ECMA-335 specification, which defines IL bytecode, says the following:

    I.12.1.6.2.6 castclass and isinst on value types

    Casting to and from value type instances isn’t permitted (the equivalent operations arebox and unbox). When boxed, however, it is possible to use the isinst instruction to see whether a value of type System.Object is the boxed representation of a particular class.

    III.4.6 - isinst

    Test if obj is an instance of typeTok, returning null or an instance of that class or interface.

    Stack Transition: ..., obj => ..., result

    Description: typeTok is a metadata token (a typeref, typedef or typespec), indicating the desired class. If typeTok is a non-nullable value type or a generic parameter type it is interpreted as “boxed” typeTok

    Verifiability: Verification tracks the type of result as typeTok.

    So the result of isinst on a value-type (struct) is a boxed ObjectRef to the value.

    II.1.5 Operand type table

    cgt.un is allowed and verifiable on ObjectRefs (O). This is commonly used when comparing an ObjectRef with null (there is no “compare-not-equal” instruction, which would otherwise be a more obvious solution)

    So isinst ldnull cgt.un will tell you if an object is of a particular type