more new js api fixes and cleanup
authorbrian <brian@brianweb.net>
Tue, 6 Jul 2004 07:59:02 +0000 (07:59 +0000)
committerbrian <brian@brianweb.net>
Tue, 6 Jul 2004 07:59:02 +0000 (07:59 +0000)
darcs-hash:20040706075902-24bed-5e89f496b7e07452d6efed7ced9303c7895cf1b6.gz

src/org/ibex/js/Interpreter.java
src/org/ibex/js/JS.java
src/org/ibex/js/JSExn.java
src/org/ibex/js/JSNumber.java
src/org/ibex/js/JSScope.java
src/org/ibex/js/PropertyFile.java
src/org/ibex/js/Test.java
src/org/ibex/js/Trap.java

index cdc1ef0..73de955 100644 (file)
@@ -307,9 +307,9 @@ class Interpreter implements ByteCodes, Tokens {
                     break;
                 } else {
                     ret = target.get(key);
+                    if (pausecount > initialPauseCount) { pc++; return null; }   // we were paused
                     if (ret == JS.METHOD) ret = new Stub(target, key);
                     stack.push(ret);
-                    if (pausecount > initialPauseCount) { pc++; return null; }   // we were paused
                     break;
                 }
             }
@@ -405,20 +405,6 @@ class Interpreter implements ByteCodes, Tokens {
                 break;
             }
 
-            // FIXME: This was for the old trap syntax, remove it, shouldn't be needed anymore
-            case ASSIGN_SUB: case ASSIGN_ADD: {
-                JS val = stack.pop();
-                JS key = stack.pop();
-                JS obj = stack.peek();
-                // The following setup is VERY important. The generated bytecode depends on the stack
-                // being setup like this (top to bottom) KEY, OBJ, VAL, KEY, OBJ
-                stack.push(key);
-                stack.push(val);
-                stack.push(obj);
-                stack.push(key);
-                break;
-            }
-
             case ADD: {
                 int count = ((JSNumber)arg).toInt();
                 if(count < 2) throw new Error("this should never happen");
@@ -510,36 +496,45 @@ class Interpreter implements ByteCodes, Tokens {
             }
 
         } catch(JSExn e) {
-            while(stack.size() > 0) {
-                JS o = stack.pop();
-                if (o instanceof CatchMarker || o instanceof TryMarker) {
-                    boolean inCatch = o instanceof CatchMarker;
-                    if(inCatch) {
-                        o = stack.pop();
-                        if(((TryMarker)o).finallyLoc < 0) continue; // no finally block, keep going
-                    }
-                    if(!inCatch && ((TryMarker)o).catchLoc >= 0) {
-                        // run the catch block, this will implicitly run the finally block, if it exists
-                        stack.push(o);
-                        stack.push(catchMarker);
-                        stack.push(e.getObject());
-                        f = ((TryMarker)o).f;
-                        scope = ((TryMarker)o).scope;
-                        pc = ((TryMarker)o).catchLoc - 1;
-                        continue OUTER;
-                    } else {
-                        stack.push(new FinallyData(e));
-                        f = ((TryMarker)o).f;
-                        scope = ((TryMarker)o).scope;
-                        pc = ((TryMarker)o).finallyLoc - 1;
-                        continue OUTER;
-                    }
-                }
-            }
-            throw e;
+            catchException(e);
+            pc--; // it'll get incremented on the next iteration
         } // end try/catch
         } // end for
     }
