-/**
- * The public API for the JS engine. JS itself is actually a class
- * implementing the absolute minimal amount of functionality for an
- * Object which can be manipulated by JavaScript code. The static
- * methods, fields, and inner classes of JS define the publicly
- * visible API for the XWT JavaScript engine; code outside this
- * package should never depend on anything not defined in this file.
- */
-public abstract class JS {
-
- // Public Helper Methods //////////////////////////////////////////////////////////////////////
-
- /** parse and compile a function */
- public static Function parse(String sourceName, int firstLine, Reader sourceCode) throws IOException {
- return new Function(sourceName, firstLine, sourceCode, null);
+/** The minimum set of functionality required for objects which are manipulated by JavaScript */
+public class JS extends org.xwt.util.BalancedTree {
+
+ public static final Object METHOD = new Object();
+ public final JS unclone() { return _unclone(); }
+ public Enumeration keys() throws JSExn { return entries == null ? emptyEnumeration : entries.keys(); }
+ public Object get(Object key) throws JSExn { return entries == null ? null : entries.get(key, null); }
+ public void put(Object key, Object val) throws JSExn { (entries==null?entries=new Hash():entries).put(key,null,val); }
+ public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ throw new JSExn("attempted to call the null value (method "+method+")");
+ }
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ throw new JSExn("you cannot call this object (class=" + this.getClass().getName() +")");
+ }
+
+ JS _unclone() { return this; }
+ public static class Cloneable extends JS {
+ public Cloneable() { }
+ public Object jsclone() throws JSExn {
+ throw new JSExn("cloning not yet implemented");
+ }
+ }
+
+ public static class Clone extends JS.Cloneable {
+ protected JS.Cloneable clonee = null;
+ JS _unclone() { return clonee.unclone(); }
+ public JS.Cloneable getClonee() { return clonee; }
+ public Clone(JS.Cloneable clonee) { this.clonee = clonee; }
+ public boolean equals(Object o) {
+ if (!(o instanceof JS)) return false;
+ return unclone() == ((JS)o).unclone();
+ }
+ public Enumeration keys() throws JSExn { return clonee.keys(); }
+ public Object get(Object key) throws JSExn { return clonee.get(key); }
+ public void put(Object key, Object val) throws JSExn { clonee.put(key, val); }
+ public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ return clonee.callMethod(method, a0, a1, a2, rest, nargs);
+ }
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ return clonee.call(a0, a1, a2, rest, nargs);
+ }
+ }
+
+ // Static Interpreter Control Methods ///////////////////////////////////////////////////////////////
+
+ /** log a message with the current JavaScript sourceName/line */
+ public static void log(Object message) { info(message); }
+ public static void debug(Object message) { Log.debug(Interpreter.getSourceName() + ":" + Interpreter.getLine(), message); }
+ public static void info(Object message) { Log.info(Interpreter.getSourceName() + ":" + Interpreter.getLine(), message); }
+ public static void warn(Object message) { Log.warn(Interpreter.getSourceName() + ":" + Interpreter.getLine(), message); }
+ public static void error(Object message) { Log.error(Interpreter.getSourceName() + ":" + Interpreter.getLine(), message); }
+
+ public static class NotPauseableException extends Exception { NotPauseableException() { } }
+
+ /** returns a callback which will restart the context; expects a value to be pushed onto the stack when unpaused */
+ public static UnpauseCallback pause() throws NotPauseableException {
+ Interpreter i = Interpreter.current();
+ if (i.pausecount == -1) throw new NotPauseableException();
+ i.pausecount++;
+ return new JS.UnpauseCallback(i);