FESI language extensions
Author: Jean-MarcLugrin
Description: Describe the FESI EcmaScript interpreter extensions
Keywords: EcmaScript, JavaScript, JScript, Java, extension

| Free EcmaScript Interpreter. A JavaScript interpreter written in Java.

The Java library (package FESI.jslib)

### Capabilities

The library can be used to extend the capabilities of the EcmaScript interpreter, allowing a Java program to create an interpreter, to evaluate scripts, and to set and get properties of EcmaScript objects. It is largely compatible with the Netscape `JSObject` and `JSExeption` classes, simplifying the development of common code.

The library is very simple, and only provide access to basic capabilities of the interpreter. However it is quite independent of the internal structure of the interpreter, so the programs are not impacted by changes of `FESI`.

See the Java Access page on how to access Java objects from an EcmaScript program.

### The API documentation

The API documentation is in `jslib package`.

The API functionality is implemented in:

`JSObject` | The generic type of EcmaScript objects, allowing to access the member properties, evaluate strings, call EcmaScript functions and find the global object of the interpreter.
`JSGlobalObject` | The global object representing the interpreter. Used to create new objects and arrays, and to evaluate strings or call functions in the global environment.
`JSExtension` | The interface that a loadable extension must implement
`JSFunction` | The interface that a function callable by EcmaScript must implement.
`JSFunctionAdapter` | An utility adapter to create new functions.
`JSUtil` | The package containing the static evaluator creation and routine to access version information.
`JSException` | The exception wrapping all exceptions.

### Creating an interpreter

The simplest example (listed below) is in `SimpleIntrp.java` in the directory `examples/jslib`. You can create an interpreter simply following this pattern:

`import java.io.*;`
`import FESI.jslib.*;`
`public class SimpleIntrp {`
`public static void main(String args[]) {`
`// Create the interpreter`
`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;`
`// read eval print loop`
`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());`
`}`
`} // while`
`}`
`}`

The source file in the example directory is more complete, initializing some extensions.

### Java object as an EcmaScript object

It is sometime useful to force a Java object to be considered an EcmaScript object, to use the JSObject functions on it (for example eval). This can be done as follow::

    JButton jb = new JButton();
    JSObject jsjb = global.makeObjectWrapper(jb);
    jsjb.eval("this.setLabel(Date())");

The function `makeObjectWrapper` has no effect if the object is already a JSObject. It will respect the bean handling flag as set by `makeBeanWrapper`.
An important restriction is that such an object cannot be considered a full interpreter. Especially it is not possible to create new properties on a java object from an EcmaScript script.

### Java object and Java beans in EcmaScript

If you set a property value to a Java object (which cannot be converted to an EcmaScript value), then it can be used as any object created via the `Package` system, including the access of bean properties which are not public fields (see the `JavaAccess` extension for details). If you want an object set as a bean (only allowing access to bean properties and method), you can use the routine `JSGlobalObject.makeBeanWrapper`. For example, in java:

    dialogBean db = new DialogBean();

    o.setMember("dialog", global.makeBeanWrapper(db));

Where `o` is some `JSObject`. Then the object `db` (the dialog property of the object `o`) will be handled as a java bean rather than a java object, and only the bean declared properties and methods can be called on it. `makeBeanWrapper` does not return a JSObject, but a general object. If you want to use the bean as a `JSObject` (to call eval on it), you must wrap the result of `makeBeanWrapper` using `makeObjectWrapper`.

### Specifying extensions to load

Extensions are specified via an array of String, as the optional second parameter of makeEvaluator. For example:

           String[] extensions = new String[] {"FESI.Extensions.BasicIO",
                                               "FESI.Extensions.JavaAccess"};
           JSObject global = JSObject.makeEvaluator(extensions);

See as well the function `loadExtension` in the `JavaAccess` extension.

### Creating an extended script host

A script host need not be (and usually is not) an extended interpreter. The example `SampleFunc.java`in the directory `examples/jslib` demonstrate how a main program can extend and use EcmaScript. See the documentation of `JSObject` for more details.

`// Create interpreter`
`JSGlobalObject global = JSUtil.makeEvaluator();`

`// Declare a function`
`global.setMember("loadPage",`
`new JSFunctionAdapter() {`
`public Object doCall(JSObject thisObject, Object args[]) throws JSException {`
`if (args.length == 0) throw new JSException("loadPage: At least one argument needed (page name)");`
`return loadPage(args[0].toString());`
`}`
`});`

`// Interpret some user macro`
`String userCommand = ...`
`global.eval(userCommand);`

`// Routine implementing the core of the function (need not be public!)`
`private Object loadPage(String pageName) {`
`// Create an EcmaScript object to store the page attributes`
`JSObject page = global.makeJSObject();`
`page.setMemeber("name", pageName);`
`page.setMember("data", loadPageFromFile("pageName");`
`}`

There can be more than one interpreter created in each program. Access to an interpreter is synchronized. Each `JSObject` belongs to a specific interpreter.

### Creating a dynamically loadable extension

The example `FesiPop` demonstrate how to create an extension which can then be loaded in any interpreter or script host program. An extension is created by implementing the `JSExtension` interface, and then using the standard `JSObject` capabilities. Using an extension allow you to adapt the capabilities of a Java package to the specific need of the EcmaScript user, and have specific error handling.

### Use of code as function

It is sometime convenient to have the code fragments of a script be implemented as anonymous parameterless function, rather than main program. This allow to sue the `return` statement, for example in a conditional statement, which can simplify the code. To achieve this goal, simply use evalAsFunction instead of eval when calling the interpreter. Then code as:

> > `if (tag == "H") return 1;`
> > `else if (tag == "A" return 2;`
> > `else return -1;`

will work as expected.

In addition it is possible as well to build a function on the fly with specified parameter names and values. This is useful for code which must look like an event handler. The function `evalAsFunction` with parameters names and values serves this purpose. For example an event handler could look like:

> > `String names[] = {"event", "timeStamp"};`
> > `Object values [] = {event, new Date(event.getTimeStamp)};`
> > `target.evalAsFunction(script, names, values);`

Where the script could be for example:

> > `var _c = event.getChar();`
> > `if (_c == "t") alert("t received at: " + timeStamp);`

This execute has if the code fragment was a function of two parameters: `event` and `timeStamp`.

Return to the main page

Last update: 13 March 1999