Multiple Isolated Scripting Environments

Isolated Scripting Envinroments

The following conditions are required to realize isolated scripting envinronments.

For example, servlet script is executed in an isolated scripting environment. Any non-local names (variables, functions, and modules) defined by a servlet script do not affect other servlet scripts.

Basic Steps

To build multiple isolated scripting environments, make sure that root package and Context objects are not shared by the isolated scripting environments.

Elements of a scripting environment

First, create a root package that is not the global package.

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

The constructor takes the package name as the first parameter, which can be an arbitrary string. The second parameter must be null in this case.

Next, create a separate Context object and set the current package with the constructor Context(Package) or Context.setCurrentPackage() method.

Context myContext = new Context(myRootPackage);
or
Context myContext = new Context();
myContext.setCurrentPackage(myRootPackage);

An Example

The following code shows how to build two isolated scripting environments. The non-local names (variables, functions, and modules) defined in script_1 does not affect script_2, and vice versa.

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);

Shared Modules

Although scripting environments should be able to isolate the name space used by user scripts, they may want to share a certain module as their "standard" module.

There are several approaches to realize that.

1. Initializing Each Context/Package

This is the simplest approach. Context.usePackage() is called for each Context object, so that the shared modules are registered in each scripting environment.


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. Copying Package

package tree can be copied calling Package.clone() on the root package.

When a package tree is copied, all symbol tables of the original package tree are duplicated. The values in the symbol tables are not duplicated (see the diagram below). Each module initialization script is executed at most once.

Operations on a copied package tree, such as registering modules, never affect the original package tree. Conversely, once a copy of a package tree is made, operations on the original package tree never affect the copied package tree.

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

This example makes a copy of a Context object by Context(Context) constructor. This technique is usefull when all Contexts used by isolated scripting environments should have the same initial settings

Package clones

3. Composing Name Spaces

In this approach, the names (variables, functions, and modules) defined in a certain package tree are also visible from composed package tree created by pnuts.ext.CompositePackage.

On the other hand, names defined in the package tree that contains CompositePackage are not visible from the package tree that contains base package.

Unlike the previous approach, changes to the base package tree are visible from composed package trees.

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

Package root_1 = new CompositePackage(base);    // Package composition
Context context_1 = new Context(context); // copies context 
context_1.setCurrentPackage(root_1);

Package root_2 = new CompositePackage(base);    // Package composition
Context context_2 = new Context(context); // copies context
context_2.setCurrentPackage(root_2);
..
Pnuts.load(script_1, context_1);
Pnuts.load(script_2, context_2);

Relation between CompositePackage and Package