2004/01/07 20:37:33
authorbrian <brian@xwt.org>
Fri, 30 Jan 2004 07:44:06 +0000 (07:44 +0000)
committerbrian <brian@xwt.org>
Fri, 30 Jan 2004 07:44:06 +0000 (07:44 +0000)
darcs-hash:20040130074406-aa32f-c56b3f845ff780f2ed7d209f9e1d5315f9448047.gz

src/org/xwt/js/JS.java
src/org/xwt/js/JSArray.java
src/org/xwt/js/JSExn.java
src/org/xwt/js/JSFunction.java
src/org/xwt/js/JSRegexp.java
src/org/xwt/js/JSScope.java
src/org/xwt/js/Parser.java
src/org/xwt/js/Trap.java
src/org/xwt/util/XML.java

index c6d5dd6..a4b1650 100644 (file)
@@ -14,6 +14,10 @@ public class JS extends org.xwt.util.BalancedTree {
     /** log a message with the current JavaScript sourceName/line */
     public static void log(Object o, Object message) { log(message); }
     public static void log(Object message) { Log.echo(JS.getSourceName() + ":" + JS.getLine(), message); }
+    public static void log(JSExn e) {
+        Log.echo(e,"JS Exception: " + e.getObject() + "\n" + e.backtrace());
+        Log.echo(e,e);
+    }
 
     public static int getLine() {
         Interpreter c = Interpreter.current();
@@ -156,7 +160,7 @@ public class JS extends org.xwt.util.BalancedTree {
     /** override and return true to allow placing traps on this object.
         *  if isRead true, this is a read trap, otherwise write trap
         **/
-    protected boolean isTrappable(Object name, boolean isRead) { return false; }
+    protected boolean isTrappable(Object name, boolean isRead) { return true; }
 
     /** performs a put, triggering traps if present; traps are run in an unpauseable interpreter */
     public void putAndTriggerTraps(Object key, Object value) throws JSExn {
index 2f5f04d..a5798bf 100644 (file)
@@ -2,14 +2,15 @@
 package org.xwt.js; 
 
 import org.xwt.util.*; 
-import java.io.*;
 import java.util.*;
 
 /** A JavaScript JSArray */
 public class JSArray extends JS {
-    private Vec vec = new Vec();
+    private static final Object NULL = new Object();
+    
     public JSArray() { }
-    public JSArray(int size) { vec.setSize(size); }
+    public JSArray(int size) { setSize(size); }
+    
     private static int intVal(Object o) {
         if (o instanceof Number) {
             int intVal = ((Number)o).intValue();
@@ -24,14 +25,16 @@ public class JSArray extends JS {
     
     public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
         //#switch(method)
-        case "pop": return vec.pop();
+        case "pop": {
+            int oldSize = size();
+            if(oldSize == 0) return null;
+            return removeElementAt(oldSize-1);
+        }
         case "reverse": return reverse();
         case "toString": return join(",");
         case "shift":
             if(length() == 0) return null;
-            Object o = vec.elementAt(0);
-            vec.removeElementAt(0);
-            return o;
+            return removeElementAt(0);
         case "join":
             return join(nargs == 0 ? "," : JS.toString(a0));
         case "sort":
@@ -40,12 +43,14 @@ public class JSArray extends JS {
             int start = toInt(nargs < 1 ? null : a0);
             int end = nargs < 2 ? length() : toInt(a1);
             return slice(start, end);
-        case "push":
-            for(int i=0; i<nargs; i++) vec.push(i==0?a0:i==1?a1:i==2?a2:rest[i-3]);
-            return N(vec.size());
+        case "push": {
+            int oldSize = size();
+            for(int i=0; i<nargs; i++) insertElementAt(i==0?a0:i==1?a1:i==2?a2:rest[i-3],oldSize+i);
+            return N(oldSize + nargs);
+        }
         case "unshift":
-            for(int i=0; i<nargs; i++) vec.insertElementAt(i==0?a0:i==1?a1:i==2?a2:rest[i-3], i);
-            return N(vec.size());
+            for(int i=0; i<nargs; i++) insertElementAt(i==0?a0:i==1?a1:i==2?a2:rest[i-3],i);
+            return N(size());
         case "splice":
             JSArray array = new JSArray();
             for(int i=0; i<nargs; i++) array.addElement(i==0?a0:i==1?a1:i==2?a2:rest[i-3]);
@@ -55,14 +60,10 @@ public class JSArray extends JS {
     }
         
     public Object get(Object key) throws JSExn {
-        if (key instanceof Number) {
-            int i = intVal(key);
-            if (i == Integer.MIN_VALUE) return super.get(key);
-            try {
-                return vec.elementAt(i);
-            } catch (ArrayIndexOutOfBoundsException e) {
-                return null;
-            }
+        int i = intVal(key);
+        if (i != Integer.MIN_VALUE) {
+            if (i < 0 || i >= size()) return null;
+            return elementAt(i);
         }
         //#switch(key)
         case "pop": return METHOD;
@@ -75,42 +76,73 @@ public class JSArray extends JS {
         case "push": return METHOD;
         case "unshift": return METHOD;
         case "splice": return METHOD;
-        case "length": return N(vec.size());
+        case "length": return N(size());
         //#end
         return super.get(key);
     }
 
     public void put(Object key, Object val) throws JSExn {
-        if (key.equals("length")) vec.setSize(toNumber(val).intValue());
+        if (key.equals("length")) setSize(toInt(val));
         int i = intVal(key);
-        if (i == Integer.MIN_VALUE) super.put(key, val);
+        if (i == Integer.MIN_VALUE)
+            super.put(key, val);
         else {
-            if (i >= vec.size()) vec.setSize(i+1);
-            vec.setElementAt(val, i);
+            int oldSize = size();
+            if(i < oldSize) {
+                setElementAt(val,i);
+            } else {
+                if(i > oldSize) setSize(i);
+                insertElementAt(val,i);
+            }
         }
     }
 
     public Enumeration keys() {
         return new Enumeration() {
                 int cur = 0;
-                public boolean hasMoreElements() { return cur >= vec.size(); }
+                public boolean hasMoreElements() { return cur >= size(); }
                 public Object nextElement() {
-                    if (cur >= vec.size()) throw new NoSuchElementException();
+                    if (cur >= size()) throw new NoSuchElementException();
                     return new Integer(cur++);
                 }
             };
     }
 
-    public void setSize(int i) { vec.setSize(i); }
-    public int length() { return vec.size(); }
-    public Object elementAt(int i) { return vec.elementAt(i); }
-    public void addElement(Object o) { vec.addElement(o); }
-    public void setElementAt(Object o, int i) { vec.setElementAt(o, i); }
-    public int size() { return vec.size(); }
+    public final void setSize(int newSize) {
+        // FEATURE: This could be done a lot more efficiently in BalancedTree
+        int oldSize = size();
+        for(int i=oldSize;i<newSize;i++) insertElementAt(null,i);
+        for(int i=oldSize-1;i>=newSize;i--) removeElementAt(i);
+    }
+    
+    public final int length() { return size(); }
+    public final Object elementAt(int i) { 
+        if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
+        Object o = getNode(i);
+        return o == NULL ? null : o;
+    }
+    public final void addElement(Object o) { 
+        insertNode(size(),o==null ? NULL : o);
+    }
+    public final void setElementAt(Object o, int i) {
+        if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
+        replaceNode(i,o==null ? NULL : o);
+    }
+    public final void insertElementAt(Object o, int i) {
+        if(i < 0 || i > size()) throw new ArrayIndexOutOfBoundsException(i);
+        insertNode(i,o==null ? NULL : o);
+    }
+    public final Object removeElementAt(int i) {
+        if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
+        Object o = deleteNode(i);
+        return o == NULL ? null : o;
+    }
+    
+    public final int size() { return treeSize(); }
     public String typeName() { return "array"; }
         
     private Object join(String sep) {
-        int length = vec.size();
+        int length = size();
         if(length == 0) return "";
         StringBuffer sb = new StringBuffer(64);
         int i=0;
@@ -123,12 +155,13 @@ public class JSArray extends JS {
         return sb.toString();
     }
     
+    // FEATURE: Implement this more efficiently
     private Object reverse() {
-        Vec oldVec = vec;
-        int size = oldVec.size();
+        int size = size();
         if(size < 2) return this;
-        vec = new Vec(size);
-        for(int i=size-1;i>=0;i--) vec.addElement(oldVec.elementAt(i));
+        Vec vec = toVec();
+        clear();
+        for(int i=size-1,j=0;i>=0;i--,j++) insertElementAt(vec.elementAt(i),j);
         return this;
     }
     
@@ -152,6 +185,7 @@ public class JSArray extends JS {
         }
     };
     private Object sort(Object tmp) throws JSExn {
+        Vec vec = toVec();
         if(tmp instanceof JS) {
             final JSArray funcArgs = new JSArray(2);
             final JS jsFunc = (JS) tmp;
@@ -170,6 +204,7 @@ public class JSArray extends JS {
         } else {
             vec.sort(defaultSort);
         }
+        setFromVec(vec);
         return this;
     }
     
@@ -203,5 +238,25 @@ public class JSArray extends JS {
         return ret;
     }
     
-     public String coerceToString() { return JS.toString(join(",")); }
+    protected Vec toVec() {
+        int count = size();
+        Vec vec = new Vec();
+        vec.setSize(count);
+        for(int i=0;i<count;i++) {
+            Object o = getNode(i);
+            vec.setElementAt(o == NULL ? null : o,i);
+        }
+        return vec;
+    }
+    
+    protected void setFromVec(Vec vec) {
+        int count = vec.size();
+        clear();
+        for(int i=0;i<count;i++) {
+            Object o = vec.elementAt(i);
+            insertNode(i,o==null ? NULL : o);
+        }
+    }
+    
+    public String coerceToString() { return JS.toString(join(",")); }
 }
index 2cc9591..b3bfb98 100644 (file)
@@ -8,11 +8,19 @@ import java.util.*;
 
 /** An exception which can be thrown and caught by JavaScript code */
 public class JSExn extends Exception { 
+    private Vec backtrace = new Vec();
     private Object js = null; 
     public JSExn(Object js) { this.js = js; } 
-    public String toString() { return "JSExn: " + js; }
+    public String toString() { return "JSExn: " + JS.toString(js); }
     public String getMessage() { return toString(); }
     public Object getObject() { return js; } 
+    public void addBacktrace(String sourceName, int lineNo) { backtrace.addElement(sourceName + ":" + lineNo); }
+    public String backtrace() {
+        StringBuffer sb = new StringBuffer(1024);
+        for(int i=0;i<backtrace.size();i++)
+            sb.append("    at " + (String) backtrace.elementAt(i) + "\n");
+        return sb.toString();
+    }
 } 
 
 /** should only be used for failed coercions */
index 8e5c92a..2f24e9a 100644 (file)
@@ -105,6 +105,7 @@ public class JSFunction extends JS implements ByteCodes, Tokens, org.xwt.Schedul
     // Debugging //////////////////////////////////////////////////////////////////////
 
     public String toString() { return "JSFunction [" + sourceName + ":" + firstLine + "]"; }
+
     public String dump() {
         StringBuffer sb = new StringBuffer(1024);
         sb.append("\n" + sourceName + ": " + firstLine + "\n");
index 3bb5ab9..0994a36 100644 (file)
@@ -153,7 +153,7 @@ public class JSRegexp extends JS {
     public static Object stringReplace(Object o, Object arg0, Object arg1) throws JSExn {
         String s = o.toString();
         RE re;
-        JS replaceFunc = null;
+        JSFunction replaceFunc = null;
         String replaceString = null;
         JSRegexp regexp = null;
         if(arg0 instanceof JSRegexp) {
@@ -162,10 +162,10 @@ public class JSRegexp extends JS {
         } else {
             re = newRE(arg0.toString(),0);
         }
-        if(arg1 instanceof JS)
-            replaceFunc = (JS) arg1;
+        if(arg1 instanceof JSFunction)
+            replaceFunc = (JSFunction) arg1;
         else
-            replaceString = arg1.toString();
+            replaceString = JS.toString(arg1.toString());
         REMatch[] matches;
         if(regexp != null && regexp.global) {
             matches = re.getAllMatches(s);
index 3e797f3..2f35de6 100644 (file)
@@ -27,6 +27,12 @@ public class JSScope extends JS {
         if (parentScope != null && !has(key)) parentScope.put(key, val);
         else super.put(key, val == null ? NULL_PLACEHOLDER : val);
     }
+    
+    public JSScope top() { 
+        JSScope s = this;
+        while(s.parentScope != null) s = s.parentScope;
+        return s;
+    }
 
     public static class Global extends JSScope {
         private final static Double NaN = new Double(Double.NaN);
index b67b59c..4bf273d 100644 (file)
@@ -336,17 +336,23 @@ class Parser extends Lexer implements ByteCodes {
         switch(tok) {
         case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH:
         case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: case ASSIGN_ADD: case ASSIGN_SUB: {
-            b.add(parserLine, GET_PRESERVE);
+            if (tok != ASSIGN_ADD && tok != ASSIGN_SUB) b.add(parserLine, GET_PRESERVE);
+            
             startExpr(b,  precedence[tok]);
+            
             int size = b.size;
             if (tok == ASSIGN_ADD || tok == ASSIGN_SUB) {
                 b.add(parserLine, tok);
+                b.add(parserLine, GET);
+                b.add(parserLine, SWAP);
             }
+            
             // tok-1 is always s/^ASSIGN_// (0 is BITOR, 1 is ASSIGN_BITOR, etc) 
             b.add(parserLine, tok - 1, tok-1==ADD ? JS.N(2) : null);
             b.add(parserLine, PUT);
             b.add(parserLine, SWAP);
             b.add(parserLine, POP);
+            
             if (tok == ASSIGN_ADD || tok == ASSIGN_SUB) b.set(size, tok, JS.N(b.size - size));
             break;
         }
index 1e299e5..8437128 100644 (file)
@@ -27,10 +27,10 @@ class Trap {
     private static final JSFunction getInvoker = new JSFunction("getInvoker", 0, null);
 
     static {
-        putInvoker.add(-1, ByteCodes.PUT, null);
-        putInvoker.add(-1, Tokens.RETURN, null);
-        getInvoker.add(-1, ByteCodes.GET, null);
-        getInvoker.add(-1, Tokens.RETURN, null);
+        putInvoker.add(1, ByteCodes.PUT, null);
+        putInvoker.add(2, Tokens.RETURN, null);
+        getInvoker.add(1, ByteCodes.GET, null);
+        getInvoker.add(2, Tokens.RETURN, null);
     }
     
     void invoke(Object value) throws JSExn {
index 9d39ac5..3c9d31a 100644 (file)
@@ -900,7 +900,7 @@ public abstract class XML
         public int getType() { return this.type; }
         public int getLine() { return this.line; }
         public int getCol()  { return this.col;  }
-        public String getMessage() { return this.error; }
+        public String getMessage() { return this.error + (line >= 0 && col >= 0 ? " at " + line + ":" + col: ""); }
     }