X-Git-Url: http://git.megacz.com/?p=org.ibex.core.git;a=blobdiff_plain;f=src%2Forg%2Fxwt%2Fjs%2FJS.java;h=ba83b01dcc72b80256dd069d8683a8d751e6ca93;hp=94809b50aebf4656fe2d01996d7fba429f126866;hb=4444c6057bb0cee5d2ae5a55b3045fd8eb790295;hpb=b1fff79c901054f97607ef1da809dfa6ad2a31dc 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(); } }