none
How can I change any type from function signature to object type in IL? RRS feed

  • Question

  • I saw the link below, and want to implement my dynamic IL for making any type to Oject type.
    https://blogs.msdn.microsoft.com/davbr/2006/02/27/bug-inspecting-value-type-returns/

    My way is below:
    1.If a CorElementType is not value type goto end
    2.Find the CorElementType's  type string (eg.  ELEMENT_TYPE_BOOLEAN -> L"System.Boolean", ELEMENT_TYPE_I4 -> L"System.Int32"  )
    3.Find mdTypeRef from the module token by FindTypeRef with type string.
    4.Write IL :  box <mdTypeRef>

    Q1: Any problem with my way above?
    Q2: How can I judge a CorElementType whether it need box or not? 
          I meet an Enumerations argument, and I don't know how to judge his CorElementType and get his mdTypeRef.
    Q3: At step2, I need to make a map to change CorElementType to string, then find mdTypeRef by string. 
          It is not elegant, any way more elegant? 
          Can I box all value type with "System.ValueType"'s mdTypeRef directly?

    Really appreciate!
    • Edited by IMMark Sunday, May 15, 2016 2:55 PM mistake at Q2
    Saturday, May 14, 2016 11:53 AM

Answers

  • Q1: It should be ok for primitive value types, but ...

    • Generic value types will require a TypeSpec token so that you can specify the generic type arguments.
    • Pointers (ELEMENT_TYPE_PTR) can't be boxed. though you can cast the pointer to an IntPtr (call IntPtr.op_Explicit(void*)) and box the result.
    • 'by-ref' arguments (ELEMENT_TYPE_BYREF) are like pointers and can't be boxed. but you can dereference (ldind.ref) them and box the result.

    Q2: I'm not sure what you are asking here. If it helps, you can safely box reference types (just results in the same object), otherwise:

    • Primitive types should be obvious (double is a value type, string is a reference type).
    • Types reference by a metadata token will be referenced using either ELEMENT_TYPE_CLASS or ELEMENT_TYPE_VALUETYPE. The former indicates it's a reference type, the latter a value type.
    • Closed generic types are referenced using ELEMENT_TYPE_GENERICINST, and will be a value type if-and-only-if the generic type definition is a value type (eg. Foo<int> will be a value type if Foo<> is a value type).
    • Generic type parameters are referenced using ELEMENT_TYPE_VAR and ELEMENT_TYPE_MVAR, it's probably easier and safer to just 'box' it regardless.

    Q3: You could do your lookup at module load time and cache the result for the JITCompilationStarted callback.

    I can't say I have ever tried boxing as a 'System.ValueType', but I wouldn't expect it to work. Boxing needs to encode the exact type into the object, but the values on the evaluation stack are sometimes ambiguous.

    Consider the following:

    ldc.i4.0
    box System.ValueType
    Should it result in a boxed int? bool? uint? short? char? byte? ....




    Sunday, May 15, 2016 1:07 PM

All replies

  • Q1: It should be ok for primitive value types, but ...

    • Generic value types will require a TypeSpec token so that you can specify the generic type arguments.
    • Pointers (ELEMENT_TYPE_PTR) can't be boxed. though you can cast the pointer to an IntPtr (call IntPtr.op_Explicit(void*)) and box the result.
    • 'by-ref' arguments (ELEMENT_TYPE_BYREF) are like pointers and can't be boxed. but you can dereference (ldind.ref) them and box the result.

    Q2: I'm not sure what you are asking here. If it helps, you can safely box reference types (just results in the same object), otherwise:

    • Primitive types should be obvious (double is a value type, string is a reference type).
    • Types reference by a metadata token will be referenced using either ELEMENT_TYPE_CLASS or ELEMENT_TYPE_VALUETYPE. The former indicates it's a reference type, the latter a value type.
    • Closed generic types are referenced using ELEMENT_TYPE_GENERICINST, and will be a value type if-and-only-if the generic type definition is a value type (eg. Foo<int> will be a value type if Foo<> is a value type).
    • Generic type parameters are referenced using ELEMENT_TYPE_VAR and ELEMENT_TYPE_MVAR, it's probably easier and safer to just 'box' it regardless.

    Q3: You could do your lookup at module load time and cache the result for the JITCompilationStarted callback.

    I can't say I have ever tried boxing as a 'System.ValueType', but I wouldn't expect it to work. Boxing needs to encode the exact type into the object, but the values on the evaluation stack are sometimes ambiguous.

    Consider the following:

    ldc.i4.0
    box System.ValueType
    Should it result in a boxed int? bool? uint? short? char? byte? ....




    Sunday, May 15, 2016 1:07 PM
  • Hi, it works well for primitive value types and types reference by a metadata token, but I have a problem token to get some metadata token.

    I write a small program, like this:
    int Foo(System.Int32 a, System.String b)
    {
      try
      {
        int z = 0;
        int y = 1;
        while(a<2)
        {
            z = y / a;
            a++;
        }
        return z;
      }
      catch(Exception e)
      {
        Console.WriteLine(e.Message);
        throw;
      }
    }

    As you say:
    To get the metadata token for "System.Int32" you can get the TypeRef token by using ICorProfilerInfo::GetModuleMetaData to get an IMetaDataImport instance for the module you are instrumenting and then using IMetaDataImport::FindTypeRef or IMetaDataImport::EnumTypeRefs to get the token.

    I get it's IMetaDataImport, then use IMetaDataImport::EnumTypeRefs to get all TypeDefs and get their name by GetTypeRefProps.  I can find "System.Exception", but why can not find "System.Int32"?

    "System.Int32" is defined in Mscorlib Assembly, and MSCorlib.dll is loaded and bound in every .NET application, it seem like every .NET application reference MSCorlib.
    So it should be found in every .NET application ?! Where can I know these details , from some book?

    Thanks a lot.

    • Edited by IMMark Thursday, June 2, 2016 6:15 AM update
    Thursday, June 2, 2016 6:03 AM
  • Int32 has a short form (Partition II of ECMA-335, section 23.2.16) that can be used in signatures and the specification states that if a type has a short form, then the short form must be used in signatures rather than referencing the type via a metadata token. This means that if the module does not box/unbox an int, call one of it's methods or contain a 'typeof(int)', then it probably won't need a TypeRef for System.Int32.

    To fix this, I can see two possible options:

    1) In ModuleLoadFinished, check to see if the module has the TypeRef's/TypeSpec's you will want, if it doesn't, then request the IMetaDataEmit for the module and add them.

    2) If you don't have a TypeRef for 'System.Int32' but you have a MethodSpec for 'System.Nullable<System.Int32>(System.Int32)' then you could achieve the same effect by taking advantage of the special casing the CLR applies to nullable types.

    ldarg.0
    newobj Nullable<int>(int)
    box Nullable<System.Int32>

    Thursday, June 2, 2016 10:14 AM