【问题标题】:F# How to compile a code quotation to an assemblyF#如何将代码引用编译为程序集
【发布时间】:2011-08-06 13:39:30
【问题描述】:

我想知道有没有办法将代码引用编译成程序集?

我了解可以在Exp<_> 对象上调用CompileUntyped()Compile(),例如:

let x = <@ 1 * 2 @>
let com = x.Compile()

但是,我怎样才能将com 作为一个程序集保存到磁盘?

谢谢。

【问题讨论】:

  • 我不确定 PowerPack 是否支持这一点。但顺便说一句,我根本不建议使用 PowerPack。他们的代码经常出错或太慢。使用 System.Reflection.Emit 从头开始​​编写自己的编译器或评估器可能会产生更好的结果。还有F#没有优化引用的问题,比如匹配变成了一系列if/then/else,而不是正常F#编译中CIL中的跳转指令。
  • 嗨,谢谢你,我想我会研究它们的替代品(比如你提到的 Reflection.Emit)。
  • F# equivalent to Eval的可能重复

标签: f# metaprogramming


【解决方案1】:

您可以使用模式匹配评估 F# 代码报价:

open Microsoft.FSharp.Quotations.Patterns

let rec eval  = function
    | Value(v,t) -> v
    | Call(None,mi,args) -> mi.Invoke(null, evalAll args)
    | arg -> raise <| System.NotSupportedException(arg.ToString())
and evalAll args = [|for arg in args -> eval arg|]

let x = eval <@ 1 * 3 @>

查看F# PowerPackUnquoteFoq OSS 项目或此snippet 以获得更完整的实现。

要编译 F# 代码引用,您可以使用 define a dynamic method 使用 Reflection.Emit

open System.Reflection.Emit

let rec generate (il:ILGenerator) = function
    | Value(v,t) when t = typeof<int> ->
        il.Emit(OpCodes.Ldc_I4, v :?> int)
    | Call(None,mi,args) -> 
        generateAll il args
        il.EmitCall(OpCodes.Call, mi, null)
    | arg -> raise <| System.NotSupportedException(arg.ToString())
and generateAll il args = for arg in args do generate il arg

type Marker = interface end

let compile quotation =
    let f = DynamicMethod("f", typeof<int>, [||], typeof<Marker>.Module)
    let il = f.GetILGenerator()
    quotation |> generate il
    il.Emit(OpCodes.Ret)
    fun () -> f.Invoke(null,[||]) :?> int

let f = compile <@ 1 + 3 @>
let x = f ()

要再次编译为程序集,请使用 Reflection.Emit 生成具有方法的类型:

open System
open System.Reflection

let createAssembly quotation =
    let name = "GeneratedAssembly"
    let domain = AppDomain.CurrentDomain
    let assembly = domain.DefineDynamicAssembly(AssemblyName(name), AssemblyBuilderAccess.RunAndSave)
    let dm = assembly.DefineDynamicModule(name+".dll")
    let t = dm.DefineType("Type", TypeAttributes.Public ||| TypeAttributes.Class)
    let mb = t.DefineMethod("f", MethodAttributes.Public, typeof<int>, [||])
    let il = mb.GetILGenerator()
    quotation |> generate il
    il.Emit(OpCodes.Ret)
    assembly.Save("GeneratedAssembly.dll")

createAssembly <@ 1 + 1 @>

请参阅Fil 项目(F# 到 IL)以获得更完整的实现。

【讨论】:

  • github.com/eiriktsarpalis/QuotationCompiler 的实现避免了需要使用反射来编译代码报价。此实现似乎大量使用 Microsoft.FSharp.Compiler 命名空间,这可能以前无法访问...
  • @Sam 好点 Eirik 的报价编译器可能是现在要走的路
猜你喜欢
  • 2011-09-08
  • 2011-08-05
  • 2010-09-07
  • 2017-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-16
  • 1970-01-01
相关资源
最近更新 更多