フリーの ECMAScript インタプリタ。
Java で記述された JavaScript インタプリタ。 |
Java ライブラリ (パッケージ FESI.jslib) |
ライブラリは非常に簡素で、インタプリタの基本的な機能へのアクセスのみを 提供します。しかし、インタプリタの内部構造からははっきり独立しているので、 FESI の変更でプログラムが影響を受けることはありません。
ECMAScript プログラムから Java オブジェクトにアクセスする方法については、 JavaAccess のページを参照して下さい。
API の機能は下記のクラスに実装されています。
ECMAScript オブジェクトの汎用の型で、メンバプロパティへのアクセスや 文字列の評価、ECMAScript 関数の呼び出しや インタプリタのグローバルオブジェクトの検索ができます。 | |
インタプリタを表すグローバルオブジェクトです。 新しいオブジェクトや配列の生々、文字列の評価、グローバル環境の関数呼び出しに 使われます。 | |
読込可能な拡張が実装しなければならないインタフェースです。 | |
ECMAScript から呼び出せる関数が実装しなければならないインタフェースです。 | |
新しい関数を作成するために使えるアダプタです。 | |
静的な評価機構の生成とバージョン情報を取得するためのルーチンを まとめたものです。 | |
すべての例外をラップする例外です。 |
example ディレクトリのソースファイルはより完全なもので、 いくつかの拡張を初期化しています。import java.io.*; import FESI.jslib.*; public class SimpleIntrp { public static void main(String args[]) { // インタプリタの生成 JSGlobalObject global = null; try { global = JSUtil.makeEvaluator(); } catch (JSException e) { System.err.println(e); System.exit(1); } DataInputStream ins = new DataInputStream(System.in); String input = null; // 読込、評価、表示の繰り返し while (true) { System.out.print("> "); System.out.flush(); try { input = ins.readLine(); } catch (IOException e) { System.exit(0); } if (input == null) break; try { Object result = global.eval(input); if (result!=null) System.out.println(result.toString()); } catch (JSException e) { System.out.println(e.getMessage()); if (DEBUG) e.printStackTrace(); Exception oe = e.getOriginalException(); if (oe!=null) { oe.printStackTrace(); }; System.out.println(e.getMessage()); System.out.println(e.getMessage()); } } // 繰り返しここまで } }
関数 makeObjectWrapper はオブジェクトがすでに JSObject である場合には なにも影響がありません。 この関数は makeBeanWrapper で設定された Bean のハンドリングフラグを 考慮します。JButton jb = new JButton(); JSObject jsjb = global.makeObjectWrapper(jb); jsjb.eval("this.setLabel(Date())");
ここで o はなんらかの JSObject です。そして、 オブジェクト db (オブジェクト o の dialog プロパティ) は Java オブジェクトとしてよりも Java Bean として扱われ、Bean として宣言された プロパティやメソッドのみが呼び出せるようになります。 makeBeanWrapper は JSObject ではなく一般的なオブジェクトを返します。 (eval を呼び出すために) Bean を JSObject として使いたい場合は、 makeBeanWrapper の結果を makeObjectWrapper でラップしなければ なりません。dialogBean db = new DialogBean(); o.setMember("dialog", global.makeBeanWrapper(db));
JavaAccess 拡張の loadExtension 関数 も参照して下さい。String[] extensions = new String[] {"FESI.Extensions.BasicIO", "FESI.Extensions.JavaAccess"}; JSObject global = JSObject.makeEvaluator(extensions);
ひとつのプログラムで複数のインタプリタを生成することもできます。 インタプリタへのアクセスは同期化されています。 それぞれの JSObject が ひとつずつのインタプリタに所属しています。// インタプリタの生成 JSGlobalObject global = JSUtil.makeEvaluator(); // 関数の宣言 global.setMember("loadPage", new JSFunctionAdapter() { public Object doCall(JSObject thisObject, Object args[]) throws JSException { if (args.length == 0) throw new JSException("loadPage: 引数 (ページ名) がありません"); return loadPage(args[0].toString()); } }); // ユーザーのマクロを実行 String userCommand = ... global.eval(userCommand); // 関数本体を実装するルーチン (public でなくてもかまいません!) private Object loadPage(String pageName) { // ページ属性を保持する ECMAScript オブジェクトを生成 JSObject page = global.makeJSObject(); page.setMemeber("name", pageName); page.setMember("data", loadPageFromFile("pageName"); }
とすれば予想通りに動きます。if (tag == "H") return 1; else if (tag == "A") return 2; else return -1;
さらに、引数の名前と値を指定して、その場で関数を作ることもできます。 これは、イベントハンドラのように見えなければならないコードで便利です。 関数 evalAsFunction に 引数として名前と値を与えるとこの目的を果たせます。例えば、 イベントハンドラは次のようになります。
ここで、スクリプトは、例えば次のようになります。String names[] = {"event", "timeStamp"}; Object values [] = {event, new Date(event.getTimeStamp)}; target.evalAsFunction(script, names, values);
すると、 断片的なコードがあたかも event および timeStamp という 2 つの 引数を取る関数であるかのように実行されます。var _c = event.getChar(); if (_c == "t") alert(timeStamp + " に t を受け取りました");