eval - 如何使函数可用于 ClojureScript 的 eval?
问题描述
在Dmitri Sotnikov 的这篇博客文章中,提供了一个函数eval-str
来运行包含 ClojureScript 的字符串:
(defn eval-str [s]
(eval (empty-state)
(read-string s)
{:eval js-eval
:source-map true
:context :expr}
(fn [result] result)))
如果我有一些x
我希望能够从 eval 字符串内部调用的函数,我该怎么做?
解决方案
答案有两个部分,假设x
是一个与 ClojureScript 函数关联的 var:
- 的编译器分析元数据
x
需要以作为第一个参数传递给cljs.js/eval
. 这样,例如,在编译期间,诸如 arity 之类的东西x
是已知的。 - 相关函数的 JavaScript 实现
x
需要存在于 JavaScript 运行时中。(如果在调用过程中实际调用cljs.js/eval
了函数,而不仅仅是引用,则尤其如此。)
如果x
是一个核心函数(例如 var #'cljs.core/map
),那么这两个条件都会自动满足。特别是,元数据将在cljs.js/empty-state
被调用时生成(假设:dump-core
是true
),并且核心函数的实现已经加载到 JavaScript 运行时中。
但是,假设x
您希望在自托管环境中编译一个全新的功能。“诀窍”是设置和重用编译器状态:例如将结果(cljs.js.empty-state)
放入 var,并将其传递给每个cljs.js/eval
调用。如果您这样做,并且其中一个cljs.js/eval
调用涉及编译defn
for x
,那么编译器状态将被修改(它实际上是一个原子),结果是编译器元数据 forx
将被置于状态,以及当然,在x
JavaScript 环境中设置的 JavaScript 实现(通过评估为 生成的 JavaScript defn
)。
另一方面,如果x
一个函数是您的“环境”ClojureScript 环境的一部分(例如,通过 JVM ClojureScript 编译器预编译,但在 JavaScript 运行时中仍然可用),那么它会以某种方式由您决定安排将编译器分析元数据x
转换为传递给的状态cljs.js/eval
。如果您查看基于 JVM 的编译器的输出,您将看到<ns-name>.cache.json
包含此类元数据的文件。查看这些文件中的数据,您可以确定其结构;有了它,您可以看到如何将所需的信息交换到下的编译器状态[:cljs.analyzer/namespaces <ns-name>]
。该cljs.js/load-analysis-cache!
函数作为此用例的帮助程序存在,一个独立的示例位于https://stackoverflow.com/a/51575204/4284484