From 9bff7ae2a6824ac83fb1e0ccd455ac3b1277ef3c Mon Sep 17 00:00:00 2001 From: brian Date: Tue, 6 Jul 2004 06:06:33 +0000 Subject: [PATCH] fixes and additions to new js api darcs-hash:20040706060633-24bed-bddfa01d2211b34a473dca5586ec4c72efcb2e72.gz --- src/org/ibex/js/Interpreter.java | 32 ++++++++--- src/org/ibex/js/JS.java | 6 ++ src/org/ibex/js/JSArray.java | 8 +-- src/org/ibex/js/JSNumber.java | 63 +++++++++++++++++++++ src/org/ibex/js/JSPrimitive.java | 114 ++++++++++++++++++++++++++++++++++++++ src/org/ibex/js/JSRegexp.java | 3 +- src/org/ibex/js/JSString.java | 32 +++++++++++ src/org/ibex/js/Stream.java | 6 +- 8 files changed, 250 insertions(+), 14 deletions(-) create mode 100644 src/org/ibex/js/JSNumber.java create mode 100644 src/org/ibex/js/JSPrimitive.java create mode 100644 src/org/ibex/js/JSString.java diff --git a/src/org/ibex/js/Interpreter.java b/src/org/ibex/js/Interpreter.java index 2d88042..cdc1ef0 100644 --- a/src/org/ibex/js/Interpreter.java +++ b/src/org/ibex/js/Interpreter.java @@ -232,7 +232,7 @@ class Interpreter implements ByteCodes, Tokens { 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; } @@ -250,7 +250,7 @@ class Interpreter implements ByteCodes, Tokens { 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 { @@ -287,7 +287,7 @@ class Interpreter implements ByteCodes, Tokens { 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; @@ -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; - scope = new JSScope(f.parentScope); + scope = new TrapScope(f.parentScope,target,f,key); pc = -1; break; } else { @@ -325,9 +325,9 @@ class Interpreter implements ByteCodes, Tokens { 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(); @@ -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 } + 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 ////////////////////////////////////////////////////////////////////// diff --git a/src/org/ibex/js/JS.java b/src/org/ibex/js/JS.java index f9bd66b..4928101 100644 --- a/src/org/ibex/js/JS.java +++ b/src/org/ibex/js/JS.java @@ -72,6 +72,12 @@ public abstract class JS { 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 { } diff --git a/src/org/ibex/js/JSArray.java b/src/org/ibex/js/JSArray.java index d9ffad1..305a5de 100644 --- a/src/org/ibex/js/JSArray.java +++ b/src/org/ibex/js/JSArray.java @@ -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); - 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); @@ -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); - 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(); } diff --git a/src/org/ibex/js/JSNumber.java b/src/org/ibex/js/JSNumber.java new file mode 100644 index 0000000..16c6836 --- /dev/null +++ b/src/org/ibex/js/JSNumber.java @@ -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 index 0000000..1b574ea --- /dev/null +++ b/src/org/ibex/js/JSPrimitive.java @@ -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= 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_); + } +} diff --git a/src/org/ibex/js/JSRegexp.java b/src/org/ibex/js/JSRegexp.java index cb2c89d..e083902 100644 --- a/src/org/ibex/js/JSRegexp.java +++ b/src/org/ibex/js/JSRegexp.java @@ -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(); diff --git a/src/org/ibex/js/JSString.java b/src/org/ibex/js/JSString.java new file mode 100644 index 0000000..1484e54 --- /dev/null +++ b/src/org/ibex/js/JSString.java @@ -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; } +} diff --git a/src/org/ibex/js/Stream.java b/src/org/ibex/js/Stream.java index 2344e76..e69c28c 100644 --- a/src/org/ibex/js/Stream.java +++ b/src/org/ibex/js/Stream.java @@ -17,12 +17,14 @@ public abstract class Stream extends JS implements JS.Cloneable { // 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); - 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; -- 1.7.10.4