From: brian Date: Tue, 6 Jul 2004 07:59:02 +0000 (+0000) Subject: more new js api fixes and cleanup X-Git-Url: http://git.megacz.com/?p=org.ibex.core.git;a=commitdiff_plain;h=4c96b0f7e40e4689434c46e4300cc9bef9d0fab9 more new js api fixes and cleanup darcs-hash:20040706075902-24bed-5e89f496b7e07452d6efed7ced9303c7895cf1b6.gz --- diff --git a/src/org/ibex/js/Interpreter.java b/src/org/ibex/js/Interpreter.java index cdc1ef0..73de955 100644 --- a/src/org/ibex/js/Interpreter.java +++ b/src/org/ibex/js/Interpreter.java @@ -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= 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; } diff --git a/src/org/ibex/js/JS.java b/src/org/ibex/js/JS.java index 4928101..d8d0f33 100644 --- a/src/org/ibex/js/JS.java +++ b/src/org/ibex/js/JS.java @@ -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(); } } diff --git a/src/org/ibex/js/JSExn.java b/src/org/ibex/js/JSExn.java index e01b769..1301199 100644 --- a/src/org/ibex/js/JSExn.java +++ b/src/org/ibex/js/JSExn.java @@ -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); } } } diff --git a/src/org/ibex/js/JSNumber.java b/src/org/ibex/js/JSNumber.java index 16c6836..8137cbc 100644 --- a/src/org/ibex/js/JSNumber.java +++ b/src/org/ibex/js/JSNumber.java @@ -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; } diff --git a/src/org/ibex/js/JSScope.java b/src/org/ibex/js/JSScope.java index 51cc33a..f7c6355 100644 --- a/src/org/ibex/js/JSScope.java +++ b/src/org/ibex/js/JSScope.java @@ -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