2003/06/07 12:00:57
[org.ibex.core.git] / src / org / xwt / js / JS.java
index 65bb465..c6b4c77 100644 (file)
@@ -5,26 +5,25 @@ import org.xwt.util.*;
 import java.io.*;
 import java.util.*;
 
-/** The public API for the JS engine */
-// FEATURE: try using mutable, recycled 'Num' objects
+/**
+ *  The public API for the JS engine; JS itself is actually a class
+ *  implementing the minimal amount of functionality for an Object
+ *  which can be manipulated by JavaScript code.
+ */
 public abstract class JS { 
 
+
     // Static Methods //////////////////////////////////////////////////////////////////////
 
-    public static Function getCurrentFunction() {
-       return (Function)currentFunction.get(Thread.currentThread());
-    }
-    public static String getCurrentFunctionSourceName() {
-       return getCurrentFunctionSourceName(Thread.currentThread());
-    }
+    private static Hashtable currentFunction = new Hashtable();
+    public static Function getCurrentFunction() { return (Function)currentFunction.get(Thread.currentThread()); }
+    public static String getCurrentFunctionSourceName() { return getCurrentFunctionSourceName(Thread.currentThread()); }
+    public static String getFileAndLine() { return getCurrentFunctionSourceName() + ":" + getCurrentFunction().getLine(); }
     public static String getCurrentFunctionSourceName(Thread t) {
        Function f = (Function)currentFunction.get(t);
        if (f == null) return "null";
        return f.getSourceName();
     }
-    public static String getFileAndLine() {
-       return "unknown:??";
-    }
 
     public static boolean toBoolean(Object o) {
        if (o == null) return false;
@@ -32,6 +31,7 @@ public abstract class JS {
        if (o instanceof Number) return o.equals(new Integer(0));
        return true;
     }
+
     public static long toLong(Object o) { return toNumber(o).longValue(); }
     public static double toDouble(Object o) { return toNumber(o).doubleValue(); }
     public static Number toNumber(Object o) {
@@ -40,10 +40,10 @@ public abstract class JS {
        if (o instanceof String) try { return new Double((String)o); } catch (NumberFormatException e) { return new Double(0); }
        if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? new Long(1) : new Long(0);
        if (o instanceof JS) return ((JS)o).coerceToNumber();
-       // FIXME
-       throw new Error("toNumber() got object of type " + o.getClass().getName());
+       throw new Error("toNumber() got object of type " + o.getClass().getName() + " which we don't know how to handle");
     }
 
+
     // Instance Methods ////////////////////////////////////////////////////////////////////
  
     public abstract Object get(Object key) throws JS.Exn; 
@@ -57,6 +57,7 @@ public abstract class JS {
 
     // Subclasses /////////////////////////////////////////////////////////////////////////
 
+    /** A slightly more featureful version of JS */
     public static class Obj extends JS {
        private Hash entries = new Hash();
        private boolean sealed = false;
@@ -68,6 +69,7 @@ public abstract class JS {
        public Object[] keys() { return(entries.keys()); }
     }
 
+    /** An exception which can be thrown and caught by JavaScripts */
     public static class Exn extends RuntimeException { 
        private Object js = null; 
        public Exn(Object js) { this.js = js; } 
@@ -76,6 +78,7 @@ public abstract class JS {
        public Object getObject() { return js; } 
     } 
 
+    /** A JavaScript Array */
     public static class Array extends Obj {
        private Vec vec = new Vec();
        public Array() { }
@@ -128,33 +131,35 @@ public abstract class JS {
        public void setElementAt(Object o, int i) { vec.setElementAt(o, i); }
     }
 
-    public static Hashtable currentFunction = new Hashtable();
+    /** Anything that is callable */
     public static abstract class Function extends Obj {
-       public abstract Object _call(JS.Array args) throws JS.Exn;
        public String getSourceName() throws JS.Exn { return "unknown"; }
        public int getLine() throws JS.Exn { return -1; }
-       public final Object call(JS.Array args) throws JS.Exn { return _call(args); }
-    }
-
-    public static class Script extends Function {
-       Vector e = null;
-       private Script(Vector e) { this.e = e; }
-       public String getSourceName() throws JS.Exn { return ((ForthBlock)e.elementAt(0)).sourceName; }
-       public Object _call(JS.Array args) throws JS.Exn {
-           Scope rootScope = (Scope)args.elementAt(0);
+       public abstract Object _call(JS.Array args) throws JS.Exn, ByteCodeBlock.ControlTransferException;
+       public final Object call(JS.Array args) throws JS.Exn {
            Function saved = (Function)currentFunction.get(Thread.currentThread());
            currentFunction.put(Thread.currentThread(), this);
            try {
-               for(int i=0; i<e.size(); i++)
-                   ((ForthBlock)e.elementAt(i)).eval(rootScope);
-           } catch (ForthBlock.ReturnException e) {
-               // ignore
-           } catch (ForthBlock.ControlTransferException e) {
-               throw new JS.Exn("block threw a ControlTransferException: " + e);
+               return _call(args);
+           } catch (ByteCodeBlock.ReturnException e) {  // ignore
+               return e.retval;
+           } catch (ByteCodeBlock.ControlTransferException e) {
+               throw new RuntimeException(getSourceName() + ":" + getLine() +
+                                          " error, ControlTransferException tried to leave a function");
            } finally {
                if (saved == null) currentFunction.remove(Thread.currentThread());
                else currentFunction.put(Thread.currentThread(), saved);
            }
+       }
+    }
+
+    public static class Script extends Function {
+       Vector e = null;
+       private Script(Vector e) { this.e = e; }
+       public String getSourceName() throws JS.Exn { return ((ByteCodeBlock)e.elementAt(0)).sourceName; }
+       public Object _call(JS.Array args) throws JS.Exn, ByteCodeBlock.ControlTransferException {
+           Scope rootScope = (Scope)args.elementAt(0);
+           for(int i=0; i<e.size(); i++) ((ByteCodeBlock)e.elementAt(i)).eval(rootScope);
            return null;
        }
        public static Script parse(Reader r, String sourceName, int line) throws IOException {
@@ -162,7 +167,7 @@ public abstract class JS {
            try {
                Vector exprs = new Vector();
                while(true) {
-                   ForthBlock ret = p.parseStatement();
+                   ByteCodeBlock ret = p.parseStatement();
                    if (ret == null) break;
                    exprs.addElement(ret);
                }