ogochan
前回のエントリで「とりあえず現段階としてはこれで良いことに」とか言ってたんですが、使っているうちにいろいろ不満があったので何とかならないかと、もうちょっと調べてみました。
どうやらちゃんと動くものが作れたので、メモとして。
前回同様、eval
についての問題そのものはここでは触れません。てか、ブラウザでREPLなんてものを作る時点で、多少安全にしようとしたところで、そんなに意味があるとも思えません。
今のところ方法が2つあります。
safe-evalを使う
1つは実は前回既に自分で答えを書いてしまっているのですが、「safe-evalを使う」ということです。ただ、前回も書いたように、これはNode.jsのvm moduleを使いますので、ブラウザでは動きません。ブラウザにはvm moduleは存在しませんから。
ということで諦めていたのですが、よく考えてみるとbrowserifyを使うという手がありましたね。vm moduleはNode.jsの深いところに関係があるものなので、勝手にないものだと思ってたんですが、vm moduleもありました。
これを使えば、多分safe-evalも動くでしょう(試してない)。
もっとも、safe-evalにしろvm moduleにしろ「巨大なブラックボックス」ですし、やっていることが大袈裟なので、もっと他に方法がないものかと考えます。
evalをhackする
この前諦めてからも、「要するにスコープだけの問題だよなぁ」ということでいろいろ書いては諦めを繰り返していたのですが、stackoverflowにエントリがあるのを発見しました。
まさにこれです。つまり、
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という巨大なブラックボックスがあるので、あまり深入りしていません。まぁ何にしろ動いて満足です。