如何在ClojureScript中运行eval并访问调用eval的命名空间?

int*_*tar 2 namespaces eval clojurescript

我有一个功能库,我想让用户在浏览器中玩.

所以我想建立一个这样的情况:

我正在用figwheel和devcards开发.

在主要的core.cljs中require,我的库中有各种函数,因此它们都在范围内.

现在我想让用户输入一些调用该库的代码.

我看到如何使用eval运行该代码,但是我无法看到如何使我的库函数对于被唤醒的代码可见.

而且我对我所看到的大部分文档感到困惑(例如,我如何使函数可用于ClojureScript的eval?)

可能吗?如果是这样,有没有人有一个简单的例子呢?

干杯

菲尔

Mik*_*kes 6

是的,可以提供对评估代码使用的环境/预编译库的访问.

首先,您必须确保库中的函数在JavaScript运行时中可用.换句话说,避免:advanced优化,因为这将消除在编译时未调用的函数(DCE).自托管ClojureScript兼容:simple.

其次,您需要将分析元数据提供给将在浏览器中运行的自托管编译器(使用cljs.js/load-analysis-cache!或使用可选参数cljs.js/empty-state).

下面(以及https://github.com/mfikes/ambient)介绍了如何执行此操作的最小项目:

项目代码

src/main/core.cljs:

(ns main.core
  (:require-macros [main.core :refer [analyzer-state]])
  (:require [cljs.js]
            [library.core]))

(def state (cljs.js/empty-state))

(defn evaluate [source cb]
  (cljs.js/eval-str state source nil {:eval cljs.js/js-eval :context :expr} cb))

(defn load-library-analysis-cache! []
  (cljs.js/load-analysis-cache! state 'library.core (analyzer-state 'library.core))
  nil)
Run Code Online (Sandbox Code Playgroud)

src/main.core.clj:

(ns main.core
  (:require [cljs.env :as env]))

(defmacro analyzer-state [[_ ns-sym]]
  `'~(get-in @env/*compiler* [:cljs.analyzer/namespaces ns-sym]))
Run Code Online (Sandbox Code Playgroud)

src/library/core.cljs:

(ns library.core)

(defn my-inc [x]
  (inc x))
Run Code Online (Sandbox Code Playgroud)

用法

我们有一个main.core提供evaluate函数的命名空间,这个例子将展示如何在环境/预编译的library.core命名空间中调用函数.

首先,启动浏览器REPL via

clj -m cljs.main
Run Code Online (Sandbox Code Playgroud)

在REPL中,通过评估加载我们的主命名空间

(require 'main.core)
Run Code Online (Sandbox Code Playgroud)

测试我们可以评估一些代码:

(main.core/evaluate "(+ 2 3)" prn)
Run Code Online (Sandbox Code Playgroud)

这应该打印

{:ns cljs.user, :value 5}
Run Code Online (Sandbox Code Playgroud)

现在,由于main.core需要library.core,我们可以调用该命名空间中的函数.在REPL评估这将产生11:

(library.core/my-inc 10)
Run Code Online (Sandbox Code Playgroud)

现在,让我们尝试使用自托管ClojureScript中的"环境"功能:

(main.core/evaluate "(library.core/my-inc 10)" prn)
Run Code Online (Sandbox Code Playgroud)

您将看到以下内容

WARNING: No such namespace: library.core, could not locate library/core.cljs, library/core.cljc, or JavaScript source providing "library.core" at line 1
WARNING: Use of undeclared Var library.core/my-inc at line 1
{:ns cljs.user, :value 11}
Run Code Online (Sandbox Code Playgroud)

简而言之,即使library.core.my_inc在JavaScript环境中可用,并且确实可以调用,产生正确的答案,您会收到来自自托管编译器的警告,它对此命名空间一无所知.

这是因为编译器分析元数据不在main.core/state原子中.(自托管编译器有自己的分析状态,保存在JavaScript环境中的那个原子中,与JVM编译器分析状态分开,通过Clojure在Java环境中保存.)

注意:如果我们改为library.core使用自托管编译器编译的源代码(通过使用main.core/evaluateeval "(require 'library.core)",并正确定义cljs.js/*load-fn*可以检索此源的东西,那么事情会很好,并且编译器分析元数据main.core/state.但是这样示例是关于调用环境/预编译函数library.core.

我们可以通过使用cljs.js/load-analysis-cache!加载与library.core命名空间关联的分析缓存来解决此问题.

此示例代码通过使用从基于JVM的编译器捕获分析缓存的宏直接将此分析缓存嵌入到代码中.您可以通过任何所需的机制将此分析缓存传输到浏览器; 这只是说明了一种简单地将其直接嵌入到运输代码中的方法(它只是数据).

继续评估以下内容,只是为了查看该命名空间的分析缓存是什么样的:

(main.core/analyzer-state 'library.core)
Run Code Online (Sandbox Code Playgroud)

如果你打电话

(main.core/load-library-analysis-cache!)
Run Code Online (Sandbox Code Playgroud)

此分析缓存将被加载以供自托管编译器使用.

现在,如果你评价

(main.core/evaluate "(library.core/my-inc 10)" prn)
Run Code Online (Sandbox Code Playgroud)

你不会看到任何警告,这将被打印:

{:ns cljs.user, :value 11}
Run Code Online (Sandbox Code Playgroud)

此外,由于自托管编译器现在具有分析元数据libraray.core,因此它可以适当地警告例如arity错误

(main.core/evaluate "(library.core/my-inc 10 12)" prn)
Run Code Online (Sandbox Code Playgroud)

将导致打印:

WARNING: Wrong number of args (2) passed to library.core/my-inc at line 1
Run Code Online (Sandbox Code Playgroud)

上面说明了当您没有为命名空间提供分析器缓存以及如何使用它进行修复时会发生什么cljs.js/load-analysis-cache!.如果您知道在启动时总是希望加载缓存,那么您可以使用可选参数cljs.js/empty-state在初始化时加载此缓存:

(defn init-state [state]
  (assoc-in state [:cljs.analyzer/namespaces 'library.core]
    (analyzer-state 'library.core)))

(def state (cljs.js/empty-state init-state))
Run Code Online (Sandbox Code Playgroud)

其他的项目

一些(更复杂的)项目使浏览器中的自托管ClojureScript可以使用库函数: