fixes and additions to new js api
authorbrian <brian@brianweb.net>
Tue, 6 Jul 2004 06:06:33 +0000 (06:06 +0000)
committerbrian <brian@brianweb.net>
Tue, 6 Jul 2004 06:06:33 +0000 (06:06 +0000)
darcs-hash:20040706060633-24bed-bddfa01d2211b34a473dca5586ec4c72efcb2e72.gz

src/org/ibex/js/Interpreter.java
src/org/ibex/js/JS.java
src/org/ibex/js/JSArray.java
src/org/ibex/js/JSNumber.java [new file with mode: 0644]
src/org/ibex/js/JSPrimitive.java [new file with mode: 0644]
src/org/ibex/js/JSRegexp.java
src/org/ibex/js/JSString.java [new file with mode: 0644]
src/org/ibex/js/Stream.java

index 2d88042..cdc1ef0 100644 (file)
@@ -232,7 +232,7 @@ class Interpreter implements ByteCodes, Tokens {
                         key = tm.key;
                         tm.cascadeHappened = true;
                         t = tm.t;
                         key = tm.key;
                         tm.cascadeHappened = true;
                         t = tm.t;
-                        if(t.readTrap()) throw new JSExn("can't put to cascade in a read trap");
+                        if(t.readTrap()) throw new JSExn("can't do a write cascade in a read trap");
                         t = t.next;
                         while(t != null && t.readTrap()) t = t.next;
                     }
                         t = t.next;
                         while(t != null && t.readTrap()) t = t.next;
                     }