+    
+    /** tries to find a handler withing the call chain for this exception
+        if a handler is found the interpreter is setup to call the exception handler
+        if a handler is not found the exception is thrown
+    */
+    void catchException(JSExn e) throws JSExn {
+        while(stack.size() > 0) {
+            JS o = stack.pop();
+            if (o instanceof CatchMarker || o instanceof TryMarker) {
+                boolean inCatch = o instanceof CatchMarker;
+                if(inCatch) {
+                    o = stack.pop();
+                    if(((TryMarker)o).finallyLoc < 0) continue; // no finally block, keep going
+                }
+                if(!inCatch && ((TryMarker)o).catchLoc >= 0) {
+                    // run the catch block, this will implicitly run the finally block, if it exists
+                    stack.push(o);
+                    stack.push(catchMarker);
+                    stack.push(e.getObject());
+                    f = ((TryMarker)o).f;
+                    scope = ((TryMarker)o).scope;
+                    pc = ((TryMarker)o).catchLoc;
+                    return;
+                } else {
+                    stack.push(new FinallyData(e));
+                    f = ((TryMarker)o).f;
+                    scope = ((TryMarker)o).scope;
+                    pc = ((TryMarker)o).finallyLoc;
+                    return;
+                }
+            }
+        }
+        throw e;
+    }
 
 
 
@@ -628,149 +623,7 @@ class Interpreter implements ByteCodes, Tokens {
         }
     }
 
