From bbfe17b34980e100bcf26ec0c0b1b3fde1992793 Mon Sep 17 00:00:00 2001 From: megacz Date: Fri, 30 Jan 2004 07:40:51 +0000 Subject: [PATCH] 2003/11/03 05:28:32 darcs-hash:20040130074051-2ba56-790088e2aae42f9cbf6a6b7c4c7285030145cbc2.gz --- src/org/xwt/js/ByteCodes.java | 7 +- src/org/xwt/js/CompiledFunctionImpl.java | 145 +++++++++++++++--------------- src/org/xwt/js/JS.java | 53 ++++++----- src/org/xwt/js/Parser.java | 4 + src/org/xwt/js/Regexp.java | 2 +- src/org/xwt/translators/MSPack.java | 2 +- src/org/xwt/util/Log.java | 2 +- 7 files changed, 115 insertions(+), 100 deletions(-) diff --git a/src/org/xwt/js/ByteCodes.java b/src/org/xwt/js/ByteCodes.java index 2e39d6f..9087965 100644 --- a/src/org/xwt/js/ByteCodes.java +++ b/src/org/xwt/js/ByteCodes.java @@ -51,7 +51,7 @@ interface ByteCodes { /** discard the top stack element */ static public final byte POP = -14; - /** pop two elements; call stack[-1](stack[top]) where stacktop is a JS.JS.Array */ + /** pop two elements; call stack[-n](stack[-n+1], stack[-n+2]...) where n is the number of args to the function */ public static final byte CALL = -15; /** pop an element; push a JS.JS.Array containing the keys of the popped element */ @@ -83,10 +83,13 @@ interface ByteCodes { /** finish a finally block and carry out whatever instruction initiated the finally block */ public static final byte FINALLY_DONE = -24; + /** same as CALL, except that the function is on top of the arguments instead of beneath them */ + public static final byte CALL_REVERSED = -25; + public static final String[] bytecodeToString = new String[] { "", "", "LITERAL", "ARRAY", "OBJECT", "NEWFUNCTION", "DECLARE", "TOPSCOPE", "GET", "GET_PRESERVE", "PUT", "JT", "JF", "JMP", "POP", "CALL", "PUSHKEYS", "SWAP", "NEWSCOPE", "OLDSCOPE", "DUP", "LABEL", "LOOP", "CALLMETHOD", - "FINALLY_DONE" + "FINALLY_DONE", "CALL_REVERSED" }; } diff --git a/src/org/xwt/js/CompiledFunctionImpl.java b/src/org/xwt/js/CompiledFunctionImpl.java index 90f0322..e4bbe0e 100644 --- a/src/org/xwt/js/CompiledFunctionImpl.java +++ b/src/org/xwt/js/CompiledFunctionImpl.java @@ -88,25 +88,26 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { return this; } - public static int getLine(JS.Thread cx) { - if(cx.pc < 0 || cx.pc >= ((CompiledFunctionImpl)cx.currentCompiledFunction).size) return -1; - return ((CompiledFunctionImpl)cx.currentCompiledFunction).line[cx.pc]; + public static int getLine(Context cx) { + if(cx.pc < 0 || cx.pc >= ((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 JS.Thread cx) throws JS.Exn { + static Object eval(final Context cx, Object pushFirst) throws JS.Exn { + cx.stack.push(pushFirst); OUTER: for(;; cx.pc++) { try { - if (cx.paused) return null; - cx.bind(); - if (cx.currentCompiledFunction == null) return cx.stack.pop(); - if (cx.pc >= ((CompiledFunctionImpl)cx.currentCompiledFunction).size) return cx.stack.pop(); + 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.currentCompiledFunction.op[cx.pc]; - Object curArg = cx.currentCompiledFunction.arg[cx.pc]; + int curOP = cx.f.op[cx.pc]; + Object curArg = cx.f.arg[cx.pc]; + Object returnedFromJava = null; + boolean checkReturnedFromJava = false; if(curOP == FINALLY_DONE) { FinallyData fd = (FinallyData) cx.stack.pop(); if(fd == null) continue OUTER; // NOP @@ -163,7 +164,7 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { case LABEL: break; case LOOP: { - cx.stack.push(new LoopMarker(cx.pc, cx.pc > 0 && cx.currentCompiledFunction.op[cx.pc - 1] == LABEL ? (String)cx.currentCompiledFunction.arg[cx.pc - 1] : (String)null, cx.scope)); + 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; } @@ -183,7 +184,7 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { if (o instanceof LoopMarker) { if (curArg == null || curArg.equals(((LoopMarker)o).label)) { int loopInstructionLocation = ((LoopMarker)o).location; - int endOfLoop = ((Integer)cx.currentCompiledFunction.arg[loopInstructionLocation]).intValue() + loopInstructionLocation; + 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; @@ -191,7 +192,7 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { } } } - throw new Error("CONTINUE/BREAK invoked but couldn't find a LoopMarker at " + ((CompiledFunctionImpl)cx.currentCompiledFunction).sourceName + ":" + getLine(cx)); + throw new Error("CONTINUE/BREAK invoked but couldn't find a LoopMarker at " + ((CompiledFunctionImpl)cx.f).sourceName + ":" + getLine(cx)); case TRY: { int[] jmps = (int[]) curArg; @@ -216,7 +217,7 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { if (o instanceof CallMarker) { cx.scope = ((CallMarker)o).scope; cx.pc = ((CallMarker)o).pc; - cx.currentCompiledFunction = (CompiledFunction)((CallMarker)o).currentCompiledFunction; + cx.f = (CompiledFunction)((CallMarker)o).f; cx.stack.push(retval); continue OUTER; } @@ -234,17 +235,11 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { throw je("tried to put a value to the " + key + " property on a " + target.getClass().getName()); if (key == null) throw je("tried to assign \"" + (val==null?"(null)":val.toString()) + "\" to the null key"); - // FIXME too many allocations here - TailCall tail = new TailCall(); - ((JS)target).put(key, val, tail); - cx.stack.push(val); - if (tail.func != null) { - cx.stack.push(new CallMarker(cx)); - cx.stack.push(tail.args); - cx.currentCompiledFunction = tail.func; - cx.scope = new CompiledFunctionImpl.FunctionScope("unknown", tail.func.parentScope); - cx.pc = -1; - break; + returnedFromJava = ((JS)target).put(key, val); + if (returnedFromJava != null) { + checkReturnedFromJava = true; + } else { + cx.stack.push(val); } break; } @@ -266,15 +261,9 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { if (o instanceof String || o instanceof Number || o instanceof Boolean) ret = Internal.getFromPrimitive(o,v); else if (o instanceof JS) { - ret = ((JS)o).get(v); - if (ret instanceof JS.TailCall) { - cx.stack.push(new CallMarker(cx)); - cx.stack.push(((JS.TailCall)ret).args); - cx.currentCompiledFunction = ((JS.TailCall)ret).func; - cx.scope = new CompiledFunctionImpl.FunctionScope("unknown", ((JS.TailCall)ret).func.parentScope); - cx.pc = -1; - break; - } + 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); @@ -283,65 +272,55 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { case CALLMETHOD: case CALL: + case CALL_REVERSED: { JS.Array arguments = new JS.Array(); int numArgs = JS.toNumber(curArg).intValue(); arguments.setSize(numArgs); + Object o = null; + if (curOP == CALL_REVERSED) o = cx.stack.pop(); for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j); - Object o = cx.stack.pop(); + if (curOP != CALL_REVERSED) o = cx.stack.pop(); if(o == null) throw je("attempted to call null"); Object ret; + if(curOP == 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 if (o instanceof JS) { - if (((JS)o).callMethod(method, arguments, true) == Boolean.TRUE) { - ret = ((JS)o).callMethod(method,arguments,false); - cx.stack.push(ret); - break; - } else { - o = ((JS)o).get(method); - if (o instanceof JS.TailCall) { - cx.stack.push(new CallMarker(cx)); - cx.stack.push(((JS.TailCall)o).args); - cx.currentCompiledFunction = ((JS.TailCall)o).func; - cx.scope = new CompiledFunctionImpl.FunctionScope("unknown", ((JS.TailCall)o).func.parentScope); - cx.pc = -1; - break; - } - } } else { - throw new JS.Exn("Tried to call a method on an object that isn't a JS object: " + o); + // put the args back on the stack and let the GET followed by CALL happen + for(int j=0; j 0) { Object o = cx.stack.pop(); @@ -540,9 +540,12 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { // Exception Stuff //////////////////////////////////////////////////////////////// static class EvaluatorException extends RuntimeException { public EvaluatorException(String s) { super(s); } } - static EvaluatorException ee(String s) { throw new EvaluatorException(((CompiledFunctionImpl)(JS.Thread.current()).currentCompiledFunction).sourceName + ":" + JS.Thread.current().getLine() + " " + s); } - static JS.Exn je(String s) { throw new JS.Exn(((CompiledFunctionImpl)(JS.Thread.current()).currentCompiledFunction).sourceName + ":" + JS.Thread.current().getLine() + " " + s); } - + static EvaluatorException ee(String s) { + throw new EvaluatorException(JS.Context.getSourceName() + ":" + JS.Context.getLine() + " " + s); + } + static JS.Exn je(String s) { + throw new JS.Exn(JS.Context.getSourceName() + ":" + JS.Context.getLine() + " " + s); + } // FunctionScope ///////////////////////////////////////////////////////////////// @@ -558,8 +561,8 @@ class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { public static class CallMarker { int pc; Scope scope; - CompiledFunctionImpl currentCompiledFunction; - public CallMarker(JS.Thread cx) { pc = cx.pc + 1; scope = cx.scope; currentCompiledFunction = cx.currentCompiledFunction; } + CompiledFunctionImpl f; + public CallMarker(Context cx) { pc = cx.pc + 1; scope = cx.scope; f = cx.f; } } public static class CatchMarker { public CatchMarker() { } } diff --git a/src/org/xwt/js/JS.java b/src/org/xwt/js/JS.java index ce48cb5..94809b5 100644 --- a/src/org/xwt/js/JS.java +++ b/src/org/xwt/js/JS.java @@ -2,6 +2,7 @@ package org.xwt.js; import org.xwt.util.*; +import org.xwt.*; import java.io.*; import java.util.*; @@ -76,11 +77,10 @@ public abstract class JS { // Instance Methods //////////////////////////////////////////////////////////////////// public abstract Object get(Object key) throws JS.Exn; - public abstract void put(Object key, Object val) throws JS.Exn; - public void put(Object key, Object val, TailCall tail) throws JS.Exn { put(key, val); } + public abstract Object put(Object key, Object val) throws JS.Exn; public abstract Object[] keys(); public Object callMethod(Object method, Array args, boolean checkOnly) throws JS.Exn { - if(checkOnly) return Boolean.FALSE; + if (checkOnly) return Boolean.FALSE; Object o = get(method); if(o instanceof JS.Callable) { return ((JS.Callable)o).call(args); @@ -107,7 +107,7 @@ public abstract class JS { public Obj() { this(false); } public Obj(boolean sealed) { this.sealed = sealed; } public void setSeal(boolean sealed) { this.sealed = sealed; } ///< a sealed object cannot have its properties modified - public void put(Object key, Object val) { put2(key, null, val); } + public Object put(Object key, Object val) { put2(key, null, val); return null; } protected void put2(Object key, Object key2, Object val) { if (sealed) return; if (entries == null) entries = new Hash(); @@ -141,7 +141,7 @@ public abstract class JS { public void addElement(Object o) { super.addElement(o); } public void setElementAt(Object o, int i) { super.setElementAt(o, i); } public Object get(Object key) { return super._get(key); } - public void put(Object key, Object val) { super._put(key, val); } + public Object put(Object key, Object val) { super._put(key, val); return null; } } /** Any object which becomes part of the scope chain must support this interface */ @@ -152,7 +152,7 @@ public abstract class JS { public boolean isTransparent() { return super.isTransparent(); } public boolean has(Object key) { return super.has(key); } public Object get(Object key) { return super._get(key); } - public void put(Object key, Object val) { super._put(key, val); } + public Object put(Object key, Object val) { super._put(key, val); return null; } public void declare(String s) { super.declare(s); } } @@ -172,7 +172,7 @@ public abstract class JS { public boolean equals(Object o) { return (this == o || graftee.equals(o)); } public int hashCode() { return graftee.hashCode(); } public Object get(Object key) { return replaced_key.equals(key) ? replaced_val : graftee.get(key); } - public void put(Object key, Object val) { graftee.put(key, val); } + public Object put(Object key, Object val) { graftee.put(key, val); return null; } public Object callMethod(Object method, Array args, boolean checkOnly) throws JS.Exn { if (!replaced_key.equals(method)) return graftee.callMethod(method, args, checkOnly); if (replaced_val instanceof Callable) return checkOnly ? Boolean.TRUE : ((Callable)replaced_val).call(args); @@ -222,36 +222,41 @@ public abstract class JS { public TailCall set(CompiledFunction func, JS.Array args) { this.func = func; this.args = args; return this; } } - /** encapsulates a single JavaScript thread; the JS.Thread->java.lang.Thread mapping is 1:1 */ - public static class Thread { + /** encapsulates a single JavaScript thread; the JS.Context->java.lang.Thread mapping is 1:1 */ + public static class Context { - CompiledFunction currentCompiledFunction = null; + CompiledFunction f = null; Vec stack = new Vec(); public Scope scope = null; int pc = 0; - boolean paused = false; - public Thread(CompiledFunction function) { this(function, null); } - public Thread(CompiledFunction function, Scope scope) { this(function, scope, new JS.Array()); } - public Thread(CompiledFunction function, Scope scope, JS.Array args) { + /** return this to signal a pause */ + 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(); } + + 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); - this.currentCompiledFunction = function; + this.f = function; this.scope = scope; } - public static JS.Thread current() { return (JS.Thread)javaThreadToJSThread.get(java.lang.Thread.currentThread()); } - public Object resume() { bind(); paused = false; Object ret = CompiledFunctionImpl.eval(this); unbind(); return ret; } - public void pause() { paused = true; unbind(); } - public void bind() { javaThreadToJSThread.put(java.lang.Thread.currentThread(), this); } - public void unbind() { if (current() == this) javaThreadToJSThread.remove(java.lang.Thread.currentThread()); } - public int getLine() { return currentCompiledFunction == null ? -1 : currentCompiledFunction.getLine(this); } - public String getSourceName() { return currentCompiledFunction == null ? null : currentCompiledFunction.getSourceName(); } + public Object resume(Object o) { + try { + javaContextToJSContext.put(Thread.currentThread(), this); + return CompiledFunctionImpl.eval(this, o); + } finally { + javaContextToJSContext.remove(Thread.currentThread()); + } + } /** fetches the currently-executing javascript function */ - public JS.CompiledFunction getCurrentCompiledFunction() { return currentCompiledFunction; } - private static Hashtable javaThreadToJSThread = new Hashtable(); + public static JS.Context current() { return (JS.Context)javaContextToJSContext.get(Thread.currentThread()); } + private static Hashtable javaContextToJSContext = new Hashtable(); } } diff --git a/src/org/xwt/js/Parser.java b/src/org/xwt/js/Parser.java index 7360876..70a497c 100644 --- a/src/org/xwt/js/Parser.java +++ b/src/org/xwt/js/Parser.java @@ -338,7 +338,11 @@ class Parser extends Lexer implements ByteCodes { } case LP: { int n = parseArgs(b); + + // if the object supports GETCALL, we use this, and jump over the following two instructions b.add(parserLine,CALLMETHOD,new Integer(n)); + b.add(parserLine,GET); + b.add(parserLine,CALL_REVERSED,new Integer(n)); break; } default: { diff --git a/src/org/xwt/js/Regexp.java b/src/org/xwt/js/Regexp.java index 18c6aeb..8179123 100644 --- a/src/org/xwt/js/Regexp.java +++ b/src/org/xwt/js/Regexp.java @@ -54,7 +54,7 @@ public class Regexp extends JS.Obj { // gcj bug... public Object get(Object key) { return _get(key); } - public void put(Object key,Object value) { _put(key,value); } + public Object put(Object key,Object value) { _put(key,value); return null; } public Object _get(Object key) { if(key.equals("lastIndex")) return new Integer(lastIndex); diff --git a/src/org/xwt/translators/MSPack.java b/src/org/xwt/translators/MSPack.java index 2ad52d2..c9482eb 100644 --- a/src/org/xwt/translators/MSPack.java +++ b/src/org/xwt/translators/MSPack.java @@ -14,7 +14,7 @@ public class MSPack { public static class MSPackException extends IOException { public MSPackException(String s) { super(s); } } - private static synchronized byte[] getImage() { + private static synchronized byte[] getImage() throws IOException { if (image == null) image = InputStreamToByteArray.convert(Main.builtin.getInputStream("libmspack.mips")); return image; diff --git a/src/org/xwt/util/Log.java b/src/org/xwt/util/Log.java index eb456d1..dd390d0 100644 --- a/src/org/xwt/util/Log.java +++ b/src/org/xwt/util/Log.java @@ -18,7 +18,7 @@ public class Log { /** log a message with the current JavaScript sourceName/line */ public static void logJS(Object o, Object message) { logJS(message); } public static void logJS(Object message) { - JS.Thread current = org.xwt.js.JS.Thread.current(); + JS.Context current = org.xwt.js.JS.Context.current(); if (current == null) { log("", message); } else { -- 1.7.10.4