Retrieving a function (delegate) type that uses GenericTypeParameterBuilder types from the assembly that is currently being built

Viewed 99

I am trying to make a compiler in F# using the API provided in System.Reflection.Emit. I am running into a problem when I try to create functions (delegates) that use GenericTypeParameterBuilder types from the assembly that I am currently building. Example translating the following Java (a slightly modified version of Java) class:

class A extends Object {
    A () {}
    Function<A, A> fun() {
        return (A a) -> { return a; }; // OK
    }
}
class B<X extends Object> extends Object {
    X val;
    B (X val) { this.val = val; }
    Function<X, X> fun() {
        return (X x) -> { return x; }; // does not work to translate
    }
}

So, I need to be able to use the System.Type of Func<A, A> and Func<X, X> in order to generate the following CIL instructions (and some similar ones that use the same Func types):

callvirt instance !1 class [mscorlib]System.Func`2<class A, class A>::Invoke(!0)
callvirt instance !1 class [mscorlib]System.Func`2<!X,!X>::Invoke(!0)

The code generating the respective instructions is:

let deleg = System.Linq.Expressions.Expression.GetFuncType(types)
let method = deleg.GetConstructor([|typeof<obj> ; typeof<nativeint>|]).DeclaringType.GetMethod("Invoke")
ilGenerator.Emit(OpCodes.Callvirt, method)

where types is an array of System.Type elements. In case of Func<A, A>, the types array is [|typeBuilder.GetType() ; typeBuilder.GetType()|] However, the same is not working for Func<X, X> from class B, as using [|genericTypeParameterBuilder.GetType() ; genericTypeParameterBuilder.GetType()|] for the types array generates a weird CIL instruction (the assembly is still being built, but the instruction is wrong):

callvirt instance !1 class [mscorlib]System.Func`2<class [mscorlib]System.Reflection.Emit.GenericTypeParameterBuilder, class [mscorlib]System.Reflection.Emit.GenericTypeParameterBuilder>::Invoke(!0)

The way the typeBuilder for the class A is generated is:

let typeBuilder = moduleBuilder.DefineType(typ, TypeAttributes.Public ||| TypeAttributes.Class)
typeBuilder.SetParent(typeof<obj>)
let constrBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, [||])
let methodBuilder = typeBuilder.DefineMethod("fun", MethodAttributes.Public)
let delegRetType =  System.Linq.Expressions.Expression.GetDelegateType([|typeof<obj> ; typeof<int>|]).GetConstructor([|typeBuilder.GetType() ; typeBuilder.GetType()|]).DeclaringType
methodBuilder.SetReturnType(delegRetType) 
methodBuilder.SetParameters([||])
//buildConstrBody - nothing special
//buildMethodBody - generating the instructions using the methodBuilder .GetILGenerator() - using various System.Func types
typeBuilder.CreateType()

Same happens for type B, the extra genericTypeParameterBuilder being constructed like this, with no modifiers or restrictions:

let genericTypeParameterBuilder = typeBuilder.DefineGenericParameters([|"X"|]).[0]

The way I am building the types array is using the following function:

let rec typesForFuncRec (types: System.Type list) : System.Type list = 
    match types with
        | [] -> []
        | typ :: rest -> 
            match typ with
                | :? GenericTypeParameterBuilder as gtpb ->     
                    printfn "generic GetType()            : %O" (gtpb.GetType())
                    printfn "generic :>                   : %O" (gtpb :> System.Type)
                    printfn "generic ReflectedType        : %O" (gtpb.ReflectedType)
                    printfn "generic UnderlyingSystemType : %O" (gtpb.UnderlyingSystemType)
                    printfn "generic MakeByRefType()      : %O" (gtpb.MakeByRefType())  
                    gtpb.GetType() :: (typesForFuncRec rest)
                | :? TypeBuilder as tb -> tb.GetType() :: (typesForFuncRec rest)
                | _ -> typ :: (typesForFuncRec rest)
;;

Console:

generic GetType()            : System.Reflection.Emit.GenericTypeParameterBuilder
generic :>                   : X
generic ReflectedType        : B[X]
generic UnderlyingSystemType : X
generic MakeByRefType()      : X&

Instead of gtpb.GetType() I have also tried to use gtpb :> System.Type and gtpb.UnderlyingSystemType, which trigger the following error:

System.NotSupportedException: Specified method is not supported.
   at System.Reflection.Emit.TypeBuilderInstantiation.GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
   at System.Type.GetConstructor(BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers)

I have also tried gtpb.MakeByRefType(), which triggers the following error:

System.ArgumentException: type must not be ByRef
   at System.Linq.Expressions.Expression.GetFuncType(Type[] typeArgs)

The questions are:

  1. How can I possibly get the System.Func type for generic type arguments? I have tried for a few hours and it starts driving me crazy.

  2. (way less important, I am just curious) For the System.Func of regular class type builders, why do I need to retrieve the System.Type as typeBuilder.GetType(), when, for every other operation (method return type/ parameter type), I can just use a cast: typeBuilder :> System.Type ??

0 Answers
Related