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:
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.
(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??