Eval

evalはいくつかのプログラミング言語が持つ、文字列を式として評価する関数、または複数の文をプログラム中のあるコンテキストで実行するサブルーチンである。

evalの類の機能はコンパイラ言語よりもインタプリタ言語でより一般的である。なぜならコンパイラ言語でこのような機能を実現するには、プログラム自体に言語処理系や実行時情報を埋め込む必要があるからである。evalに近い機能を実現しているコンパイラ言語も存在する。

1. セキュリティ上のリスク
--------------

信頼されないソースからのデータをevalするときには特に注意が必要である。例としてインターネット上からデータを得る get_data 関数を考える。次の擬似コードのようなプログラムは潜在的に危険である。 data = get_data foo = eval 攻撃者がこのプログラムに例えば "delete_system_files" という文字列を与えることができると、delete_system_files 関数が実行されてしまい、重要なファイルが消されてしまうかもしれない。これを防ぐためには、evalされる文字列はすべてエスケープしたり、潜在的に危険な機能を利用できないようにして実行するなどの対策が必要となる。プログラミング言語によっては、外部から入力されたデータを「汚染されている」として印をつけるものもある。

2. 適切な使用
--------

evalは非常に強力なため、経験の浅いプログラマは何でもevalを使って済ませてしまうことがある。たいてい、そのような場合には専用のより良い選択肢が存在し、コードのパースにかかる時間が節約できる。

例えば、evalは簡易テンプレートエンジンとして使われることがある。PHPでの例を示す。 $name = 'John Doe'; $greeting = 'Hello'; $template = '"$greeting, $name! How can I help you today?"'; print eval この例は期待した動作をするが、前述のようにセキュリティ上の問題を抱えており、他の方法よりもはるかに遅い。より高速で安全な方法は単に "$name" という文字列を $name の値で置換することである。

eval は表計算ソフトなどの数式を評価する必要のあるアプリケーションで使われることがある。これは数式のパーサを自作するよりも手軽だが、自作や既存の専用のパーサを利用するほうがより良い。前述の問題点に加え、言語組み込みのevalはアプリケーション用にカスタマイズできないからである。

おそらく、evalの最も優れた使い道は処理系のブートストラップや、言語の対話的な実行環境でユーザが書いたプログラムを実行することであろう。

3. 実装
-----

インタプリタ言語ではevalは通常のコードと同じインタプリタで実装されるのがほとんどである。

コンパイラ言語ではevalを実装するために通常のコンパイラをプログラムに埋め込むこともある。また特別のインタプリタを使うこともあり、その場合はコードの重複が問題となる。

4. 実例
-----

4.1. JavaScript, ActionScript
-----------------------------

JavaScriptやActionScriptにおいては、evalは式の評価器と文の実行器のハイブリッドのような存在である。evalは最後に評価された式の値を返し、最後のセミコロンは省くことができる。

式評価器としての例: foo = 2; alert);

文実行器としての例: foo = 2; eval;');

JavaScriptでのevalの使用の一例はAjaxなどにおけるJSONのパースである。しかし、現在は多くのブラウザでより特化したJSON.parseが定義されている。

ActionScriptではevalを任意の式を評価するために使うことはできない。Flash 8のドキュメントによれば、その使用は「変数、プロパティ、オブジェクト、ムービークリップの名前」を表す式に限られ、「パラメータはStringまたはオブジェクトのインスタンスへの直接の参照のどちらでもよい」。

4.2. en
-------

enはenが最初に登場した言語である。enの実装によって、最初のenインタプリタが現れたのである。それ以前は、enの式はコンパイルされていた。しかし一度evalが実装されると、それは単純な入力・評価・出力のループ  の一部として使われるようになり、最初のenインタプリタの基礎を形作った。enの後のバージョンのenはコンパイラとしても実装されている。

4.3. Perl
---------

Perlのevalは、文字列をプログラムとして解釈するほか、例外処理機構としても機能する。コードの塊をテストし、必要ならそれに関して警告することができる。例えば、除算において実行時エラーが発生すると$@で警告を出力することができる。

0による除算の例外を回避するeval { $answer = $x / $y; }; warn $@ if $@;<WtXmlEndTag />

4.4. PHP
--------

PHPでのevalは、ほぼ、文字列中のコードをまるで eval 呼び出しの代わりにファイル中に記述されているかのように実行する。例外は、エラーは eval から復帰した時点で報告され、またreturn文で eval の戻り値を返すことができる点である。

echoを使った例: <?php $foo = "Hello, world! "; eval; ?>

値を返す例: <?php $foo = "Goodbye, world! "; echo eval; ?>

4.5. PostScript
---------------

PostScriptのexec演算子はオペランドを1つ取り、それが単純なリテラルならスタックにプッシュし返す。PostScriptの式を含む文字列が来た場合は、その文字列をインタプリタによって実行可能な形式に変換する。例えば、  =) cvx exec は PostScript の式である  =を実行可能な型を持つように変換し、そして実行する。

PostScriptでは run 演算子も似た機能を持つが、代わりにインタプリタそのものがファイル中のPostScriptの式を評価する。

4.6. Python
-----------

Pythonでの eval 関数は単一の式を評価する。文を実行することはできないが、文を実行する関数を実行することはできる。exec 文は文を実行することができる。

eval の例: >>> x = 1 >>> eval 2 >>> eval 1

exec の例: >>> x = 1 >>> y = 1 >>> exec "x += 1; y -= 1" >>> x 2 >>> y 0

4.7. ColdFusion
---------------

ColdFusionの evaluate 関数は文字列で与えられた式を実行時に評価することができる。

これは読み込む変数をプログラム的に選択するときなどに、特に便利である。

4.8. Ruby
---------

Rubyには式を評価するコンテキストごとに3種類のevalが存在する。eval はその場またはProcやBindingオブジェクト、instance_eval はインスタンス、module_evalはモジュールかクラス、それぞれのコンテキストで評価を行う。なお、評価コンテキスト変更のためにinstance_evalやmodule_evalを使う場合、文字列でなくブロックを引数とすることができる。

evalの例: def foo x = 1 binding end

eval #=> 2<WtXmlEndTag />

instance_eval と module_eval の例: class Bar @@cvar = 10 #クラス変数 def initialize @ivar = 5 #インスタンス変数 end end

bar = Bar.new bar.instance_eval #=> 5 Bar.class_eval #=> 10<WtXmlEndTag />

5. 脚注
-----

6. 外部リンク
--------

[1]