mardi 5 janvier 2016

Quotations is lost after converted to delegate

With F# derived pattern MethodWithReflectedDefinition, we can get the quotation object from a MethodInfo. But if we convert an F# function or member method into a delegate, we cannot get the quotations even that function or method is tagged with [<ReflectedDefinition>]. Here is an example:

open System
open System.Reflection
open System.Runtime.CompilerServices
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.DerivedPatterns
open NUnit.Framework

let printQuotation (methodInfo:MethodInfo) =
    match methodInfo with
    | MethodWithReflectedDefinition expr -> printfn "%A" expr
    | _ -> printfn "Cannot get quotations."

let printQuotationFromDelegate (del:Func<int, int>) =
    printQuotation del.Method

[<ReflectedDefinition>]
let foo a = a + 1

type MyClass private () =

    [<ReflectedDefinition>]
    static member Foo(a:int) =
        a + 1

[<Test>]
let test() =
    let methodInfo = typeof<MyClass>.GetMethod("Foo", BindingFlags.Static ||| BindingFlags.Public)
    printQuotation methodInfo   // works

    // cannot get quotations
    printQuotationFromDelegate(Func<_,_> MyClass.Foo)

    // cannot get quotations
    printQuotationFromDelegate(Func<_,_> foo)

If I check the assembly IL code, I found the reason is, the normal foo value and MyType.Foo method have [ReflectedDefinition] attribute:

.method public static 
    int32 Foo (
        int32 a
    ) cil managed 
{
    .custom instance void [FSharp.Core]Microsoft.FSharp.Core.ReflectedDefinitionAttribute::.ctor() = (
        01 00 00 00
    )
    // Method begins at RVA 0x2150
    // Code size 5 (0x5)
    .maxstack 8

    IL_0000: nop
    IL_0001: ldarg.0
    IL_0002: ldc.i4.1
    IL_0003: add
    IL_0004: ret
} // end of method MyClass::Foo

But the converted delegate doesn't use these functions, it uses a generated method, which is a copy of the original method. But unfortunately, these generated functions have no such ReflectedDefinition attribute, and I think the quotations are not connected to these generated method:

IL_0021: newobj instance void QuotationInDelegate/test@33::.ctor()
IL_0026: ldftn instance int32 QuotationInDelegate/test@33::Invoke(int32)
IL_002c: newobj instance void class [mscorlib]System.Func`2<int32, int32>::.ctor(object, native int)
IL_0031: call void QuotationInDelegate::printQuotationFromDelegate(class [mscorlib]System.Func`2<int32, int32>)

and the generated method test@33:Invoke:

.class nested assembly auto auto sealed specialname serializable beforefieldinit test@33
    extends [mscorlib]System.Object
{
    .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = (
        01 00 06 00 00 00 00 00
    )
    // Methods
    .method public specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        // Method begins at RVA 0x2158
        // Code size 7 (0x7)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: call instance void [mscorlib]System.Object::.ctor()
        IL_0006: ret
    } // end of method test@33::.ctor

    .method assembly hidebysig 
        instance int32 Invoke (
            int32 arg
        ) cil managed 
    {
        // Method begins at RVA 0x2160
        // Code size 8 (0x8)
        .maxstack 8

        IL_0000: nop
        IL_0001: ldarg.1
        IL_0002: call int32 QuotationInDelegate/MyClass::Foo(int32)
        IL_0007: ret
    } // end of method test@33::Invoke

} // end of class test@33

So, how can we retain the quotation information after it is converted to delegate?





Aucun commentaire:

Enregistrer un commentaire