问题
In this blog post by Dmitri Sotnikov a function eval-str
is provided for running a string containing ClojureScript:
(defn eval-str [s]
(eval (empty-state)
(read-string s)
{:eval js-eval
:source-map true
:context :expr}
(fn [result] result)))
If I have some function x
that I want to be able to call from inside the eval string, how can I do that?
回答1:
There are two parts to the answer, assuming x
is a var associated with a ClojureScript function:
- The compiler analysis metadata for
x
needs to be present in the state passed as the first argument tocljs.js/eval
. This is so that, during compilation, things like the arity ofx
is known, for example. - The JavaScript implementation of the function associated with
x
needs to be present in the JavaScript runtime. (This is especially true if the function is actually called during thecljs.js/eval
call, and not just referenced.)
If x
is a core function (say the var #'cljs.core/map
for example), then both of these conditions is automatically satisfied. In particular, the metadata will be produced when cljs.js/empty-state
is called (assuming :dump-core is true
), and the implementation of the core functions will have already been loaded into the JavaScript runtime.
But, let's say x
is a wholly new function that you wish to have compiled in the self-hosted environment. The “trick” is to set up and reuse compiler state: For example put the result of (cljs.js.empty-state)
into a var, and pass it to every cljs.js/eval
call. If you do that, and one of the cljs.js/eval
calls involves compiling a defn
for x
, then the compiler state will be modified (it is actually an atom), with the result being that the compiler metadata for x
will be put in the state, along with, of course, the JavaScript implementation for x
being set within the JavaScript environment (by virtue of evaluating the JavaScript produced for the defn
).
If, on the other hand, x
is a function that is part of your “ambient” ClojureScript environment (say, pre-compiled via the JVM ClojureScript compiler, but nevertheless available in the JavaScript runtime), then it will be up to you to somehow to arrange to get the compiler analysis metadata for x
into the state passed to cljs.js/eval
. If you look at the output of the JVM-based compiler, you will see <ns-name>.cache.json
files containing such metadata. Take a look at the data that is in these files and you can ascertain its structure; with that you can see how to swap the needed information into the compiler state under [:cljs.analyzer/namespaces <ns-name>]
. The cljs.js/load-analysis-cache!
function exists as a helper for this use case, and a self-contained example is at https://stackoverflow.com/a/51575204/4284484
来源:https://stackoverflow.com/questions/50828008/how-can-i-make-functions-available-to-clojurescripts-eval