From 4444c6057bb0cee5d2ae5a55b3045fd8eb790295 Mon Sep 17 00:00:00 2001 From: megacz Date: Fri, 30 Jan 2004 07:40:53 +0000 Subject: [PATCH] 2003/11/03 06:32:55 darcs-hash:20040130074053-2ba56-c8bfd5c149f765d5439fddfaaeca5a245cfb719f.gz --- src/org/xwt/HTTP.java | 2 +- src/org/xwt/Res.java | 36 ++- src/org/xwt/Template.java | 14 +- src/org/xwt/Trap.java | 12 +- src/org/xwt/XMLRPC.java | 24 +- src/org/xwt/XWT.java | 15 +- src/org/xwt/builtin/splash.xwt | 7 +- .../{CompiledFunctionImpl.java => Function.java} | 243 ++++++++------------ src/org/xwt/js/JS.java | 103 +++++---- 9 files changed, 208 insertions(+), 248 deletions(-) rename src/org/xwt/js/{CompiledFunctionImpl.java => Function.java} (73%) diff --git a/src/org/xwt/HTTP.java b/src/org/xwt/HTTP.java index 7295f1f..eca310f 100644 --- a/src/org/xwt/HTTP.java +++ b/src/org/xwt/HTTP.java @@ -780,7 +780,7 @@ public class HTTP { if (Log.on) Log.log(Proxy.class, script); } - JS.CompiledFunction scr = JS.parse("PAC script at " + url, 0, new StringReader(script)); + Function scr = JS.parse("PAC script at " + url, 0, new StringReader(script)); // FIXME /* scr.call(new JS.Array(), proxyAutoConfigRootScope); diff --git a/src/org/xwt/Res.java b/src/org/xwt/Res.java index 399476b..954f290 100644 --- a/src/org/xwt/Res.java +++ b/src/org/xwt/Res.java @@ -80,7 +80,7 @@ public abstract class Res extends JS { } if (url.startsWith("http://")) return new HTTP(url); if (url.startsWith("https://")) return new HTTP(url); - if (url.startsWith("cab:")) return new CAB(stringToRes(url.substring(4))); + if (url.startsWith("cab:")) return new Cab(stringToRes(url.substring(4))); if (url.startsWith("data:")) return new ByteArray(Base64.decode(url.substring(5))); if (url.startsWith("utf8:")) return new ByteArray(url.substring(5).getBytes()); throw new JS.Exn("invalid resource specifier " + url); @@ -145,6 +145,18 @@ public abstract class Res extends JS { } } + /** "unwrap" a Cab archive */ + public static class Cab extends Res { + private Res parent; + Cab(Res parent) { this.parent = parent; } + public String getDescriptiveName() { return "cab[" + parent.getDescriptiveName() + "]"; } + public InputStream getInputStream(String path) throws IOException { + // FIXME: knownlength + if (path.startsWith("/")) path = path.substring(1); + return new org.xwt.translators.MSPack(parent.getInputStream()).getInputStream(path); + } + } + /** the Builtin resource */ public static class Builtin extends Res { public Builtin() { }; @@ -204,8 +216,8 @@ public abstract class Res extends JS { /** shadow resource which replaces the graft */ public static class ProgressWatcher extends Res { final Res watchee; - JS.CompiledFunction callback; - ProgressWatcher(Res watchee, JS.CompiledFunction callback) { this.watchee = watchee; this.callback = callback; } + Function callback; + ProgressWatcher(Res watchee, Function callback) { this.watchee = watchee; this.callback = callback; } public String getDescriptiveName() { return watchee.getDescriptiveName(); } public InputStream getInputStream(String s) throws IOException { final InputStream is = watchee.getInputStream(s); @@ -232,24 +244,6 @@ public abstract class Res extends JS { } } - /** unpacks a Microsoft CAB file (possibly embedded in another file; we scan for 'MSCF' */ - public static class CAB extends Res { - private Res parent; - CAB(Res parent) { this.parent = parent; } - private int swap_endian(int i) { - return ((i & 0xff) << 24) | ((i & 0xff00) << 8) | ((i & 0xff0000) >>> 8) | (i >>> 24); - } - public InputStream getInputStream(String path) throws IOException { - try { - return org.xwt.util.CAB.getFileInputStream(parent.getInputStream(), 2, path); - } catch (EOFException eof) { - throw new JS.Exn("MSCF header tag not found in file"); - } catch (IOException ioe) { - throw new JS.Exn("IOException while reading file"); - } - } - } - public Object callMethod(Object method, Array args, boolean checkOnly) throws JS.Exn { if (method.equals("getUTF")) { if (checkOnly) return Boolean.TRUE; diff --git a/src/org/xwt/Template.java b/src/org/xwt/Template.java index bf88c4f..a81f150 100644 --- a/src/org/xwt/Template.java +++ b/src/org/xwt/Template.java @@ -32,7 +32,7 @@ public class Template { private Vec children = new Vec(); ///< during XML parsing, this holds the list of currently-parsed children; null otherwise private int numunits = -1; ///< see numUnits(); -1 means that this value has not yet been computed - private JS.CompiledFunction script = null; ///< the script on this node + private Function script = null; ///< the script on this node private String fileName = "unknown"; ///< the filename this node came from; used only for debugging private Vec preapply = new Vec(); ///< templates that should be preapplied (in the order of application) @@ -40,7 +40,7 @@ public class Template { // Instance Members that are only meaningful on root Template ////////////////////////////////////// private JS.Scope staticScope = null; ///< the scope in which the static block is executed - private JS.CompiledFunction staticscript = null; ///< the script on the static node of this template, null already performed + private Function staticscript = null; ///< the script on the static node of this template, null already performed // Only used during parsing ///////////////////////////////////////////////////////////////// @@ -95,9 +95,9 @@ public class Template { JS.Scope getStatic() { if (staticScope == null) staticScope = new JS.Scope(null); if (staticscript == null) return staticScope; - JS.CompiledFunction temp = staticscript; + Function temp = staticscript; staticscript = null; - new JS.Context(temp, staticScope, null).resume(null); + new JS.Context(temp, staticScope).resume(); return staticScope; } @@ -126,7 +126,7 @@ public class Template { b.put(b.numChildren(), kid); } - if (script != null) new JS.Context(script, pis, null).resume(null); + if (script != null) new JS.Context(script, pis).resume(); for(int i=0; keys != null && i= ((CompiledFunctionImpl)cx.f).size) return -1; - return ((CompiledFunctionImpl)cx.f).line[cx.pc]; - } - // Invoking the Bytecode /////////////////////////////////////////////////////// /** returns false if the thread has been paused */ - static Object eval(final Context cx, Object pushFirst) throws JS.Exn { - cx.stack.push(pushFirst); + static Object eval(final Context cx) throws JS.Exn { OUTER: for(;; cx.pc++) { try { - if (cx.f == null) return cx.stack.pop(); - if (cx.pc >= ((CompiledFunctionImpl)cx.f).size) return cx.stack.pop(); - String label = null; - int curOP = cx.f.op[cx.pc]; - Object curArg = cx.f.arg[cx.pc]; + if (cx.f == null || cx.pc >= cx.f.size) return cx.stack.pop(); + int op = cx.f.op[cx.pc]; + Object arg = cx.f.arg[cx.pc]; Object returnedFromJava = null; boolean checkReturnedFromJava = false; - if(curOP == FINALLY_DONE) { + if(op == FINALLY_DONE) { FinallyData fd = (FinallyData) cx.stack.pop(); if(fd == null) continue OUTER; // NOP - curOP = fd.op; - curArg = fd.arg; + op = fd.op; + arg = fd.arg; } - switch(curOP) { - case LITERAL: cx.stack.push(curArg); break; + switch(op) { + case LITERAL: cx.stack.push(arg); break; case OBJECT: cx.stack.push(new JS.Obj()); break; - case ARRAY: cx.stack.push(new JS.Array(JS.toNumber(curArg).intValue())); break; - case DECLARE: cx.scope.declare((String)(curArg==null ? cx.stack.peek() : curArg)); if(curArg != null) cx.stack.push(curArg); break; + case ARRAY: cx.stack.push(new JS.Array(JS.toNumber(arg).intValue())); break; + case DECLARE: cx.scope.declare((String)(arg==null ? cx.stack.peek() : arg)); if(arg != null) cx.stack.push(arg); break; case TOPSCOPE: cx.stack.push(cx.scope); break; - case JT: if (JS.toBoolean(cx.stack.pop())) cx.pc += JS.toNumber(curArg).intValue() - 1; break; - case JF: if (!JS.toBoolean(cx.stack.pop())) cx.pc += JS.toNumber(curArg).intValue() - 1; break; - case JMP: cx.pc += JS.toNumber(curArg).intValue() - 1; break; + case JT: if (JS.toBoolean(cx.stack.pop())) cx.pc += JS.toNumber(arg).intValue() - 1; break; + case JF: if (!JS.toBoolean(cx.stack.pop())) cx.pc += JS.toNumber(arg).intValue() - 1; break; + case JMP: cx.pc += JS.toNumber(arg).intValue() - 1; break; case POP: cx.stack.pop(); break; case SWAP: { Object o1 = cx.stack.pop(); Object o2 = cx.stack.pop(); cx.stack.push(o1); cx.stack.push(o2); break; } case DUP: cx.stack.push(cx.stack.peek()); break; @@ -131,6 +114,8 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { case ASSERT: if (!JS.toBoolean(cx.stack.pop())) throw je("assertion failed"); break; case BITNOT: cx.stack.push(new Long(~JS.toLong(cx.stack.pop()))); break; case BANG: cx.stack.push(new Boolean(!JS.toBoolean(cx.stack.pop()))); break; + case NEWFUNCTION: cx.stack.push(((Function)arg).cloneWithNewParentScope(cx.scope)); break; + case LABEL: break; case TYPEOF: { Object o = cx.stack.pop(); @@ -143,15 +128,6 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { break; } - case NEWFUNCTION: { - try { - cx.stack.push(((CompiledFunctionImpl)curArg).cloneWithNewParentScope(cx.scope)); - } catch (IOException e) { - throw new Error("this should never happen"); - } - break; - } - case PUSHKEYS: { Object o = cx.stack.peek(); Object[] keys = ((JS)o).keys(); @@ -162,12 +138,11 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { break; } - case LABEL: break; - case LOOP: { - cx.stack.push(new LoopMarker(cx.pc, cx.pc > 0 && cx.f.op[cx.pc - 1] == LABEL ? (String)cx.f.arg[cx.pc - 1] : (String)null, cx.scope)); + case LOOP: + cx.stack.push(new LoopMarker(cx.pc, cx.pc > 0 && cx.f.op[cx.pc - 1] == LABEL ? + (String)cx.f.arg[cx.pc - 1] : (String)null, cx.scope)); cx.stack.push(Boolean.TRUE); break; - } case BREAK: case CONTINUE: @@ -176,26 +151,27 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { if (o instanceof CallMarker) ee("break or continue not within a loop"); if (o instanceof TryMarker) { if(((TryMarker)o).finallyLoc < 0) continue; // no finally block, keep going - cx.stack.push(new FinallyData(curOP, curArg)); + cx.stack.push(new FinallyData(op, arg)); cx.scope = ((TryMarker)o).scope; cx.pc = ((TryMarker)o).finallyLoc - 1; continue OUTER; } if (o instanceof LoopMarker) { - if (curArg == null || curArg.equals(((LoopMarker)o).label)) { + if (arg == null || arg.equals(((LoopMarker)o).label)) { int loopInstructionLocation = ((LoopMarker)o).location; int endOfLoop = ((Integer)cx.f.arg[loopInstructionLocation]).intValue() + loopInstructionLocation; cx.scope = ((LoopMarker)o).scope; - if (curOP == CONTINUE) { cx.stack.push(o); cx.stack.push(Boolean.FALSE); } - cx.pc = curOP == BREAK ? endOfLoop - 1 : loopInstructionLocation; + if (op == CONTINUE) { cx.stack.push(o); cx.stack.push(Boolean.FALSE); } + cx.pc = op == BREAK ? endOfLoop - 1 : loopInstructionLocation; continue OUTER; } } } - throw new Error("CONTINUE/BREAK invoked but couldn't find a LoopMarker at " + ((CompiledFunctionImpl)cx.f).sourceName + ":" + getLine(cx)); + throw new Error("CONTINUE/BREAK invoked but couldn't find LoopMarker at " + + cx.getSourceName() + ":" + cx.getLine()); case TRY: { - int[] jmps = (int[]) curArg; + int[] jmps = (int[]) arg; // jmps[0] is how far away the catch block is, jmps[1] is how far away the finally block is // each can be < 0 if the specified block does not exist cx.stack.push(new TryMarker(jmps[0] < 0 ? -1 : cx.pc + jmps[0], jmps[1] < 0 ? -1 : cx.pc + jmps[1], cx.scope)); @@ -213,11 +189,10 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { cx.scope = ((TryMarker)o).scope; cx.pc = ((TryMarker)o).finallyLoc - 1; continue OUTER; - } - if (o instanceof CallMarker) { + } else if (o instanceof CallMarker) { cx.scope = ((CallMarker)o).scope; cx.pc = ((CallMarker)o).pc; - cx.f = (CompiledFunction)((CallMarker)o).f; + cx.f = (Function)((CallMarker)o).f; cx.stack.push(retval); continue OUTER; } @@ -236,19 +211,16 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { if (key == null) throw je("tried to assign \"" + (val==null?"(null)":val.toString()) + "\" to the null key"); returnedFromJava = ((JS)target).put(key, val); - if (returnedFromJava != null) { - checkReturnedFromJava = true; - } else { - cx.stack.push(val); - } + if (returnedFromJava != null) checkReturnedFromJava = true; + else cx.stack.push(val); break; } case GET: case GET_PRESERVE: { Object o, v; - if (curOP == GET) { - v = curArg == null ? cx.stack.pop() : curArg; + if (op == GET) { + v = arg == null ? cx.stack.pop() : arg; o = cx.stack.pop(); } else { v = cx.stack.pop(); @@ -258,67 +230,59 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { Object ret = null; if (o == null) throw je("tried to get property \"" + v + "\" from the null value"); if (v == null) throw je("tried to get the null key from " + o); - if (o instanceof String || o instanceof Number || o instanceof Boolean) + if (o instanceof String || o instanceof Number || o instanceof Boolean) { ret = Internal.getFromPrimitive(o,v); - else if (o instanceof JS) { + cx.stack.push(ret); + break; + } else if (o instanceof JS) { returnedFromJava = ((JS)o).get(v); checkReturnedFromJava = true; break; - } else - throw je("tried to get property " + v + " from a " + o.getClass().getName()); - cx.stack.push(ret); - break; + } + throw je("tried to get property " + v + " from a " + o.getClass().getName()); } - case CALLMETHOD: - case CALL: - case CALL_REVERSED: - { + case CALL: case CALLMETHOD: case CALL_REVERSED: { JS.Array arguments = new JS.Array(); - int numArgs = JS.toNumber(curArg).intValue(); + int numArgs = JS.toNumber(arg).intValue(); arguments.setSize(numArgs); Object o = null; - if (curOP == CALL_REVERSED) o = cx.stack.pop(); + if (op == CALL_REVERSED) o = cx.stack.pop(); for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j); - if (curOP != CALL_REVERSED) o = cx.stack.pop(); + if (op != CALL_REVERSED) o = cx.stack.pop(); if(o == null) throw je("attempted to call null"); Object ret; - if(curOP == CALLMETHOD) { + if(op == CALLMETHOD) { Object method = o; o = cx.stack.pop(); if(o instanceof String || o instanceof Number || o instanceof Boolean) { ret = Internal.callMethodOnPrimitive(o,method,arguments); cx.stack.push(ret); cx.pc += 2; // skip the GET and CALL - break; } else if (o instanceof JS && ((JS)o).callMethod(method, arguments, true) == Boolean.TRUE) { returnedFromJava = ((JS)o).callMethod(method, arguments, false); checkReturnedFromJava = true; cx.pc += 2; // skip the GET and CALL - break; } else { // put the args back on the stack and let the GET followed by CALL happen for(int j=0; j 0) || (curOP == GE && result >= 0))); + cx.stack.push(new Boolean((op == LT && result < 0) || (op == LE && result <= 0) || + (op == GT && result > 0) || (op == GE && result >= 0))); break; } @@ -451,10 +411,10 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { else if (l instanceof Number) ret = JS.toNumber(r).doubleValue() == JS.toNumber(l).doubleValue(); else if (l instanceof String) ret = r != null && l.equals(r.toString()); else ret = l.equals(r); - cx.stack.push(new Boolean(curOP == EQ ? ret : !ret)); break; + cx.stack.push(new Boolean(op == EQ ? ret : !ret)); break; } - default: throw new Error("unknown opcode " + curOP); + default: throw new Error("unknown opcode " + op); } } } @@ -469,7 +429,7 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { cx.stack.push(new CallMarker(cx)); cx.stack.push(((JS.TailCall)returnedFromJava).args); cx.f = ((JS.TailCall)returnedFromJava).func; - cx.scope = new CompiledFunctionImpl.FunctionScope("unknown", cx.f.parentScope); + cx.scope = new Scope(cx.f.parentScope); cx.pc = -1; } else { cx.stack.push(returnedFromJava); @@ -511,9 +471,11 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { } // end for } + // Debugging ////////////////////////////////////////////////////////////////////// - public String toString() { + public String toString() { return "Function [" + sourceName + ":" + firstLine + "]"; } + public String dump() { StringBuffer sb = new StringBuffer(1024); sb.append("\n" + sourceName + ": " + firstLine + "\n"); for (int i=0; i < size; i++) { @@ -537,22 +499,15 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { return sb.toString(); } + // Exception Stuff //////////////////////////////////////////////////////////////// static class EvaluatorException extends RuntimeException { public EvaluatorException(String s) { super(s); } } static EvaluatorException ee(String s) { - throw new EvaluatorException(JS.Context.getSourceName() + ":" + JS.Context.getLine() + " " + s); + throw new EvaluatorException(Context.getSourceName() + ":" + Context.getLine() + " " + s); } static JS.Exn je(String s) { - throw new JS.Exn(JS.Context.getSourceName() + ":" + JS.Context.getLine() + " " + s); - } - - // FunctionScope ///////////////////////////////////////////////////////////////// - - static class FunctionScope extends JS.Scope { - String sourceName; - public FunctionScope(String sourceName, Scope parentScope) { super(parentScope); this.sourceName = sourceName; } - public String getSourceName() { return sourceName; } + throw new JS.Exn(Context.getSourceName() + ":" + Context.getLine() + " " + s); } @@ -561,7 +516,7 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { public static class CallMarker { int pc; Scope scope; - CompiledFunctionImpl f; + Function f; public CallMarker(Context cx) { pc = cx.pc + 1; scope = cx.scope; f = cx.f; } } @@ -596,7 +551,3 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { } } -/** this class exists solely to work around a GCJ bug */ -abstract class JSCallable extends JS.Callable { - public abstract Object call(JS.Array args) throws JS.Exn; -} diff --git a/src/org/xwt/js/JS.java b/src/org/xwt/js/JS.java index 94809b5..ba83b01 100644 --- a/src/org/xwt/js/JS.java +++ b/src/org/xwt/js/JS.java @@ -19,8 +19,8 @@ public abstract class JS { // Public Helper Methods ////////////////////////////////////////////////////////////////////// /** parse and compile a function */ - public static CompiledFunction parse(String sourceName, int firstLine, Reader sourceCode) throws IOException { - return new CompiledFunction(sourceName, firstLine, sourceCode, null); + public static Function parse(String sourceName, int firstLine, Reader sourceCode) throws IOException { + return new Function(sourceName, firstLine, sourceCode, null); } /** coerce an object to a Boolean */ @@ -54,7 +54,8 @@ public abstract class JS { // NOTE: There are about 3 pages of rules in ecma262 about string to number conversions // We aren't even close to following all those rules. We probably never will be. - if (o instanceof String) try { return new Double((String)o); } catch (NumberFormatException e) { return new Double(Double.NaN); } + if (o instanceof String) + try { return new Double((String)o); } catch (NumberFormatException e) { return new Double(Double.NaN); } if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? new Long(1) : new Long(0); if (o instanceof JS) return ((JS)o).coerceToNumber(); throw new Error("toNumber() got object of type " + o.getClass().getName() + " which we don't know how to handle"); @@ -82,19 +83,14 @@ public abstract class JS { public Object callMethod(Object method, Array args, boolean checkOnly) throws JS.Exn { if (checkOnly) return Boolean.FALSE; Object o = get(method); - if(o instanceof JS.Callable) { - return ((JS.Callable)o).call(args); - } else if(o == null) { - throw new JS.Exn("Attempted to call non-existent method: " + method); - } else { - throw new JS.Exn("Attempted to call a non-method: " +method); - } + if (o instanceof JS.Callable) return ((JS.Callable)o).call(args); + else if (o == null) throw new JS.Exn("Attempted to call non-existent method: " + method); + else throw new JS.Exn("Attempted to call a non-function: " +method); } - public Number coerceToNumber() { return new Integer(0); } + public Number coerceToNumber() { throw new JS.Exn("tried to coerce a JavaScript object to a Number"); } public String coerceToString() { throw new JS.Exn("tried to coerce a JavaScript object to a String"); } - public boolean coerceToBoolean() { return true; } - + public boolean coerceToBoolean() { throw new JS.Exn("tried to coerce a JavaScript object to a Boolean"); } public String typeName() { return "object"; } @@ -148,8 +144,7 @@ public abstract class JS { public static class Scope extends ScopeImpl { public Scope(Scope parentScope) { this(parentScope, false); } public Scope(Scope parentScope, boolean sealed) { super(parentScope, sealed); } - /** transparent scopes are not returned by THIS */ - public boolean isTransparent() { return super.isTransparent(); } + public boolean isTransparent() { return super.isTransparent(); } //< transparent scopes are not returned by THIS public boolean has(Object key) { return super.has(key); } public Object get(Object key) { return super._get(key); } public Object put(Object key, Object val) { super._put(key, val); return null; } @@ -193,70 +188,82 @@ public abstract class JS { } } - /** anything that is callable with the () operator */ + /** anything that is callable with the () operator and wasn't compiled from JS code */ public static abstract class Callable extends JS.Obj { public abstract Object call(JS.Array args) throws JS.Exn; } - /** a Callable which was compiled from JavaScript code */ - public static class CompiledFunction extends CompiledFunctionImpl { - public int getNumFormalArgs() { return numFormalArgs; } - CompiledFunction(String sourceName, int firstLine, Reader sourceCode, Scope scope) throws IOException { - super(sourceName, firstLine, sourceCode, scope); - } - } - /** a scope that is populated with js objects and functions normally found in the global scope */ public static class GlobalScope extends GlobalScopeImpl { public GlobalScope() { this(null); } public GlobalScope(JS.Scope parent) { super(parent); } } - public static final JS Math = new org.xwt.js.Math(); - public static class TailCall { - CompiledFunction func = null; + Function func = null; JS.Array args = null; public TailCall() { } - public TailCall set(CompiledFunction func) { this.func = func; this.args = new JS.Array(); return this; } - public TailCall set(CompiledFunction func, JS.Array args) { this.func = func; this.args = args; return this; } + public TailCall set(Function func) { this.func = func; this.args = new JS.Array(); return this; } + public TailCall set(Function func, JS.Array args) { this.func = func; this.args = args; return this; } } - /** encapsulates a single JavaScript thread; the JS.Context->java.lang.Thread mapping is 1:1 */ + /** encapsulates the state of a JavaScript "thread" (ie stack) */ public static class Context { - CompiledFunction f = null; - Vec stack = new Vec(); - public Scope scope = null; - int pc = 0; - /** return this to signal a pause */ + // Statics ////////////////////////////////////////////////////////////////////// + + /** + * Return this from call/get/put in order to make the interpreter pause. The interpreter will expect a value + * (the return from the call or the get) to be pushed onto the stack when it is resumed. + */ public static Object pause = new Object(); - public static int getLine() { return current().f == null ? -1 : current().f.getLine(current()); } - public static String getSourceName() { return current().f == null ? null : current().f.getSourceName(); } + private int getLine_() { return current().f == null ? -1 : (pc < 0 || pc >= f.size) ? -1 : f.line[pc]; } + public static int getLine() { return current().getLine_(); } + public static String getSourceName() { return current().f == null ? null : current().f.sourceName; } + + /** fetches the currently-executing javascript function */ + public static JS.Context current() { return (JS.Context)threadToContext.get(Thread.currentThread()); } + private static Hashtable threadToContext = new Hashtable(); - public Context(CompiledFunction function, Scope scope, JS.Array args) { - if (scope == null) scope = new CompiledFunctionImpl.FunctionScope("unknown", function.parentScope); - if (args == null) args = new JS.Array(); - stack.push(new CompiledFunctionImpl.CallMarker(this)); - stack.push(args); + + // Instance members and methods ////////////////////////////////////////////////////////////////////// + + /** the currently-executing Function */ + Function f = null; + + /** the currently-executing scope */ + public Scope scope = null; // FIXME: do we really need this? the function should contain this info + + /** the object stack */ + Vec stack = new Vec(); + + /** the program counter */ + int pc = 0; + + public Context(Function function, Scope scope) { + if (scope == null) scope = new Scope(function.parentScope); + stack.push(new Function.CallMarker(this)); + stack.push(new JS.Array()); this.f = function; this.scope = scope; } + public Object resume() { return resume(null); } public Object resume(Object o) { + Thread t = Thread.currentThread(); + Context old = (Context)threadToContext.get(t); try { - javaContextToJSContext.put(Thread.currentThread(), this); - return CompiledFunctionImpl.eval(this, o); + threadToContext.put(t, this); + stack.push(o); + return Function.eval(this); } finally { - javaContextToJSContext.remove(Thread.currentThread()); + if (old == null) threadToContext.remove(t); + else threadToContext.put(t, old); } } - /** fetches the currently-executing javascript function */ - public static JS.Context current() { return (JS.Context)javaContextToJSContext.get(Thread.currentThread()); } - private static Hashtable javaContextToJSContext = new Hashtable(); } } -- 1.7.10.4