複数の隔離されたスクリプト環境を組み込む

隔離されたスクリプト環境

「スクリプト環境が隔離されている」とは、次の条件を満たす場合を指します。

たとえば、PnutsのWebスクリプティングの環境では、サーブレット・スクリプトごとに、スクリプト実行環境が隔離されています。あるサーブレット・スクリプトで定義した名前(変数、関数、モジュール)は他のサーブレット・スクリプトに影響を与えません。

基本的な手順

複数の隔離されたスクリプト環境を組み込むには、ルート・パッケージContextがスクリプト間で共有されないようにします。

スクリプト環境の単位

まず、次のようにグローバル・パッケージとは異なるルート・パッケージを作ります。

Package myRootPackage = new Package("root", null);

コンストラクタの最初のパラメータはパッケージ名で、任意の名前を付けることができます。2番目のパラメータには、親パッケージがないことを示すためにnullを指定します。

次に、別々のContextオブジェクトを作成します。そして、コンストラクタContext(Package)、またはContext.setCurrentPackage()メソッドで、カレントパッケージを指定します。

Context myContext = new Context(myRootPackage);
または、
Context myContext = new Context();
myContext.setCurrentPackage(myRootPackage);

次のプログラムは、2つの隔離されたスクリプト環境を作る例です。

Package root_1 = new Package("root_1", null);
Context context_1 = new Context(root_1);
..
Package root_2 = new Package("root_2", null);
Context context_2 = new Context(root_2);
...
Pnuts.load(script_1, context_1);
Pnuts.load(script_2, context_2);

モジュールを共有する

複数のスクリプト環境を互いに隔離したい場合でも、標準的なモジュールを共有したい場合があります。

これを実現する方法として、以下のような方法があります。

1. 各Context/Packageを初期化する

これは、もっとも単純なアプローチです。各Contextオブジェクトに対してContext.usePackage()を呼び出し、共有モジュールを各スクリプト環境に登録します。

static void initialize(Context context){
   context.usePackage(standard_module);
   ...
}
...
Package root_1 = new Package("root_1", null);
Context context_1 = new Context(root_1);
initialize(context_1);
..
Package root_2 = new Package("root_2", null);
Context context_2 = new Context(root_2);
initialize(context_2);
...
Pnuts.load(script_1, context_1);
Pnuts.load(script_2, context_2);

2. Packageの複製

ルート・パッケージに対してPackage.clone()メソッドを使うと、パッケージ・ツリーを複製することができます。パッケージ・ツリーを複製すると、もとのパッケージ・ツリーの記号表がすべて複製されます(ただし、変数の値は共有されます)。

複製したパッケージ・ツリーで、変数やモジュールを定義しても、もとのパッケージ・ツリーに影響を与えることはありません。

また、複製を作った後で、もとのパッケージ・ツリーに対して新しい変数やモジュールを定義しても、複製されたパッケージ・ツリーに影響を与えることはありません。

Package root = new Package("root", null);
Context context = new Context(root);
context.usePackage("shared");
..
Package root_1 = (Package)root.clone();
Context context_1 = new Context(context);
context_1.setCurrentPackage(root_1);
...
Package root_2 = (Package)root.clone();
Context context_2 = new Context(context);
context_2.setCurrentPackage(root_2);
...
Pnuts.load(script_1, context_1);
Pnuts.load(script_2, context_2);

この例では、Packageの複製を作ると同時に、Context(Context)コンストラクタを使ってContextの複製を作っています。各スクリプトで使うすべてのContextに対して同じ初期設定をしたい場合、一度状態を設定したContextをテンプレートとして複製を作成すると便利です。

Packageの複製

3. 名前空間の合成

このアプローチでは、あるパッケージ・ツリーで定義される変数、関数、モジュールが、pnuts.ext.CompositePackageクラスを使って合成されたパッケージ・ツリーから参照されます。

一方、CompositePackageを含むパッケージ・ツリーで定義される変数は、ルート・パッケージを含むパッケージ・ツリーからは参照されません。

一つ前の方法(Packageの複製)とは異なり、合成されたパッケージ・ツリーは元のパッケージ・ツリーで新たに定義された名前にアクセスできます。

Package pkg = new Package("root", null);
Context context = new Context(pkg);
context.setImplementation(new CompilerPnutsImpl());
context.usePackage("module1");
context.usePackage("module2");
...

Package root_1 = new CompositePackage(pkg);  // パッケージ・ツリーの合成
Context context_1 = new Context(context); // Contextの複製
context_1.setCurrentPackage(root_1);

Package root_2 = new CompositePackage(pkg);  // パッケージ・ツリーの合成
Context context_2 = new Context(context); // Contextの複製
context_2.setCurrentPackage(root_2);
...
Pnuts.load(script_1, context_1);
Pnuts.load(script_2, context_2);

CompositePackageとPackageの関係