-    // Operations on Primitives //////////////////////////////////////////////////////////////////////
-
-    // FIXME: Move these into JSString and JSNumber
-    /*static Object callMethodOnPrimitive(Object o, Object method, Object arg0, Object arg1, Object arg2, Object[] rest, int alength) throws JSExn {
-        if (method == null || !(method instanceof String) || "".equals(method))
-            throw new JSExn("attempt to call a non-existant method on a primitive");
-
-        if (o instanceof Number) {
-            //#switch(method)
-            case "toFixed": throw new JSExn("toFixed() not implemented");
-            case "toExponential": throw new JSExn("toExponential() not implemented");
-            case "toPrecision": throw new JSExn("toPrecision() not implemented");
-            case "toString": {
-                int radix = alength >= 1 ? JS.toInt(arg0) : 10;
-                return Long.toString(((Number)o).longValue(),radix);
-            }
-            //#end
-        } else if (o instanceof Boolean) {
-            // No methods for Booleans
-            throw new JSExn("attempt to call a method on a Boolean");
-        }
-
-        String s = JS.toString(o);
-        int slength = s.length();
-        //#switch(method)
-        case "substring": {
-            int a = alength >= 1 ? JS.toInt(arg0) : 0;
-            int b = alength >= 2 ? JS.toInt(arg1) : slength;
-            if (a > slength) a = slength;
-            if (b > slength) b = slength;
-            if (a < 0) a = 0;
-            if (b < 0) b = 0;
-            if (a > b) { int tmp = a; a = b; b = tmp; }
-            return s.substring(a,b);
-        }
-        case "substr": {
-            int start = alength >= 1 ? JS.toInt(arg0) : 0;
-            int len = alength >= 2 ? JS.toInt(arg1) : Integer.MAX_VALUE;
-            if (start < 0) start = slength + start;
-            if (start < 0) start = 0;
-            if (len < 0) len = 0;
-            if (len > slength - start) len = slength - start;
-            if (len <= 0) return "";
-            return s.substring(start,start+len);
-        }
-        case "charAt": {
-            int p = alength >= 1 ? JS.toInt(arg0) : 0;
-            if (p < 0 || p >= slength) return "";
-            return s.substring(p,p+1);
-        }
-        case "charCodeAt": {
-            int p = alength >= 1 ? JS.toInt(arg0) : 0;
-            if (p < 0 || p >= slength) return JS.N(Double.NaN);
-            return JS.N(s.charAt(p));
-        }
-        case "concat": {
-            StringBuffer sb = new StringBuffer(slength*2).append(s);
-            for(int i=0;i<alength;i++) sb.append(i==0?arg0:i==1?arg1:i==2?arg2:rest[i-3]);
-            return sb.toString();
-        }
-        case "indexOf": {
-            String search = alength >= 1 ? JS.toString(arg0) : "null";
-            int start = alength >= 2 ? JS.toInt(arg1) : 0;
-            // Java's indexOf handles an out of bounds start index, it'll return -1
-            return JS.N(s.indexOf(search,start));
-        }
-        case "lastIndexOf": {
-            String search = alength >= 1 ? JS.toString(arg0) : "null";
-            int start = alength >= 2 ? JS.toInt(arg1) : 0;
-            // Java's indexOf handles an out of bounds start index, it'll return -1
-            return JS.N(s.lastIndexOf(search,start));            
-        }
-        case "match": return JSRegexp.stringMatch(s,arg0);
-        case "replace": return JSRegexp.stringReplace(s,arg0,arg1);
-        case "search": return JSRegexp.stringSearch(s,arg0);
-        case "split": return JSRegexp.stringSplit(s,arg0,arg1,alength);
-        case "toLowerCase": return s.toLowerCase();
-        case "toUpperCase": return s.toUpperCase();
-        case "toString": return s;
-        case "slice": {
-            int a = alength >= 1 ? JS.toInt(arg0) : 0;
-            int b = alength >= 2 ? JS.toInt(arg1) : slength;
-            if (a < 0) a = slength + a;
-            if (b < 0) b = slength + b;
-            if (a < 0) a = 0;
-            if (b < 0) b = 0;
-            if (a > slength) a = slength;
-            if (b > slength) b = slength;
-            if (a > b) return "";
-            return s.substring(a,b);
-        }
-        //#end
-        throw new JSExn("Attempted to call non-existent method: " + method);
-    }
-    
-    static Object getFromPrimitive(Object o, Object key) throws JSExn {
-        boolean returnJS = false;
-        if (o instanceof Boolean) {
-            throw new JSExn("Booleans do not have properties");
-        } else if (o instanceof Number) {
-            if (key.equals("toPrecision") || key.equals("toExponential") || key.equals("toFixed"))
-                returnJS = true;
-        }
-        if (!returnJS) {
-            // the string stuff applies to everything
-            String s = JS.toString(o);
-            
-            // this is sort of ugly, but this list should never change
-            // These should provide a complete (enough) implementation of the ECMA-262 String object
-
-            //#switch(key)
-            case "length": return JS.N(s.length());
-            case "substring": returnJS = true; break; 
-            case "charAt": returnJS = true; break; 
-            case "charCodeAt": returnJS = true; break; 
-            case "concat": returnJS = true; break; 
-            case "indexOf": returnJS = true; break; 
-            case "lastIndexOf": returnJS = true; break; 
-            case "match": returnJS = true; break; 
-            case "replace": returnJS = true; break; 
-            case "search": returnJS = true; break; 
-            case "slice": returnJS = true; break; 
-            case "split": returnJS = true; break; 
-            case "toLowerCase": returnJS = true; break; 
-            case "toUpperCase": returnJS = true; break; 
-            case "toString": returnJS = true; break; 
-            case "substr": returnJS = true; break;  
-           //#end
-        }
-        if (returnJS) {
-            final Object target = o;
-            final String method = JS.toString(o);
-            return new JS() {
-                    public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
-                        if (nargs > 2) throw new JSExn("cannot call that method with that many arguments");
-                        return callMethodOnPrimitive(target, method, a0, a1, a2, rest, nargs);
-                    }
-            };
-        }
-        return null;
-    }*/
-
-    private static class Stub extends JS {
+    static class Stub extends JS {
         private JS method;
         JS obj;
         public Stub(JS obj, JS method) { this.obj = obj; this.method = method; }
index 4928101..d8d0f33 100644 (file)
@@ -117,21 +117,30 @@ public abstract class JS {
     public static UnpauseCallback pause() throws NotPauseableException {
         Interpreter i = Interpreter.current();
         if (i.pausecount == -1) throw new NotPauseableException();
+        boolean get;
+        switch(i.f.op[i.pc]) {
+            case Tokens.RETURN: case ByteCodes.PUT: get = false; break;
+            case ByteCodes.GET: get = true; break;
+            default: throw new Error("should never happen");
+        }
         i.pausecount++;
-        return new JS.UnpauseCallback(i);
+        return new JS.UnpauseCallback(i,get);
     }
 
     public static class UnpauseCallback implements Task {
-        Interpreter i;
-        UnpauseCallback(Interpreter i) { this.i = i; }
-        public void perform() throws JSExn { unpause((JS)null); }
-        public void unpause(JS o) throws JSExn {
-            i.stack.push(o);
-            i.resume();
+        private Interpreter i;
+        private boolean get;
+        UnpauseCallback(Interpreter i, boolean get) { this.i = i; this.get = get; }
+        public void perform() throws JSExn { unpause(); }
+        public JS unpause() throws JSExn { return unpause((JS)null); }
+        public JS unpause(JS o) throws JSExn {
+            if (o == JS.METHOD) throw new JSExn("can't return a method to a paused context");
+            if(get) i.stack.push(o);
+            return i.resume();
         }
-        public void unpause(JSExn e) {
-            // FIXME: Throw the JSExn into the js world
-            throw new Error("Exception " + e + " thrown from background task");
+        public JS unpause(JSExn e) throws JSExn {
+            i.catchException(e);
+            return i.resume();
         }
     }
 
index e01b769..1301199 100644 (file)
@@ -17,17 +17,15 @@ public class JSExn extends Exception {
     public JSExn(JS js, Interpreter.Stack stack, JSFunction f, int pc, JSScope scope) { this.js = js; fill(stack, f, pc, scope); }
     private void fill(Interpreter.Stack stack, JSFunction f, int pc, JSScope scope) {
         addBacktrace(f.sourceName + ":" + f.line[pc]);
-        // FIXME: "trap on property"
-        /*if (scope != null && scope instanceof Trap.TrapScope)
-            addBacktrace("trap on property \"" + ((Trap.TrapScope)scope).t.name + "\"");*/
         for(int i=stack.size()-1; i>=0; i--) {
             JS element = stack.elementAt(i);
             if (element instanceof Interpreter.CallMarker) {
                 Interpreter.CallMarker cm = (Interpreter.CallMarker)element;
-                if (cm.f != null)
-                    addBacktrace(cm.f.sourceName + ":" + cm.f.line[cm.pc-1]);
-                /*if (cm.scope != null && cm.scope instanceof Trap.TrapScope)
-                    addBacktrace("trap on property \"" + ((Trap.TrapScope)cm.scope).t.name + "\"");*/
+                if(cm.f == null) break;
+                String s = cm.f.sourceName + ":" + cm.f.line[cm.pc-1];
+                if(cm instanceof Interpreter.TrapMarker) 
+                    s += " (trap on " + JS.debugToString(((Interpreter.TrapMarker)cm).key) + ")";
+                addBacktrace(s);
             }
         }
     }
index 16c6836..8137cbc 100644 (file)
@@ -27,8 +27,7 @@ abstract class JSNumber extends JSPrimitive {
     boolean toBoolean() { return toInt() != 0; }
     double toDouble() { return toLong(); }
     float toFloat() { return (float) toDouble(); }
-    // FIXME: Number functions
-
+    
     final static class I extends JSNumber {
         final int i;
         I(int i) { this.i = i; }
index 51cc33a..f7c6355 100644 (file)
@@ -33,14 +33,26 @@ public class JSScope extends JS.BT {
         return s;
     }
 
-    // FIXME: Update this for new API, it also has some other problems
-    /*public static class Global extends JSScope {
-        private final static Double NaN = new Double(Double.NaN);
-        private final static Double POSITIVE_INFINITY = new Double(Double.POSITIVE_INFINITY);
+    public static class Global extends JSScope {
+        private final static JS NaN = N(Double.NaN);
+        private final static JS POSITIVE_INFINITY = N(Double.POSITIVE_INFINITY);
 
         public Global() { super(null); }
-        public Object get(Object key) throws JSExn {
-            //#switch(key)
+        public Global(JSScope p) { super(p); }
+        
+        public void declare(JS k) throws JSExn { throw new JSExn("can't declare variables in the global scope"); }
+        
+        // HACK: "this" gets lost on the way back through the scope chain
+        // We'll have to do something better with this when Scope is rewritten
+        public JS get(JS key) throws JSExn {
+            JS ret = _get(key);
+            if(ret == METHOD) return new Interpreter.Stub(this,key);
+            return ret;
+        }
+        
+        public JS _get(JS key) throws JSExn {
+            if(!JS.isString(key)) return super.get(key);
+            //#switch(JS.toString(key))
             case "NaN": return NaN;
             case "Infinity": return POSITIVE_INFINITY;
             case "undefined": return null;
@@ -59,43 +71,25 @@ public class JSScope extends JS.BT {
             return super.get(key);
         }
 
-        public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
-            switch(nargs) {
-                case 0: {
-                    //#switch(method)
-                    case "stringFromCharCode":
-                        char buf[] = new char[nargs];
-                        for(int i=0; i<nargs; i++) buf[i] = (char)(JS.toInt(i==0?a0:i==1?a1:i==2?a2:rest[i-3]) & 0xffff);
-                        return new String(buf);
-                    //#end
-                    break;
-                }
-                case 1: {
-                    //#switch(method)
-                    case "parseInt": return parseInt(a0, N(0));
-                    case "parseFloat": return parseFloat(a0);
-                    case "isNaN": { double d = toDouble(a0); return d == d ? F : T; }
-                    case "isFinite": { double d = toDouble(a0); return (d == d && !Double.isInfinite(d)) ? T : F; }
-                    case "decodeURI": throw new JSExn("unimplemented");
-                    case "decodeURIComponent": throw new JSExn("unimplemented");
-                    case "encodeURI": throw new JSExn("unimplemented");
-                    case "encodeURIComponent": throw new JSExn("unimplemented");
-                    case "escape": throw new JSExn("unimplemented");
-                    case "unescape": throw new JSExn("unimplemented");
-                    //#end
-                    break;
-                }
-                case 2: {
-                    //#switch(method)
-                    case "parseInt": return parseInt(a0, a1);
-                    //#end
-                    break;
-                }
-            }
+        public JS callMethod(JS method, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
+            if(!JS.isString(method)) return super.callMethod(method, a0, a1, a2, rest, nargs);
+            //#switch(JS.toString(method))
+            case "parseInt": return parseInt(a0, N(0));
+            case "parseFloat": return parseFloat(a0);
+            case "isNaN": { double d = toDouble(a0); return d == d ? F : T; }
+            case "isFinite": { double d = toDouble(a0); return (d == d && !Double.isInfinite(d)) ? T : F; }
+            case "decodeURI": throw new JSExn("unimplemented");
+            case "decodeURIComponent": throw new JSExn("unimplemented");
+            case "encodeURI": throw new JSExn("unimplemented");
+            case "encodeURIComponent": throw new JSExn("unimplemented");
+            case "escape": throw new JSExn("unimplemented");
+            case "unescape": throw new JSExn("unimplemented");
+            case "parseInt": return parseInt(a0, a1);
+            //#end
             return super.callMethod(method, a0, a1, a2, rest, nargs);
         }
 
-        private Object parseInt(Object arg, Object r) throws JSExn {
+        private JS parseInt(JS arg, JS r) throws JSExn {
             int radix = JS.toInt(r);
             String s = JS.toString(arg);
             int start = 0;
@@ -136,7 +130,7 @@ public class JSScope extends JS.BT {
             return JS.N((long)sign*n);
         }
 
-        private Object parseFloat(Object arg) throws JSExn {
+        private JS parseFloat(JS arg) throws JSExn {
             String s = JS.toString(arg);
             int start = 0;
             int length = s.length();
@@ -146,12 +140,12 @@ public class JSScope extends JS.BT {
             // trailing garbage
             while(start < end) {
                 try {
-                    return JS.N(s.substring(start,length));
+                    return JS.N(new Double(s.substring(start,length)));
                 } catch(NumberFormatException e) { }
                 end--;
             }
             return NaN;
         }
-    }*/
+    }
 }
 
index f312a7e..ee7be2e 100644 (file)
@@ -5,7 +5,7 @@ import org.ibex.util.*;
 import java.util.*;
 import java.io.*;
 
-// FIXME: Update for new api
+// FEATURE: Update for new api
 
 /** A JS interface to a Java '.properties' file; very crude */
 public class PropertyFile extends JS {
index 5c7055e..d2dd77f 100644 (file)
@@ -3,22 +3,58 @@ package org.ibex.js;
 import java.io.*;
 
 public class Test extends JS {
+    static JS.UnpauseCallback up = null;
+    static String action;
+    
     public static void main(String[] args) throws Exception {
         if(args.length == 0) { System.err.println("Usage Test filename"); System.exit(1); }
-        JS f = JS.fromReader(args[0],0,new FileReader(args[0]));
+        JS f = JS.fromReader(args[0],1,new FileReader(args[0]));
         System.out.println(((JSFunction)f).dump());
-        JSScope s = new JSScope(null);
+        JSScope s = new JSScope(new JSScope.Global());
         s.put(JS.S("sys"),new Test());
         f = JS.cloneWithNewParentScope(f,s);
-        JS ret = f.call(null,null,null,null,0);
+        //JS ret = f.call(null,null,null,null,0);
+        Interpreter i = new Interpreter((JSFunction)f, true, new JSArray());
+        JS ret = i.resume();
+        while(up != null) {
+            JS.UnpauseCallback up = Test.up; Test.up = null;
+            if("throw".equals(action)) ret = up.unpause(new JSExn("this was thrown to a paused context"));
+            else if("bgget".equals(action)) ret = up.unpause(JS.S("I'm returning this from a get request"));
+            else {
+                System.out.println("got a background put " + action);
+                ret = up.unpause();
+            }
+        }
         System.out.println("Script returned: " + JS.toString(ret));
     }
     
     public JS get(JS key) throws JSExn {
         if(!JS.isString(key)) return null;
         if("print".equals(JS.toString(key))) return METHOD;
+        if("bgget".equals(JS.toString(key))) {
+            action = "bgget";
+            try {
+                up = JS.pause();
+            } catch(NotPauseableException e) {
+                throw new Error("should never happen");
+            }
+            return null;
+        }
         return super.get(key);
     }
+        
+    public void put(JS key, JS val) throws JSExn {
+        if("bgput".equals(JS.toString(key))) {
+            action = JS.toString(val);
+            try {
+                up = JS.pause();
+            } catch(NotPauseableException e) {
+                throw new Error("should never happen");
+            }
+        return;
+        }        
+        super.put(key,val);
+    }
     
     public JS callMethod(JS method, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
         if(!JS.isString(method)) return null;
index 248ae6a..67d0238 100644 (file)
@@ -46,19 +46,4 @@ class Trap {
         i.stack.push(name);
         return i.resume();
     }
-
-    // FIXME: review; is necessary?
-    /*static class TrapScope extends JSScope {
-        Trap t;
-        Object val = null;
-        boolean cascadeHappened = false;
-        public TrapScope(JSScope parent, Trap t, Object val) { super(parent); this.t = t; this.val = val; }
-        public Object get(Object key) throws JSExn {
-            if (key.equals("trapee")) return t.trapee;
-            if (key.equals("callee")) return t.f;
-            if (key.equals("trapname")) return t.name;
-            return super.get(key);
-        }
-    }*/
 }
-