フリーの ECMAScript インタプリタ。
Java で記述された JavaScript インタプリタ。

 
Java ライブラリ (パッケージ FESI.jslib)

機能

このライブラリは、ECMAScript インタプリタの機能を拡張して Java プログラムから インタプリタを生成したりスクリプトを評価したり ECMAScript オブジェクトのプロパティを 取得・設定したりできるようにするために使うものです。 Netscape の JSObject および JSExeption クラスと だいたい互換なので、共用コードの開発が簡単になっています。

ライブラリは非常に簡素で、インタプリタの基本的な機能へのアクセスのみを 提供します。しかし、インタプリタの内部構造からははっきり独立しているので、 FESI の変更でプログラムが影響を受けることはありません。

ECMAScript プログラムから Java オブジェクトにアクセスする方法については、 JavaAccess のページを参照して下さい。

API ドキュメンテーション

API ドキュメンテーションは、 jslib パッケージ にあります。

API の機能は下記のクラスに実装されています。
 

ECMAScript オブジェクトの汎用の型で、メンバプロパティへのアクセスや 文字列の評価、ECMAScript 関数の呼び出しや インタプリタのグローバルオブジェクトの検索ができます。
インタプリタを表すグローバルオブジェクトです。 新しいオブジェクトや配列の生々、文字列の評価、グローバル環境の関数呼び出しに 使われます。
読込可能な拡張が実装しなければならないインタフェースです。
ECMAScript から呼び出せる関数が実装しなければならないインタフェースです。
新しい関数を作成するために使えるアダプタです。
静的な評価機構の生成とバージョン情報を取得するためのルーチンを まとめたものです。
すべての例外をラップする例外です。

 

インタプリタの生成

最も簡単な例 (下記) が examples/jslib ディレクトリ の SimpleIntrp.java にあります。 このパターンに従うだけでインタプリタを生成することができます。
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());
            }
        } // 繰り返しここまで
    }
}
example ディレクトリのソースファイルはより完全なもので、 いくつかの拡張を初期化しています。
 

Java オブジェクトを ECMAScript オブジェクトとして使う

Java オブジェクトを ECMAScript オブジェクトとみなすようにして それに対して JSObject の関数 (eval など) を使うと便利なことがあります。 これは次のようにして行なうことができます。
JButton jb = new JButton();
JSObject jsjb = global.makeObjectWrapper(jb);
jsjb.eval("this.setLabel(Date())");
関数 makeObjectWrapper はオブジェクトがすでに JSObject である場合には なにも影響がありません。 この関数は makeBeanWrapper で設定された Bean のハンドリングフラグを 考慮します。
重要な制限事項として、このようなオブジェクトは完全なインタプリタであるとは みなせません。とくに、ECMAScript のスクリプトから Java オブジェクトに 新しいプロパティを生成することはできません。
 

ECMAScript における Java オブジェクトと Java Bean

プロパティの値を (ECMAScript に変換できない) Java オブジェクトにすると、 Package システムを通じて生成されたオブジェクトのように扱うことが できるようになります。これには Bean プロパティの 非 public なフィールドへの アクセスも含まれます (詳しくは、 JavaAccess 拡張を参照して下さい)。 Bean として設定されたオブジェクト (Bean のプロパティやメソッドへのアクセスのみ 可能) が使いたい場合は、ルーチン JSGlobalObject.makeBeanWrapper を 使います。例えば、Java で次のようにします。
dialogBean db = new DialogBean();

o.setMember("dialog", global.makeBeanWrapper(db));
ここで o はなんらかの JSObject です。そして、 オブジェクト db (オブジェクト odialog プロパティ) は Java オブジェクトとしてよりも Java Bean として扱われ、Bean として宣言された プロパティやメソッドのみが呼び出せるようになります。 makeBeanWrapper は JSObject ではなく一般的なオブジェクトを返します。 (eval を呼び出すために) Bean を JSObject として使いたい場合は、 makeBeanWrapper の結果を makeObjectWrapper でラップしなければ なりません。
 

拡張の読込みを指定する

makeEvaluator の 必須ではない第 2 引数で、String の配列を通して拡張を指定することができます。 例を挙げます。
String[] extensions = new String[] {"FESI.Extensions.BasicIO",
                                    "FESI.Extensions.JavaAccess"};
JSObject global = JSObject.makeEvaluator(extensions);
JavaAccess 拡張の loadExtension 関数 も参照して下さい。
 

拡張されたスクリプトホストの生成

スクリプトホストはインタプリタを拡張したものである必要はありません (実際、 多くの場合はそうなっていません)。ディレクトリ examples/jslib の 例である SampleFunc.java は メインプログラムが ECMAScript を拡張して使う方法を示したものです。 さらに詳しくは 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");
}
ひとつのプログラムで複数のインタプリタを生成することもできます。 インタプリタへのアクセスは同期化されています。 それぞれの JSObject が ひとつずつのインタプリタに所属しています。

読込可能な拡張を動的に生成する

FesiPop の例では、インタプリタや スクリプトホストとなるプログラムに読み込める拡張の生成方法を示したものです。 拡張は JSExtension インタフェース を実装することで作られ、 標準の JSObject の機能を使います。 拡張を使うと、Java パッケージを ECMAScript ユーザーの特定の要求に適合させたり 特定のエラーハンドリングを行なうことができます。
 

コードを関数として使う

ときによっては、スクリプトの断片的なコードをメインプログラムではなく 無名で引数なしの関数として実装できると便利です。すると、 例えば条件文で return 文を置かせられるようになり、 コードが単純になります。この目標を達成する方法は、 インタプリタの呼び出しに、 eval で なく evalAsFunction を 使うだけです。コードは
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);
ここで、スクリプトは、例えば次のようになります。
var _c = event.getChar();
if (_c == "t") alert(timeStamp + " に t を受け取りました");
すると、 断片的なコードがあたかも event および timeStamp という 2 つの 引数を取る関数であるかのように実行されます。
 
 
 


メインページに戻る

最終更新日 (原文): 1999 年 3 月 13 日
(翻訳): 1999 年 4 月 1 日