@@ -250,7 +250,7 @@ class Interpreter implements ByteCodes, Tokens {
                     args.addElement(val);
                     stack.push(args);
                     f = t.f;
                     args.addElement(val);
                     stack.push(args);
                     f = t.f;
-                    scope = new JSScope(f.parentScope);
+                    scope = new TrapScope(f.parentScope,target,f,key);
                     pc = -1;
                     break;
                 } else {
                     pc = -1;
                     break;
                 } else {
@@ -287,7 +287,7 @@ class Interpreter implements ByteCodes, Tokens {
                         target = tm.trapee;
                         key = tm.key;
                         t = tm.t;
                         target = tm.trapee;
                         key = tm.key;
                         t = tm.t;
-                        if(t.writeTrap()) throw new JSExn("can't do a write cascade in a read trap");
+                        if(t.writeTrap()) throw new JSExn("can't do a read cascade in a write trap");
                         t = t.next;
                         while(t != null && t.writeTrap()) t = t.next;
                         if(t != null) tm.cascadeHappened = true;
                         t = t.next;
                         while(t != null && t.writeTrap()) t = t.next;
                         if(t != null) tm.cascadeHappened = true;
@@ -302,7 +302,7 @@ class Interpreter implements ByteCodes, Tokens {
                     stack.push(new TrapMarker(this,t,(JS)target,key,null));
                     stack.push(new JSArray());
                     f = t.f;
                     stack.push(new TrapMarker(this,t,(JS)target,key,null));
                     stack.push(new JSArray());
                     f = t.f;
-                    scope = new JSScope(f.parentScope);
+                    scope = new TrapScope(f.parentScope,target,f,key);
                     pc = -1;
                     break;
                 } else {
                     pc = -1;
                     break;
                 } else {
@@ -325,9 +325,9 @@ class Interpreter implements ByteCodes, Tokens {
                         method = stack.pop();
                         object = stack.pop();
                     } else if (object == null) {
                         method = stack.pop();
                         object = stack.pop();
                     } else if (object == null) {
-                        Object name = stack.pop();
-                        stack.pop();
-                        throw new JSExn("function '"+name+"' not found");
+                        method = stack.pop();
+                        object = stack.pop();
+                        throw new JSExn("function '"+JS.debugToString(method)+"' not found in " + object.getClass().getName());
                     } else {
                         stack.pop();
                         stack.pop();
                     } else {
                         stack.pop();
                         stack.pop();
@@ -609,6 +609,24 @@ class Interpreter implements ByteCodes, Tokens {
         public FinallyData(JSExn exn) { this.exn = exn; this.op = -1; this.arg = null; } // Just throw this exn
     }
 
         public FinallyData(JSExn exn) { this.exn = exn; this.op = -1; this.arg = null; } // Just throw this exn
     }
 
+    static class TrapScope extends JSScope {
+        JS trapee;
+        JS callee;
+        JS trapname;
+        public TrapScope(JSScope parent, JS trapee, JS callee, JS trapname) {
+            super(parent); this.trapee = trapee; this.callee = callee; this.trapname = trapname;
+        }
+        public JS get(JS key) throws JSExn {
+            if(JS.isString(key)) {
+                //#switch(JS.toString(key))
+                case "trapee": return trapee;
+                case "callee": return callee;
+                case "trapname": return trapname;
+                //#end
+            }
+            return super.get(key);
+        }
+    }
 
     // Operations on Primitives //////////////////////////////////////////////////////////////////////
 
 
     // Operations on Primitives //////////////////////////////////////////////////////////////////////
 
index f9bd66b..4928101 100644 (file)
@@ -72,6 +72,12 @@ public abstract class JS {
         public final int indexNode(Object o) { return bt().indexNode(o); }
     }
     
         public final int indexNode(Object o) { return bt().indexNode(o); }
     }
     
+    // FEATURE: JS.StringKeys
+    /* public static StringKeys extends JS {
+        public JS get(JS key) { return JS.isString(key) ? get(JS.toString(key) : null; }
+        ...
+    */
+    
     JS _unclone() { return this; }
     
     public interface Cloneable { }
     JS _unclone() { return this; }
     
     public interface Cloneable { }
index d9ffad1..305a5de 100644 (file)
@@ -127,8 +127,8 @@ public class JSArray extends JS.BT {
     public final int length() { return size(); }
     public final JS elementAt(int i) { 
         if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
     public final int length() { return size(); }
     public final JS elementAt(int i) { 
         if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
-        JS o = (JS) getNode(i);
-        return o == NULL ? null : o;
+        Object o = getNode(i);
+        return o == NULL ? (JS)null : (JS)o;
     }
     public final void addElement(JS o) { 
         insertNode(size(),o==null ? NULL : o);
     }
     public final void addElement(JS o) { 
         insertNode(size(),o==null ? NULL : o);
@@ -143,8 +143,8 @@ public class JSArray extends JS.BT {
     }
     public final JS removeElementAt(int i) {
         if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
     }
     public final JS removeElementAt(int i) {
         if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
-        JS o = (JS) deleteNode(i);
-        return o == NULL ? null : o;
+        Object o = deleteNode(i);
+        return o == NULL ? (JS)null : (JS)o;
     }
     
     public final int size() { return treeSize(); }
     }
     
     public final int size() { return treeSize(); }
diff --git a/src/org/ibex/js/JSNumber.java b/src/org/ibex/js/JSNumber.java
new file mode 100644 (file)
index 0000000..16c6836
--- /dev/null
@@ -0,0 +1,63 @@
+package org.ibex.js;
+
+abstract class JSNumber extends JSPrimitive {
+    boolean jsequals(JS o) {
+        if(o == this) return true;
+        if(o instanceof JSNumber) {
+            JSNumber n = (JSNumber) o;
+            if(this instanceof D || n instanceof D) return n.toDouble() == toDouble();
+            return n.toLong() == toLong();
+        } else if(o instanceof JSString) {
+            String s = ((JSString)o).s.trim();
+            try {
+                if(this instanceof D || s.indexOf('.') != -1) return Double.parseDouble(s) == toDouble();
+                return Long.parseLong(s) == toLong();
+            } catch(NumberFormatException e) {
+                return false;
+            }
+        } else {
+            return false;
+        }
+    }
+    // FEATURE: Better hash function? (if d != (int) d then do something double specific)
+    public int hashCode() { return toInt(); }
+    
+    abstract int toInt();
+    long toLong() { return toInt(); }
+    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; }
+        int toInt() { return i; }
+        public String coerceToString() { return Integer.toString(i); }
+    }
+    
+    final static class L extends JSNumber {
+        private final long l;
+        L(long l) { this.l = l; }
+        int toInt() { return (int) l; }
+        long toLong() { return l; }
+        public String coerceToString() { return Long.toString(l); }
+    }
+    
+    final static class D extends JSNumber {
+        private final double d;
+        D(double d) { this.d = d; }
+        int toInt() { return (int) d; }
+        long toLong() { return (long) d; }
+        double toDouble()  { return d; }
+        boolean toBoolean() { return d == d && d != 0.0; }
+        public String coerceToString() { return d == (long) d ? Long.toString((long)d) : Double.toString(d); }
+    }
+    
+    final static class B extends JSNumber {
+        private final boolean b;
+        B(boolean b) { this.b = b; }
+        int toInt() { return b ? 1 : 0; }
+        public String coerceToString() { return b ? "true" : "false"; }
+    }
+}
diff --git a/src/org/ibex/js/JSPrimitive.java b/src/org/ibex/js/JSPrimitive.java
new file mode 100644 (file)
index 0000000..1b574ea
--- /dev/null
@@ -0,0 +1,114 @@
+package org.ibex.js;
+
+class JSPrimitive extends JS {
+    public JS callMethod(JS method_, JS arg0, JS arg1, JS arg2, JS[] rest, int alength) throws JSExn {
+        if(!JS.isString(method_)) return super.callMethod(method_,arg0,arg1,arg2,rest,alength);
+        String method = JS.toString(method_);
+        
+        //#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": return this instanceof JSString ? this : JS.S(JS.toString(this));
+        //#end
+            
+        String s = coerceToString();
+        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 JS.S(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 JS.S("");
+            return JS.S(s.substring(start,start+len));
+        }
+        case "charAt": {
+            int p = alength >= 1 ? JS.toInt(arg0) : 0;
+            if (p < 0 || p >= slength) return JS.S("");
+            return JS.S(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 JS.S(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(this,arg0);
+        case "replace": return JSRegexp.stringReplace(this,arg0,arg1);
+        case "search": return JSRegexp.stringSearch(this,arg0);
+        case "split": return JSRegexp.stringSplit(this,arg0,arg1,alength);
+        case "toLowerCase": return JS.S(s.toLowerCase());
+        case "toUpperCase": return JS.S(s.toUpperCase());
+        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 JS.S("");
+            return JS.S(s.substring(a,b));
+        }
+        //#end
+        return super.callMethod(method_,arg0,arg1,arg2,rest,alength);
+    }
+    
+    public JS get(JS key_) throws JSExn {
+        if(!JS.isString(key_)) return super.get(key_);
+        String key = JS.toString(key_);
+        //#switch(key)
+        case "length": return JS.N(JS.toString(this).length());
+        case "substring": return METHOD;
+        case "charAt": return METHOD;
+        case "charCodeAt": return METHOD;
+        case "concat": return METHOD;
+        case "indexOf": return METHOD;
+        case "lastIndexOf": return METHOD; 
+        case "match": return METHOD;
+        case "replace": return METHOD;
+        case "search": return METHOD;
+        case "slice": return METHOD;
+        case "split": return METHOD;
+        case "toLowerCase": return METHOD; 
+        case "toUpperCase": return METHOD; 
+        case "toString": return METHOD; 
+        case "substr": return METHOD;  
+        case "toPrecision": return METHOD;
+        case "toExponential": return METHOD;
+        case "toFixed": return METHOD;
+        //#end
+        return super.get(key_);
+    }
+}
index cb2c89d..e083902 100644 (file)
@@ -283,7 +283,8 @@ public class JSRegexp extends JS {
     }
                     
     
     }
                     
     
-    public static JS stringSplit(String s, JS arg0, JS arg1, int nargs) throws JSExn {
+    public static JS stringSplit(JS s_, JS arg0, JS arg1, int nargs) throws JSExn {
+        String s = JS.toString(s_);
         int limit = nargs < 2 ? Integer.MAX_VALUE : JS.toInt(arg1);
         if(limit < 0) limit = Integer.MAX_VALUE;
         if(limit == 0) return new JSArray();
         int limit = nargs < 2 ? Integer.MAX_VALUE : JS.toInt(arg1);
         if(limit < 0) limit = Integer.MAX_VALUE;
         if(limit == 0) return new JSArray();
diff --git a/src/org/ibex/js/JSString.java b/src/org/ibex/js/JSString.java
new file mode 100644 (file)
index 0000000..1484e54
--- /dev/null
@@ -0,0 +1,32 @@
+package org.ibex.js;
+
+import org.ibex.util.*;
+
+final class JSString extends JSPrimitive {
+    final String s;
+    public JSString(String s) { this.s = s; }
+    public int hashCode() { return s.hashCode(); }
+    
+    public boolean jsequals(JS o) {
+        if(o == this) return true;
+        if(o instanceof JSString) {
+            return ((JSString)o).s.equals(s);
+        } else if(o instanceof JSNumber) {
+            return o.jsequals(this);
+        } else {
+            return false;
+        }
+    }
+    
+    int length() { return s.length(); }
+    
+    private static Hash internHash = new Hash();
+    static synchronized JS intern(String s) {
+        JS js = (JS)internHash.get(s);
+        if(js == null) internHash.put(s,js = new JSString(s));
+        return js;
+    }
+    JS intern() { return intern(s); }
+    
+    String coerceToString() { return s; }
+}
index 2344e76..e69c28c 100644 (file)
@@ -17,12 +17,14 @@ public abstract class Stream extends JS implements JS.Cloneable {
 
     // Public Interface //////////////////////////////////////////////////////////////////////////////
 
 
     // Public Interface //////////////////////////////////////////////////////////////////////////////
 
+    // FIXME: This should be in JS, "everything has a stream"
     public static InputStream getInputStream(JS js) throws IOException { return ((Stream)js.unclone()).getInputStream();}
     public static class NotCacheableException extends Exception { }
 
     private Cache getCache = new Cache(100);
     public static InputStream getInputStream(JS js) throws IOException { return ((Stream)js.unclone()).getInputStream();}
     public static class NotCacheableException extends Exception { }
 
     private Cache getCache = new Cache(100);
-    protected JS _get(Object key) { return null; }
-    public final JS get(JS key) {
+    // FEATURE: Mandate that Streams use only String keys?
+    protected JS _get(JS key) throws JSExn { return null; }
+    public final JS get(JS key) throws JSExn {
         JS ret = (JS) getCache.get(key);
         if (ret == null) getCache.put(key, ret = _get(key));
         return ret;
         JS ret = (JS) getCache.get(key);
         if (ret == null) getCache.put(key, ret = _get(key));
         return ret;