ハードウェアからサーバ・アプリまでワンストップで開発

Javascriptのeval(2)

ogochan

前回のエントリで「とりあえず現段階としてはこれで良いことに」とか言ってたんですが、使っているうちにいろいろ不満があったので何とかならないかと、もうちょっと調べてみました。

どうやらちゃんと動くものが作れたので、メモとして。

前回同様、evalについての問題そのものはここでは触れません。てか、ブラウザでREPLなんてものを作る時点で、多少安全にしようとしたところで、そんなに意味があるとも思えません。

今のところ方法が2つあります。

safe-evalを使う

1つは実は前回既に自分で答えを書いてしまっているのですが、「safe-evalを使う」ということです。ただ、前回も書いたように、これはNode.jsのvm moduleを使いますので、ブラウザでは動きません。ブラウザにはvm moduleは存在しませんから。

ということで諦めていたのですが、よく考えてみるとbrowserifyを使うという手がありましたね。vm moduleはNode.jsの深いところに関係があるものなので、勝手にないものだと思ってたんですが、vm moduleもありました。

vm-browserify

これを使えば、多分safe-evalも動くでしょう(試してない)。

もっとも、safe-evalにしろvm moduleにしろ「巨大なブラックボックス」ですし、やっていることが大袈裟なので、もっと他に方法がないものかと考えます。

evalをhackする

この前諦めてからも、「要するにスコープだけの問題だよなぁ」ということでいろいろ書いては諦めを繰り返していたのですが、stackoverflowにエントリがあるのを発見しました。

Context-preserving eval

まさにこれです。つまり、

var __EVAL = s => eval(`void (__EVAL = ${__EVAL.toString()}); ${s}`);

function evaluate(expr) {
    try {
        const result = __EVAL(expr);
        console.log(expr, '===>', result)
    } catch(err) {
        console.log(expr, 'ERROR:', err.message)
    }
}

こんな感じでいけるわけです。

何が起きるかはコードを見るとわかりますが、evalマトリョーシカのようになって行くわけです。並べることが出来ないので、内側に内側にevalを入れて行く、内側はスコープの中なので... という感じですね。

実際に動かしてみると、

という感じでちゃんと動きました。

この前から画面の中身がちょっと変わっていることについては、またエントリを書きます。

既に書いてるように、実際にコードを書いて試したのは後の方だけです。前のやり方でも多分動くだろうと思うのですが、そこで書いたように

ブラックボックスは小さい方がいい

という考えがあるので、後の方がちゃんと動いたので満足したわけです。同じ理由で、Starboardのevalのロジック(TypeScriptで書いてあります)とかもあるのですが、あれもbabelという巨大なブラックボックスがあるので、あまり深入りしていません。まぁ何にしろ動いて満足です。