From: brian Date: Tue, 6 Jul 2004 00:03:48 +0000 (+0000) Subject: new js api X-Git-Url: http://git.megacz.com/?p=org.ibex.core.git;a=commitdiff_plain;h=ce791e4058158295bce9cf7b6698c2b565d571d7 new js api darcs-hash:20040706000348-24bed-639cbadde652727bf343f2c2bfe3ef803b3a7e4a.gz --- diff --git a/src/org/ibex/js/Directory.java b/src/org/ibex/js/Directory.java index e9f7b95..1728e5b 100644 --- a/src/org/ibex/js/Directory.java +++ b/src/org/ibex/js/Directory.java @@ -43,7 +43,8 @@ public class Directory extends JS { * Create the directory object. Existing directories will be * preserved; if a file is present it will be obliterated. */ - public Directory(File f) throws IOException { + // FIXME: Update this for new API + /*public Directory(File f) throws IOException { this.f = f; if (!f.exists()) new Directory(new File(f.getParent())); if (!f.isDirectory()) destroy(f); @@ -113,5 +114,5 @@ public class Directory extends JS { public boolean hasMoreElements() { return i < elements.length; } public Object nextElement() { return FileNameEncoder.decode(elements[i++]); } }; - } + }*/ } diff --git a/src/org/ibex/js/Interpreter.java b/src/org/ibex/js/Interpreter.java index d16c582..2d88042 100644 --- a/src/org/ibex/js/Interpreter.java +++ b/src/org/ibex/js/Interpreter.java @@ -6,8 +6,8 @@ import java.util.*; /** Encapsulates a single JS interpreter (ie call stack) */ class Interpreter implements ByteCodes, Tokens { - private static final int MAX_STACK_SIZE = 512; - + private static final JS CASCADE = JSString.intern("cascade"); + // Thread-Interpreter Mapping ///////////////////////////////////////////////////////////////////////// static Interpreter current() { return (Interpreter)threadToInterpreter.get(Thread.currentThread()); } @@ -19,19 +19,24 @@ class Interpreter implements ByteCodes, Tokens { int pausecount; ///< the number of times pause() has been invoked; -1 indicates unpauseable JSFunction f = null; ///< the currently-executing JSFunction JSScope scope; ///< the current top-level scope (LIFO stack via NEWSCOPE/OLDSCOPE) - Vec stack = new Vec(); ///< the object stack + final Stack stack = new Stack(); ///< the object stack int pc = 0; ///< the program counter Interpreter(JSFunction f, boolean pauseable, JSArray args) { - stack.push(new Interpreter.CallMarker(this)); // the "root function returned" marker -- f==null this.f = f; this.pausecount = pauseable ? 0 : -1; this.scope = new JSScope(f.parentScope); - stack.push(args); + try { + stack.push(new CallMarker()); // the "root function returned" marker -- f==null + stack.push(args); + } catch(JSExn e) { + throw new Error("should never happen"); + } } /** this is the only synchronization point we need in order to be threadsafe */ - synchronized Object resume() throws JSExn { + synchronized JS resume() throws JSExn { + if(f == null) throw new RuntimeException("function already finished"); Thread t = Thread.currentThread(); Interpreter old = (Interpreter)threadToInterpreter.get(t); threadToInterpreter.put(t, this); @@ -55,15 +60,13 @@ class Interpreter implements ByteCodes, Tokens { private static JSExn je(String s) { return new JSExn(getSourceName() + ":" + getLine() + " " + s); } - // FIXME: double check the trap logic - private Object run() throws JSExn { + private JS run() throws JSExn { // if pausecount changes after a get/put/call, we know we've been paused final int initialPauseCount = pausecount; OUTER: for(;; pc++) { try { - if (f == null) return stack.pop(); int op = f.op[pc]; Object arg = f.arg[pc]; if(op == FINALLY_DONE) { @@ -74,28 +77,32 @@ class Interpreter implements ByteCodes, Tokens { arg = fd.arg; } switch(op) { - case LITERAL: stack.push(arg); break; + case LITERAL: stack.push((JS)arg); break; case OBJECT: stack.push(new JS.O()); break; - case ARRAY: stack.push(new JSArray(JS.toNumber(arg).intValue())); break; - case DECLARE: scope.declare((String)(arg==null ? stack.peek() : arg)); if(arg != null) stack.push(arg); break; + case ARRAY: stack.push(new JSArray(JS.toInt((JS)arg))); break; + case DECLARE: scope.declare((JS)(arg==null ? stack.peek() : arg)); if(arg != null) stack.push((JS)arg); break; case TOPSCOPE: stack.push(scope); break; - case JT: if (JS.toBoolean(stack.pop())) pc += JS.toNumber(arg).intValue() - 1; break; - case JF: if (!JS.toBoolean(stack.pop())) pc += JS.toNumber(arg).intValue() - 1; break; - case JMP: pc += JS.toNumber(arg).intValue() - 1; break; + case JT: if (JS.toBoolean(stack.pop())) pc += JS.toInt((JS)arg) - 1; break; + case JF: if (!JS.toBoolean(stack.pop())) pc += JS.toInt((JS)arg) - 1; break; + case JMP: pc += JS.toInt((JS)arg) - 1; break; case POP: stack.pop(); break; case SWAP: { - int depth = (arg == null ? 1 : JS.toInt(arg)); - Object save = stack.elementAt(stack.size() - 1); + int depth = (arg == null ? 1 : JS.toInt((JS)arg)); + JS save = stack.elementAt(stack.size() - 1); for(int i=stack.size() - 1; i > stack.size() - 1 - depth; i--) stack.setElementAt(stack.elementAt(i-1), i); stack.setElementAt(save, stack.size() - depth - 1); - break; } + break; + } case DUP: stack.push(stack.peek()); break; case NEWSCOPE: scope = new JSScope(scope); break; case OLDSCOPE: scope = scope.getParentScope(); break; - case ASSERT: - if (JS.checkAssertions && !JS.toBoolean(stack.pop())) - throw je("ibex.assertion.failed" /*FEATURE: line number*/); break; + case ASSERT: { + JS o = stack.pop(); + if (JS.checkAssertions && !JS.toBoolean(o)) + throw je("ibex.assertion.failed"); + break; + } case BITNOT: stack.push(JS.N(~JS.toLong(stack.pop()))); break; case BANG: stack.push(JS.B(!JS.toBoolean(stack.pop()))); break; case NEWFUNCTION: stack.push(((JSFunction)arg)._cloneWithNewParentScope(scope)); break; @@ -104,32 +111,32 @@ class Interpreter implements ByteCodes, Tokens { case TYPEOF: { Object o = stack.pop(); if (o == null) stack.push(null); - else if (o instanceof JS) stack.push("object"); - else if (o instanceof String) stack.push("string"); - else if (o instanceof Number) stack.push("number"); - else if (o instanceof Boolean) stack.push("boolean"); - else throw new Error("this should not happen"); + else if (o instanceof JSString) stack.push(JS.S("string")); + else if (o instanceof JSNumber.B) stack.push(JS.S("boolean")); + else if (o instanceof JSNumber) stack.push(JS.S("number")); + else stack.push(JS.S("object")); break; } case PUSHKEYS: { - Object o = stack.peek(); - Enumeration e = ((JS)o).keys(); + JS o = stack.peek(); + Enumeration e = o.keys(); JSArray a = new JSArray(); - while(e.hasMoreElements()) a.addElement(e.nextElement()); + // FEATURE: Take advantage of the Enumeration, don't create a JSArray + while(e.hasMoreElements()) a.addElement((JS)e.nextElement()); stack.push(a); break; } case LOOP: stack.push(new LoopMarker(pc, pc > 0 && f.op[pc - 1] == LABEL ? (String)f.arg[pc - 1] : (String)null, scope)); - stack.push(Boolean.TRUE); + stack.push(JS.T); break; case BREAK: case CONTINUE: while(stack.size() > 0) { - Object o = stack.pop(); + JS o = stack.pop(); if (o instanceof CallMarker) je("break or continue not within a loop"); if (o instanceof TryMarker) { if(((TryMarker)o).finallyLoc < 0) continue; // no finally block, keep going @@ -141,9 +148,9 @@ class Interpreter implements ByteCodes, Tokens { if (o instanceof LoopMarker) { if (arg == null || arg.equals(((LoopMarker)o).label)) { int loopInstructionLocation = ((LoopMarker)o).location; - int endOfLoop = ((Integer)f.arg[loopInstructionLocation]).intValue() + loopInstructionLocation; + int endOfLoop = JS.toInt((JS)f.arg[loopInstructionLocation]) + loopInstructionLocation; scope = ((LoopMarker)o).scope; - if (op == CONTINUE) { stack.push(o); stack.push(Boolean.FALSE); } + if (op == CONTINUE) { stack.push(o); stack.push(JS.F); } pc = op == BREAK ? endOfLoop - 1 : loopInstructionLocation; continue OUTER; } @@ -161,7 +168,7 @@ class Interpreter implements ByteCodes, Tokens { } case RETURN: { - Object retval = stack.pop(); + JS retval = stack.pop(); while(stack.size() > 0) { Object o = stack.pop(); if (o instanceof TryMarker) { @@ -198,6 +205,7 @@ class Interpreter implements ByteCodes, Tokens { f = (JSFunction)((CallMarker)o).f; stack.push(retval); if (pausecount > initialPauseCount) { pc++; return null; } // we were paused + if(f == null) return retval; continue OUTER; } } @@ -205,22 +213,15 @@ class Interpreter implements ByteCodes, Tokens { } case PUT: { - Object val = stack.pop(); - Object key = stack.pop(); - Object target = stack.peek(); - if (target == null) - throw je("tried to put a value to the " + key + " property on the null value"); - if (!(target instanceof JS)) - 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"); + JS val = stack.pop(); + JS key = stack.pop(); + JS target = stack.peek(); + if (target == null) throw je("tried to put " + JS.debugToString(val) + " to the " + JS.debugToString(key) + " property on the null value"); + if (key == null) throw je("tried to assign \"" + JS.debugToString(val) + "\" to the null key"); - if (target instanceof String || target instanceof Number || target instanceof Boolean) throw new JSExn("can't put values to primitives"); - if(!(target instanceof JS)) throw new Error("should never happen"); - Trap t = null; TrapMarker tm = null; - if(target instanceof JSScope && key.equals("cascade")) { + if(target instanceof JSScope && key.jsequals(CASCADE)) { Object o=null; int i; for(i=stack.size()-1;i>=0;i--) if((o = stack.elementAt(i)) instanceof CallMarker) break; @@ -244,7 +245,7 @@ class Interpreter implements ByteCodes, Tokens { stack.push(val); if(t != null) { - stack.push(new TrapMarker(this,t,(JS)target,key,val)); + stack.push(new TrapMarker(this,t,target,key,val)); JSArray args = new JSArray(); args.addElement(val); stack.push(args); @@ -253,7 +254,7 @@ class Interpreter implements ByteCodes, Tokens { pc = -1; break; } else { - ((JS)target).put(key,val); + target.put(key,val); if (pausecount > initialPauseCount) { pc++; return null; } // we were paused break; } @@ -261,29 +262,23 @@ class Interpreter implements ByteCodes, Tokens { case GET: case GET_PRESERVE: { - Object target, key; + JS target, key; if (op == GET) { - key = arg == null ? stack.pop() : arg; + key = arg == null ? stack.pop() : (JS)arg; target = stack.pop(); } else { key = stack.pop(); target = stack.peek(); stack.push(key); } - Object ret = null; + JS ret = null; if (key == null) throw je("tried to get the null key from " + target); if (target == null) throw je("tried to get property \"" + key + "\" from the null object"); - if (target instanceof String || target instanceof Number || target instanceof Boolean) { - ret = getFromPrimitive(target,key); - stack.push(ret); - break; - } - if(!(target instanceof JS)) throw new Error("should never happen"); Trap t = null; TrapMarker tm = null; - if(target instanceof JSScope && key.equals("cascade")) { - Object o=null; + if(target instanceof JSScope && key.jsequals(CASCADE)) { + JS o=null; int i; for(i=stack.size()-1;i>=0;i--) if((o = stack.elementAt(i)) instanceof CallMarker) break; if(i==0) throw new Error("didn't find a call marker while doing cascade"); @@ -311,8 +306,8 @@ class Interpreter implements ByteCodes, Tokens { pc = -1; break; } else { - ret = ((JS)target).get(key); - if (ret == JS.METHOD) ret = new Stub((JS)target, key); + ret = target.get(key); + if (ret == JS.METHOD) ret = new Stub(target, key); stack.push(ret); if (pausecount > initialPauseCount) { pc++; return null; } // we were paused break; @@ -320,10 +315,10 @@ class Interpreter implements ByteCodes, Tokens { } case CALL: case CALLMETHOD: { - int numArgs = JS.toInt(arg); - Object method = null; - Object ret = null; - Object object = stack.pop(); + int numArgs = JS.toInt((JS)arg); + JS method = null; + JS ret = null; + JS object = stack.pop(); if (op == CALLMETHOD) { if (object == JS.METHOD) { @@ -338,35 +333,28 @@ class Interpreter implements ByteCodes, Tokens { stack.pop(); } } - Object[] rest = numArgs > 3 ? new Object[numArgs - 3] : null; + JS[] rest = numArgs > 3 ? new JS[numArgs - 3] : null; for(int i=numArgs - 1; i>2; i--) rest[i-3] = stack.pop(); - Object a2 = numArgs <= 2 ? null : stack.pop(); - Object a1 = numArgs <= 1 ? null : stack.pop(); - Object a0 = numArgs <= 0 ? null : stack.pop(); + JS a2 = numArgs <= 2 ? null : stack.pop(); + JS a1 = numArgs <= 1 ? null : stack.pop(); + JS a0 = numArgs <= 0 ? null : stack.pop(); - if (object instanceof String || object instanceof Number || object instanceof Boolean) { - ret = callMethodOnPrimitive(object, method, a0, a1, a2, null, numArgs); - } else if (object instanceof JSFunction) { + if (object instanceof JSFunction) { // FIXME: use something similar to call0/call1/call2 here JSArray arguments = new JSArray(); for(int i=0; i MAX_STACK_SIZE) throw new JSExn("stack overflow"); stack.push(arguments); f = (JSFunction)object; scope = new JSScope(f.parentScope); pc = -1; break; - - } else if (object instanceof JS) { + } else { JS c = (JS)object; ret = method == null ? c.call(a0, a1, a2, rest, numArgs) : c.callMethod(method, a0, a1, a2, rest, numArgs); - - } else { - throw new JSExn("can't call a " + object + " @" + pc + "\n" + f.dump()); - } + if (pausecount > initialPauseCount) { pc++; return null; } stack.push(ret); break; @@ -375,7 +363,7 @@ class Interpreter implements ByteCodes, Tokens { case THROW: throw new JSExn(stack.pop(), stack, f, pc, scope); - /* FIXME + /* FIXME GRAMMAR case MAKE_GRAMMAR: { final Grammar r = (Grammar)arg; final JSScope final_scope = scope; @@ -399,11 +387,11 @@ class Interpreter implements ByteCodes, Tokens { } */ case ADD_TRAP: case DEL_TRAP: { - Object val = stack.pop(); - Object key = stack.pop(); - Object obj = stack.peek(); + JS val = stack.pop(); + JS key = stack.pop(); + JS js = stack.peek(); // A trap addition/removal - JS js = (JS) obj; + if(!(val instanceof JSFunction)) throw new JSExn("tried to add/remove a non-function trap"); if(js instanceof JSScope) { JSScope s = (JSScope) js; while(s.getParentScope() != null) { @@ -412,17 +400,16 @@ class Interpreter implements ByteCodes, Tokens { } js = s; } - // might want this? - // if(!js.has(key)) throw new JSExn("tried to add/remove a trap from an uninitialized variable"); if(op == ADD_TRAP) js.addTrap(key, (JSFunction)val); else js.delTrap(key, (JSFunction)val); break; } + // FIXME: This was for the old trap syntax, remove it, shouldn't be needed anymore case ASSIGN_SUB: case ASSIGN_ADD: { - Object val = stack.pop(); - Object key = stack.pop(); - Object obj = stack.peek(); + 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); @@ -433,25 +420,33 @@ class Interpreter implements ByteCodes, Tokens { } case ADD: { - int count = ((Number)arg).intValue(); + int count = ((JSNumber)arg).toInt(); if(count < 2) throw new Error("this should never happen"); if(count == 2) { // common case - Object right = stack.pop(); - Object left = stack.pop(); - if(left instanceof String || right instanceof String) - stack.push(JS.toString(left).concat(JS.toString(right))); - else stack.push(JS.N(JS.toDouble(left) + JS.toDouble(right))); + JS right = stack.pop(); + JS left = stack.pop(); + JS ret; + if(left instanceof JSString || right instanceof JSString) + ret = JS.S(JS.toString(left).concat(JS.toString(right))); + else if(left instanceof JSNumber.D || right instanceof JSNumber.D) + ret = JS.N(JS.toDouble(left) + JS.toDouble(right)); + else { + long l = JS.toLong(left) + JS.toLong(right); + if(l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) ret = JS.N(l); + ret = JS.N((int)l); + } + stack.push(ret); } else { - Object[] args = new Object[count]; + JS[] args = new JS[count]; while(--count >= 0) args[count] = stack.pop(); - if(args[0] instanceof String) { + if(args[0] instanceof JSString) { StringBuffer sb = new StringBuffer(64); for(int i=0;i> JS.toLong(right))); break; case URSH: stack.push(JS.N(JS.toLong(left) >>> JS.toLong(right))); break; - case LT: case LE: case GT: case GE: { - if (left == null) left = JS.N(0); - if (right == null) right = JS.N(0); - int result = 0; - if (left instanceof String || right instanceof String) { - result = JS.toString(left).compareTo(JS.toString(right)); - } else { - result = (int)java.lang.Math.ceil(JS.toDouble(left) - JS.toDouble(right)); - } - stack.push(JS.B((op == LT && result < 0) || (op == LE && result <= 0) || - (op == GT && result > 0) || (op == GE && result >= 0))); - break; + //#repeat />= LT/LE/GT/GE + case LT: { + if(left instanceof JSString && right instanceof JSString) + stack.push(JS.B(JS.toString(left).compareTo(JS.toString(right)) < 0)); + else + stack.push(JS.B(JS.toDouble(left) < JS.toDouble(right))); } + //#end case EQ: case NE: { - // FIXME: This is not correct, see ECMA-262 11.9.3 - Object l = left; - Object r = right; boolean ret; - if (l == null) { Object tmp = r; r = l; l = tmp; } - if (l == null && r == null) ret = true; - else if (r == null) ret = false; // l != null, so its false - else if (l instanceof Boolean) ret = JS.B(JS.toBoolean(r)).equals(l); - else if (l instanceof Number) ret = JS.toNumber(r).doubleValue() == JS.toNumber(l).doubleValue(); - else if (l instanceof String) ret = r != null && l.equals(JS.toString(r)); - else ret = l.equals(r); + if(left == null && right == null) ret = true; + else if(left == null || right == null) ret = false; + else ret = left.jsequals(right); stack.push(JS.B(op == EQ ? ret : !ret)); break; } @@ -529,7 +511,7 @@ class Interpreter implements ByteCodes, Tokens { } catch(JSExn e) { while(stack.size() > 0) { - Object o = stack.pop(); + JS o = stack.pop(); if (o instanceof CatchMarker || o instanceof TryMarker) { boolean inCatch = o instanceof CatchMarker; if(inCatch) { @@ -563,20 +545,28 @@ class Interpreter implements ByteCodes, Tokens { // Markers ////////////////////////////////////////////////////////////////////// - public static class CallMarker { - int pc; - JSScope scope; - JSFunction f; + static class Marker extends JS { + public JS get(JS key) throws JSExn { throw new Error("this should not be accessible from a script"); } + public void put(JS key, JS val) throws JSExn { throw new Error("this should not be accessible from a script"); } + public String coerceToString() { throw new Error("this should not be accessible from a script"); } + public JS call(JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn { throw new Error("this should not be accessible from a script"); } + } + + static class CallMarker extends Marker { + final int pc; + final JSScope scope; + final JSFunction f; public CallMarker(Interpreter cx) { pc = cx.pc + 1; scope = cx.scope; f = cx.f; } + public CallMarker() { pc = -1; scope = null; f = null; } } - public static class TrapMarker extends CallMarker { + static class TrapMarker extends CallMarker { Trap t; - JS trapee; - Object key; - Object val; + final JS trapee; + final JS key; + final JS val; boolean cascadeHappened; - public TrapMarker(Interpreter cx, Trap t, JS trapee, Object key, Object val) { + public TrapMarker(Interpreter cx, Trap t, JS trapee, JS key, JS val) { super(cx); this.t = t; this.trapee = trapee; @@ -585,24 +575,24 @@ class Interpreter implements ByteCodes, Tokens { } } - public static class CatchMarker { } + static class CatchMarker extends Marker { } private static CatchMarker catchMarker = new CatchMarker(); - public static class LoopMarker { - public int location; - public String label; - public JSScope scope; + static class LoopMarker extends Marker { + final public int location; + final public String label; + final public JSScope scope; public LoopMarker(int location, String label, JSScope scope) { this.location = location; this.label = label; this.scope = scope; } } - public static class TryMarker { - public int catchLoc; - public int finallyLoc; - public JSScope scope; - public JSFunction f; + static class TryMarker extends Marker { + final public int catchLoc; + final public int finallyLoc; + final public JSScope scope; + final public JSFunction f; public TryMarker(int catchLoc, int finallyLoc, Interpreter cx) { this.catchLoc = catchLoc; this.finallyLoc = finallyLoc; @@ -610,19 +600,20 @@ class Interpreter implements ByteCodes, Tokens { this.f = cx.f; } } - public static class FinallyData { - public int op; - public Object arg; - public JSExn exn; + static class FinallyData extends Marker { + final public int op; + final public Object arg; + final public JSExn exn; public FinallyData(int op) { this(op,null); } - public FinallyData(int op, Object arg) { this.op = op; this.arg = arg; } - public FinallyData(JSExn exn) { this.exn = exn; } // Just throw this exn + public FinallyData(int op, Object arg) { this.op = op; this.arg = arg; this.exn = null; } + public FinallyData(JSExn exn) { this.exn = exn; this.op = -1; this.arg = null; } // Just throw this exn } // Operations on Primitives ////////////////////////////////////////////////////////////////////// - static Object callMethodOnPrimitive(Object o, Object method, Object arg0, Object arg1, Object arg2, Object[] rest, int alength) throws JSExn { + // 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"); @@ -759,14 +750,41 @@ class Interpreter implements ByteCodes, Tokens { }; } return null; - } + }*/ private static class Stub extends JS { - private Object method; + private JS method; JS obj; - public Stub(JS obj, Object method) { this.obj = obj; this.method = method; } - public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn { + public Stub(JS obj, JS method) { this.obj = obj; this.method = method; } + public JS call(JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn { return ((JS)obj).callMethod(method, a0, a1, a2, rest, nargs); } } + + static class Stack { + private static final int MAX_STACK_SIZE = 512; + private JS[] stack = new JS[64]; + private int sp = 0; + public final void push(JS o) throws JSExn { + if(sp == stack.length) grow(); + stack[sp++] = o; + } + public final JS peek() { + if(sp == 0) throw new RuntimeException("Stack underflow"); + return stack[sp-1]; + } + public final JS pop() { + if(sp == 0) throw new RuntimeException("Stack underflow"); + return stack[--sp]; + } + private void grow() throws JSExn { + if(stack.length >= MAX_STACK_SIZE) throw new JSExn("Stack overflow"); + JS[] stack2 = new JS[stack.length * 2]; + System.arraycopy(stack,0,stack2,0,stack.length); + } + // FIXME: Eliminate all uses of SWAP n>1 so we don't need this + public int size() { return sp; } + public void setElementAt(JS o, int i) { stack[i] = o; } + public JS elementAt(int i) { return stack[i]; } + } } diff --git a/src/org/ibex/js/JS.java b/src/org/ibex/js/JS.java index 81feec1..f9bd66b 100644 --- a/src/org/ibex/js/JS.java +++ b/src/org/ibex/js/JS.java @@ -10,42 +10,51 @@ public abstract class JS { public static boolean checkAssertions = false; - public static final Object METHOD = new Object() { public String toString() { return "JS.METHOD"; } }; + public static final JS METHOD = new JS() { }; public final JS unclone() { return _unclone(); } public final JS jsclone() throws JSExn { return new Clone(this); } - public Enumeration keys() throws JSExn { throw new JSExn("you can't enumerate the keys of this object (class=" + this.getClass().getName() +")"); } - public Object get(Object key) throws JSExn { return null; } - public void put(Object key, Object val) throws JSExn { throw new JSExn("this object is read only (class=" + this.getClass().getName() +")"); } + // FEATURE: JSEnumeration + public Enumeration keys() throws JSExn { throw new JSExn("you can't enumerate the keys of this object (class=" + getClass().getName() +")"); } + public JS get(JS key) throws JSExn { return null; } + public void put(JS key, JS val) throws JSExn { throw new JSExn("" + key + " is read only (class=" + getClass().getName() +")"); } - public final boolean hasTrap(Object key) { return getTrap(key) != null; } + public final boolean hasTrap(JS key) { return getTrap(key) != null; } - public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn { - throw new JSExn("method not found (" + method + ")"); + public JS callMethod(JS method, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn { + throw new JSExn("method not found (" + JS.debugToString(method) + ")"); } // FIXME: JSArgs objects, pointers into stack frame - public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn { + public JS call(JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn { throw new JSExn("you cannot call this object (class=" + this.getClass().getName() +")"); } - - Trap getTrap(Object key) { return null; } - void putTrap(Object key, Trap value) throws JSExn { throw new JSExn("traps cannot be placed on this object (class=" + this.getClass().getName() +")"); } + + public final boolean equals(Object o) { return this == o || ((o instanceof JS) && jsequals((JS)o)); } + + // FIXME: Remove this, eventually + public final String toString() { throw new Error("you shouldn't use toString() on JS objects"); } + + // These are only used internally by org.ibex.js + Trap getTrap(JS key) { return null; } + void putTrap(JS key, Trap value) throws JSExn { throw new JSExn("traps cannot be placed on this object (class=" + this.getClass().getName() +")"); } + String coerceToString() throws JSExn { throw new JSExn("can't coerce to a string (class=" + getClass().getName() +")"); } + boolean jsequals(JS o) { return this == o; } public static class O extends JS { private Hash entries; public Enumeration keys() throws JSExn { return entries == null ? emptyEnumeration : entries.keys(); } - public Object get(Object key) throws JSExn { return entries == null ? null : entries.get(key, null); } - public void put(Object key, Object val) throws JSExn { (entries==null?entries=new Hash():entries).put(key,null,val); } + public JS get(JS key) throws JSExn { return entries == null ? null : (JS)entries.get(key, null); } + public void put(JS key, JS val) throws JSExn { (entries==null?entries=new Hash():entries).put(key,null,val); } /** retrieve a trap from the entries hash */ - final Trap getTrap(Object key) { + final Trap getTrap(JS key) { return entries == null ? null : (Trap)entries.get(key, Trap.class); } /** retrieve a trap from the entries hash */ - final void putTrap(Object key, Trap value) { + final void putTrap(JS key, Trap value) { if (entries == null) entries = new Hash(); entries.put(key, Trap.class, value); } @@ -68,28 +77,25 @@ public abstract class JS { public interface Cloneable { } public static class Clone extends JS.O implements Cloneable { - protected JS clonee; + protected final JS clonee; JS _unclone() { return clonee.unclone(); } public JS getClonee() { return clonee; } public Clone(JS clonee) throws JSExn { - if(!(clonee instanceof Cloneable)) throw new JSExn("this object isn't cloneable"); + if(!(clonee instanceof Cloneable)) throw new JSExn("" + getClass().getName() + " isn't cloneable"); this.clonee = clonee; } - public boolean equals(Object o) { - if (!(o instanceof JS)) return false; - return unclone() == ((JS)o).unclone(); - } + public boolean jsequals(JS o) { return unclone().jsequals(o.unclone()); } public Enumeration keys() throws JSExn { return clonee.keys(); } - public Object get(Object key) throws JSExn { return clonee.get(key); } - public void put(Object key, Object val) throws JSExn { clonee.put(key, val); } - public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn { + public JS get(JS key) throws JSExn { return clonee.get(key); } + public void put(JS key, JS val) throws JSExn { clonee.put(key, val); } + public JS callMethod(JS method, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn { return clonee.callMethod(method, a0, a1, a2, rest, nargs); - } - public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn { + } + public JS call(JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn { return clonee.call(a0, a1, a2, rest, nargs); } } - + // Static Interpreter Control Methods /////////////////////////////////////////////////////////////// /** log a message with the current JavaScript sourceName/line */ @@ -112,12 +118,15 @@ public abstract class JS { public static class UnpauseCallback implements Task { Interpreter i; UnpauseCallback(Interpreter i) { this.i = i; } - public void perform() throws JSExn { unpause(null); } - public void unpause(Object o) throws JSExn { - // FIXME: if o instanceof JSExn, throw it into the JSworld + public void perform() throws JSExn { unpause((JS)null); } + public void unpause(JS o) throws JSExn { i.stack.push(o); i.resume(); } + public void unpause(JSExn e) { + // FIXME: Throw the JSExn into the js world + throw new Error("Exception " + e + " thrown from background task"); + } } @@ -125,87 +134,89 @@ public abstract class JS { // Static Helper Methods /////////////////////////////////////////////////////////////////////////////////// /** coerce an object to a Boolean */ - public static boolean toBoolean(Object o) { - if (o == null) return false; - if (o instanceof Boolean) return ((Boolean)o).booleanValue(); - if (o instanceof Long) return ((Long)o).longValue() != 0; - if (o instanceof Integer) return ((Integer)o).intValue() != 0; - if (o instanceof Number) { - double d = ((Number) o).doubleValue(); - // NOTE: d == d is a test for NaN. It should be faster than Double.isNaN() - return d != 0.0 && d == d; - } - if (o instanceof String) return ((String)o).length() != 0; + public static boolean toBoolean(JS o) { + if(o == null) return false; + if(o instanceof JSNumber) return ((JSNumber)o).toBoolean(); + if(o instanceof JSString) return ((JSString)o).length() != 0; return true; } - - /** coerce an object to a Long */ - public static long toLong(Object o) { return toNumber(o).longValue(); } - - /** coerce an object to an Int */ - public static int toInt(Object o) { return toNumber(o).intValue(); } - - /** coerce an object to a Double */ - public static double toDouble(Object o) { return toNumber(o).doubleValue(); } - + //#repeat long/int/double/float toLong/toInt/toDouble/toFloat Long/Integer/Double/Float parseLong/parseInt/parseDouble/parseFloat /** coerce an object to a Number */ - public static Number toNumber(Object o) { - if (o == null) return ZERO; - if (o instanceof Number) return ((Number)o); - - // 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 N((String)o); } catch (NumberFormatException e) { return N(Double.NaN); } - if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? N(1) : ZERO; - throw new Error("toNumber() got object of type " + o.getClass().getName() + " which we don't know how to handle"); + public static long toLong(JS o) throws JSExn { + if(o == null) return 0; + if(o instanceof JSNumber) return ((JSNumber)o).toLong(); + if(o instanceof JSString) return Long.parseLong(o.coerceToString()); + throw new JSExn("can't coerce a " + o.getClass().getName() + " to a number"); } - - /** coerce an object to a String */ - public static String toString(Object o) { + //#end + + public static String toString(JS o) throws JSExn { if(o == null) return "null"; - if(o instanceof String) return (String) o; - if(o instanceof Integer || o instanceof Long || o instanceof Boolean) return o.toString(); - if(o instanceof JSArray) return o.toString(); - if(o instanceof JSDate) return o.toString(); - if(o instanceof Double || o instanceof Float) { - double d = ((Number)o).doubleValue(); - if((int)d == d) return Integer.toString((int)d); - return o.toString(); + return o.coerceToString(); + } + + public static String debugToString(JS o) { + try { return toString(o); } + catch(JSExn e) { return "[class=" + o.getClass().getName() + "]"; } + } + + public static boolean isInt(JS o) { + if(o == null) return true; + if(o instanceof JSNumber.I) return true; + if(o instanceof JSNumber.B) return false; + if(o instanceof JSNumber) { + JSNumber n = (JSNumber) o; + return n.toInt() == n.toDouble(); } - if (o instanceof JS) return ((JS)o).coerceToString(); // HACK for now, this will probably go away - throw new RuntimeException("can't coerce "+o+" [" + o.getClass().getName() + "] to type String."); + if(o instanceof JSString) { + String s = ((JSString)o).s; + for(int i=0;i '9') return false; + return true; + } + return false; } - - public String coerceToString() { - throw new RuntimeException("can't coerce "+this+" [" + getClass().getName() + "] to type String."); + + public static boolean isString(JS o) { + if(o instanceof JSString) return true; + return false; } // Instance Methods //////////////////////////////////////////////////////////////////// - - public static final Integer ZERO = new Integer(0); - // this gets around a wierd fluke in the Java type checking rules for ?..: - public static final Object T = Boolean.TRUE; - public static final Object F = Boolean.FALSE; + public final static JS NaN = new JSNumber.D(Double.NaN); + public final static JS ZERO = new JSNumber.I(0); + public final static JS ONE = new JSNumber.I(1); + + public static final JS T = new JSNumber.B(true); + public static final JS F = new JSNumber.B(false); - public static final Boolean B(boolean b) { return b ? Boolean.TRUE : Boolean.FALSE; } - public static final Boolean B(int i) { return i==0 ? Boolean.FALSE : Boolean.TRUE; } - public static final Number N(String s) { return s.indexOf('.') == -1 ? N(Integer.parseInt(s)) : new Double(s); } - public static final Number N(double d) { return (int)d == d ? N((int)d) : new Double(d); } - public static final Number N(long l) { return N((int)l); } + public static final JS B(boolean b) { return b ? T : F; } + public static final JS B(int i) { return i==0 ? F : T; } + + public static final JS S(String s) { return new JSString(s); } + + public static final JS N(double d) { return new JSNumber.D(d); } + public static final JS N(long l) { return new JSNumber.L(l); } + + public static final JS N(Number n) { + if(n instanceof Integer) return N(n.intValue()); + if(n instanceof Long) return N(n.longValue()); + return N(n.doubleValue()); + } - private static final Integer[] smallIntCache = new Integer[65535 / 4]; - private static final Integer[] largeIntCache = new Integer[65535 / 4]; - public static final Number N(int i) { - Integer ret = null; + private static final JSNumber.I[] smallIntCache = new JSNumber.I[65535 / 4]; + private static final JSNumber.I[] largeIntCache = new JSNumber.I[65535 / 4]; + public static final JS N(int i) { + JSNumber.I ret = null; int idx = i + smallIntCache.length / 2; if (idx < smallIntCache.length && idx > 0) { ret = smallIntCache[idx]; if (ret != null) return ret; } else ret = largeIntCache[Math.abs(idx % largeIntCache.length)]; - if (ret == null || ret.intValue() != i) { - ret = new Integer(i); + if (ret == null || ret.i != i) { + ret = new JSNumber.I(i); if (idx < smallIntCache.length && idx > 0) smallIntCache[idx] = ret; else largeIntCache[Math.abs(idx % largeIntCache.length)] = ret; } @@ -230,30 +241,30 @@ public abstract class JS { // Trap support ////////////////////////////////////////////////////////////////////////////// /** performs a put, triggering traps if present; traps are run in an unpauseable interpreter */ - public void putAndTriggerTraps(Object key, Object value) throws JSExn { + public void putAndTriggerTraps(JS key, JS value) throws JSExn { Trap t = getTrap(key); if (t != null) t.invoke(value); else put(key, value); } /** performs a get, triggering traps if present; traps are run in an unpauseable interpreter */ - public Object getAndTriggerTraps(Object key) throws JSExn { + public JS getAndTriggerTraps(JS key) throws JSExn { Trap t = getTrap(key); if (t != null) return t.invoke(); else return get(key); } /** adds a trap, avoiding duplicates */ - protected final void addTrap(Object name, JSFunction f) throws JSExn { + final void addTrap(JS key, JSFunction f) throws JSExn { if (f.numFormalArgs > 1) throw new JSExn("traps must take either one argument (write) or no arguments (read)"); boolean isRead = f.numFormalArgs == 0; - for(Trap t = getTrap(name); t != null; t = t.next) if (t.f == f) return; - putTrap(name, new Trap(this, name.toString(), f, (Trap)getTrap(name))); + for(Trap t = getTrap(key); t != null; t = t.next) if (t.f == f) return; + putTrap(key, new Trap(this, key, f, (Trap)getTrap(key))); } /** deletes a trap, if present */ - protected final void delTrap(Object name, JSFunction f) throws JSExn { - Trap t = (Trap)getTrap(name); + final void delTrap(JS key, JSFunction f) throws JSExn { + Trap t = (Trap)getTrap(key); if (t == null) return; if (t.f == f) { putTrap(t.name, t.next); return; } for(; t.next != null; t = t.next) if (t.next.f == f) { t.next = t.next.next; return; } diff --git a/src/org/ibex/js/JSArray.java b/src/org/ibex/js/JSArray.java index 168bab3..d9ffad1 100644 --- a/src/org/ibex/js/JSArray.java +++ b/src/org/ibex/js/JSArray.java @@ -11,7 +11,7 @@ public class JSArray extends JS.BT { public JSArray() { } public JSArray(int size) { setSize(size); } - private static int intVal(Object o) { + /*private static int intVal(Object o) { if (o instanceof Number) { int intVal = ((Number)o).intValue(); if (intVal == ((Number)o).doubleValue()) return intVal; @@ -21,72 +21,73 @@ public class JSArray extends JS.BT { String s = (String)o; for(int i=0; i '9') return Integer.MIN_VALUE; return Integer.parseInt(s); - } + }*/ - public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn { - //#switch(method) - case "pop": { - int oldSize = size(); - if(oldSize == 0) return null; - return removeElementAt(oldSize-1); - } - case "reverse": return reverse(); - case "toString": return join(","); - case "shift": - if(length() == 0) return null; - return removeElementAt(0); - case "join": - return join(nargs == 0 ? "," : JS.toString(a0)); - case "sort": - return sort(nargs < 1 ? null : a0); - case "slice": - int start = toInt(nargs < 1 ? null : a0); - int end = nargs < 2 ? length() : toInt(a1); - return slice(start, end); - case "push": { - int oldSize = size(); - for(int i=0; i= size()) return null; return elementAt(i); } - //#switch(key) - case "pop": return METHOD; - case "reverse": return METHOD; - case "toString": return METHOD; - case "shift": return METHOD; - case "join": return METHOD; - case "sort": return METHOD; - case "slice": return METHOD; - case "push": return METHOD; - case "unshift": return METHOD; - case "splice": return METHOD; - case "length": return N(size()); - //#end + if(isString(key)) { + //#switch(JS.toString(key)) + case "pop": return METHOD; + case "reverse": return METHOD; + case "toString": return METHOD; + case "shift": return METHOD; + case "join": return METHOD; + case "sort": return METHOD; + case "slice": return METHOD; + case "push": return METHOD; + case "unshift": return METHOD; + case "splice": return METHOD; + case "length": return N(size()); + //#end + } return super.get(key); } - public void put(Object key, Object val) throws JSExn { - if (key.equals("length")) setSize(toInt(val)); - int i = intVal(key); - if (i == Integer.MIN_VALUE) - super.put(key, val); - else { + public void put(JS key, JS val) throws JSExn { + if (isInt(key)) { + int i = toInt(key); int oldSize = size(); if(i < oldSize) { setElementAt(val,i); @@ -95,8 +96,16 @@ public class JSArray extends JS.BT { insertElementAt(val,i); } } + if(isString(key)) { + if (JS.toString(key).equals("length")) { + setSize(JS.toInt(val)); + return; + } + } + super.put(key,val); } + // FIXME: Needs to include super's keys public Enumeration keys() { return new Enumeration() { private int n = size(); @@ -116,56 +125,55 @@ public class JSArray extends JS.BT { } public final int length() { return size(); } - public final Object elementAt(int i) { + public final JS elementAt(int i) { if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i); - Object o = getNode(i); + JS o = (JS) getNode(i); return o == NULL ? null : o; } - public final void addElement(Object o) { + public final void addElement(JS o) { insertNode(size(),o==null ? NULL : o); } - public final void setElementAt(Object o, int i) { + public final void setElementAt(JS o, int i) { if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i); replaceNode(i,o==null ? NULL : o); } - public final void insertElementAt(Object o, int i) { + public final void insertElementAt(JS o, int i) { if(i < 0 || i > size()) throw new ArrayIndexOutOfBoundsException(i); insertNode(i,o==null ? NULL : o); } - public final Object removeElementAt(int i) { + public final JS removeElementAt(int i) { if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i); - Object o = deleteNode(i); + JS o = (JS) deleteNode(i); return o == NULL ? null : o; } public final int size() { return treeSize(); } - public String typeName() { return "array"; } - - private Object join(String sep) { + + private JS join(String sep) throws JSExn { int length = size(); - if(length == 0) return ""; + if(length == 0) return JS.S(""); StringBuffer sb = new StringBuffer(64); int i=0; while(true) { - Object o = elementAt(i); + JS o = elementAt(i); if(o != null) sb.append(JS.toString(o)); if(++i == length) break; sb.append(sep); } - return sb.toString(); + return JS.S(sb.toString()); } // FEATURE: Implement this more efficiently - private Object reverse() { + private JS reverse() { int size = size(); if(size < 2) return this; Vec vec = toVec(); clear(); - for(int i=size-1,j=0;i>=0;i--,j++) insertElementAt(vec.elementAt(i),j); + for(int i=size-1,j=0;i>=0;i--,j++) insertElementAt((JS)vec.elementAt(i),j); return this; } - private Object slice(int start, int end) { + private JS slice(int start, int end) { int length = length(); if(start < 0) start = length+start; if(end < 0) end = length+end; @@ -181,23 +189,25 @@ public class JSArray extends JS.BT { private static final Vec.CompareFunc defaultSort = new Vec.CompareFunc() { public int compare(Object a, Object b) { - return JS.toString(a).compareTo(JS.toString(b)); + try { + return JS.toString((JS)a).compareTo(JS.toString((JS)b)); + } catch(JSExn e) { + // FIXME: See emca about this + throw new RuntimeException(e.toString()); + } } }; - private Object sort(Object tmp) throws JSExn { + private JS sort(JS tmp) throws JSExn { Vec vec = toVec(); if(tmp instanceof JS) { - final JSArray funcArgs = new JSArray(2); final JS jsFunc = (JS) tmp; vec.sort(new Vec.CompareFunc() { public int compare(Object a, Object b) { try { - funcArgs.setElementAt(a,0); - funcArgs.setElementAt(b,1); - return JS.toInt(jsFunc.call(a, b, null, null, 2)); - } catch (Exception e) { - // FIXME - throw new JSRuntimeExn(e.toString()); + return JS.toInt(jsFunc.call((JS)a, (JS)b, null, null, 2)); + } catch (JSExn e) { + // FIXME: Check ecma to see what we should do here + throw new RuntimeException(e.toString()); } } }); @@ -208,7 +218,7 @@ public class JSArray extends JS.BT { return this; } - private Object splice(JSArray args) { + private JS splice(JSArray args) throws JSExn { int oldLength = length(); int start = JS.toInt(args.length() < 1 ? null : args.elementAt(0)); int deleteCount = JS.toInt(args.length() < 2 ? null : args.elementAt(1)); @@ -258,5 +268,5 @@ public class JSArray extends JS.BT { } } - public String toString() { return JS.toString(join(",")); } + String coerceToString() throws JSExn { return JS.toString(join(",")); } } diff --git a/src/org/ibex/js/JSDate.java b/src/org/ibex/js/JSDate.java index 694122c..600b59d 100644 --- a/src/org/ibex/js/JSDate.java +++ b/src/org/ibex/js/JSDate.java @@ -55,19 +55,21 @@ public class JSDate extends JS { } } - public String toString() { return date_format(date, FORMATSPEC_FULL); } + String coerceToString() { return date_format(date, FORMATSPEC_FULL); } - public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn { + public JS callMethod(JS method_, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn { + if(!JS.isString(method_)) return super.callMethod(method_, a0, a1, a2, rest, nargs); + String method = JS.toString(method_); switch(nargs) { case 0: { //#switch(method) - case "toString": return date_format(date, FORMATSPEC_FULL); - case "toTimeString": return date_format(date, FORMATSPEC_TIME); - case "toDateString": return date_format(date, FORMATSPEC_DATE); - case "toLocaleString": return toLocaleString(date); - case "toLocaleTimeString": return toLocaleTimeString(date); - case "toLocaleDateString": return toLocaleDateString(date); - case "toUTCString": return toUTCString(date); + case "toString": return JS.S(date_format(date, FORMATSPEC_FULL)); + case "toTimeString": return JS.S(date_format(date, FORMATSPEC_TIME)); + case "toDateString": return JS.S(date_format(date, FORMATSPEC_DATE)); + case "toLocaleString": return JS.S(toLocaleString(date)); + case "toLocaleTimeString": return JS.S(toLocaleTimeString(date)); + case "toLocaleDateString": return JS.S(toLocaleDateString(date)); + case "toUTCString": return JS.S(toUTCString(date)); case "valueOf": return N(this.date); case "getTime": return N(this.date); case "getYear": return N(getYear(date)); @@ -89,7 +91,7 @@ public class JSDate extends JS { case "getUTCMilliseconds": return N(msFromTime(date)); case "getTimezoneOffset": return N(getTimezoneOffset(date)); //#end - return super.callMethod(method, a0, a1, a2, rest, nargs); + return super.callMethod(method_, a0, a1, a2, rest, nargs); } case 1: { //#switch(method) @@ -99,7 +101,7 @@ public class JSDate extends JS { // fall through } default: { - Object[] args = new Object[nargs]; + JS[] args = new JS[nargs]; for(int i=0; i=0; i--) { - Object element = stack.elementAt(i); + JS element = stack.elementAt(i); if (element instanceof Interpreter.CallMarker) { Interpreter.CallMarker cm = (Interpreter.CallMarker)element; if (cm.f != null) @@ -39,9 +40,9 @@ public class JSExn extends Exception { for(int i=0; iunpauseable context. */ - public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn { + public JS call(JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn { JSArray args = new JSArray(); if (nargs > 0) args.addElement(a0); if (nargs > 1) args.addElement(a1); @@ -103,7 +103,8 @@ class JSFunction extends JS implements ByteCodes, Tokens, Task { // Debugging ////////////////////////////////////////////////////////////////////// - public String toString() { return "JSFunction [" + sourceName + ":" + firstLine + "]"; } + // FIXME: Put this back in + public String xtoString() { return "JSFunction [" + sourceName + ":" + firstLine + "]"; } String dump() { return dump(""); } private String dump(String prefix) { @@ -115,7 +116,7 @@ class JSFunction extends JS implements ByteCodes, Tokens, Task { if (op[i] < 0) sb.append(bytecodeToString[-op[i]]); else sb.append(codeToString[op[i]]); sb.append(" "); - sb.append(arg[i] == null ? "(no arg)" : arg[i]); + sb.append(arg[i] == null ? "(no arg)" : arg[i] instanceof JS ? JS.debugToString((JS)arg[i]) : arg[i]); if((op[i] == JF || op[i] == JT || op[i] == JMP) && arg[i] != null && arg[i] instanceof Number) { sb.append(" jump to ").append(i+((Number) arg[i]).intValue()); } else if(op[i] == TRY) { diff --git a/src/org/ibex/js/JSMath.java b/src/org/ibex/js/JSMath.java index 04828ed..835eefa 100644 --- a/src/org/ibex/js/JSMath.java +++ b/src/org/ibex/js/JSMath.java @@ -7,57 +7,59 @@ public class JSMath extends JS { public static JSMath singleton = new JSMath(); - private static final Double E = new Double(java.lang.Math.E); - private static final Double PI = new Double(java.lang.Math.PI); - private static final Double LN10 = new Double(java.lang.Math.log(10)); - private static final Double LN2 = new Double(java.lang.Math.log(2)); - private static final Double LOG10E = new Double(1/java.lang.Math.log(10)); - private static final Double LOG2E = new Double(1/java.lang.Math.log(2)); - private static final Double SQRT1_2 = new Double(1/java.lang.Math.sqrt(2)); - private static final Double SQRT2 = new Double(java.lang.Math.sqrt(2)); + private static final JS E = JS.N(java.lang.Math.E); + private static final JS PI = JS.N(java.lang.Math.PI); + private static final JS LN10 = JS.N(java.lang.Math.log(10)); + private static final JS LN2 = JS.N(java.lang.Math.log(2)); + private static final JS LOG10E = JS.N(1/java.lang.Math.log(10)); + private static final JS LOG2E = JS.N(1/java.lang.Math.log(2)); + private static final JS SQRT1_2 = JS.N(1/java.lang.Math.sqrt(2)); + private static final JS SQRT2 = JS.N(java.lang.Math.sqrt(2)); - public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn { + public JS callMethod(JS method_, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn { + if(!JS.isString(method_)) return super.callMethod(method_,a0,a1,a2,rest,nargs); + String method = JS.toString(method_); switch(nargs) { case 0: { //#switch(method) - case "random": return new Double(java.lang.Math.random()); + case "random": return JS.N(java.lang.Math.random()); //#end break; } case 1: { //#switch(method) - case "ceil": return new Long((long)java.lang.Math.ceil(toDouble(a0))); - case "floor": return new Long((long)java.lang.Math.floor(toDouble(a0))); - case "round": return new Long((long)java.lang.Math.round(toDouble(a0))); - case "abs": return new Double(java.lang.Math.abs(toDouble(a0))); - case "sin": return new Double(java.lang.Math.sin(toDouble(a0))); - case "cos": return new Double(java.lang.Math.cos(toDouble(a0))); - case "tan": return new Double(java.lang.Math.tan(toDouble(a0))); - case "asin": return new Double(java.lang.Math.asin(toDouble(a0))); - case "acos": return new Double(java.lang.Math.acos(toDouble(a0))); - case "atan": return new Double(java.lang.Math.atan(toDouble(a0))); - case "sqrt": return new Double(java.lang.Math.sqrt(toDouble(a0))); - case "exp": return new Double(java.lang.Math.exp(toDouble(a0))); - case "log": return new Double(java.lang.Math.log(toDouble(a0))); + case "ceil": return JS.N((long)java.lang.Math.ceil(toDouble(a0))); + case "floor": return JS.N((long)java.lang.Math.floor(toDouble(a0))); + case "round": return JS.N((long)java.lang.Math.round(toDouble(a0))); + case "abs": return JS.N(java.lang.Math.abs(toDouble(a0))); + case "sin": return JS.N(java.lang.Math.sin(toDouble(a0))); + case "cos": return JS.N(java.lang.Math.cos(toDouble(a0))); + case "tan": return JS.N(java.lang.Math.tan(toDouble(a0))); + case "asin": return JS.N(java.lang.Math.asin(toDouble(a0))); + case "acos": return JS.N(java.lang.Math.acos(toDouble(a0))); + case "atan": return JS.N(java.lang.Math.atan(toDouble(a0))); + case "sqrt": return JS.N(java.lang.Math.sqrt(toDouble(a0))); + case "exp": return JS.N(java.lang.Math.exp(toDouble(a0))); + case "log": return JS.N(java.lang.Math.log(toDouble(a0))); //#end break; } case 2: { //#switch(method) - case "min": return new Double(java.lang.Math.min(toDouble(a0), toDouble(a1))); - case "max": return new Double(java.lang.Math.max(toDouble(a0), toDouble(a1))); - case "pow": return new Double(java.lang.Math.pow(toDouble(a0), toDouble(a1))); - case "atan2": return new Double(java.lang.Math.atan2(toDouble(a0), toDouble(a1))); + case "min": return JS.N(java.lang.Math.min(toDouble(a0), toDouble(a1))); + case "max": return JS.N(java.lang.Math.max(toDouble(a0), toDouble(a1))); + case "pow": return JS.N(java.lang.Math.pow(toDouble(a0), toDouble(a1))); + case "atan2": return JS.N(java.lang.Math.atan2(toDouble(a0), toDouble(a1))); //#end break; } } - return super.callMethod(method, a0, a1, a2, rest, nargs); + return super.callMethod(method_, a0, a1, a2, rest, nargs); } - public void put(Object key, Object val) { } - - public Object get(Object key) throws JSExn { + public JS get(JS key_) throws JSExn { + if(!JS.isString(key_)) return super.get(key_); + String key = JS.toString(key_); //#switch(key) case "E": return E; case "LN10": return LN10; @@ -86,6 +88,6 @@ public class JSMath extends JS { case "log": return METHOD; case "random": return METHOD; //#end - return super.get(key); + return super.get(key_); } } diff --git a/src/org/ibex/js/JSReflection.java b/src/org/ibex/js/JSReflection.java index 69fb9b3..d2a9d14 100644 --- a/src/org/ibex/js/JSReflection.java +++ b/src/org/ibex/js/JSReflection.java @@ -6,10 +6,11 @@ import java.io.*; import java.util.*; import java.lang.reflect.*; +// FIXME: Update for new API /** Automatic JS-ification via Reflection (not for use in the core) */ public class JSReflection extends JS { - public static Object wrap(Object o) throws JSExn { + /*public static Object wrap(Object o) throws JSExn { if (o == null) return null; if (o instanceof String) return o; if (o instanceof Boolean) return o; @@ -76,5 +77,5 @@ public class JSReflection extends JS { throw new JSExn("unhandled reflected exception: " + ite.toString()); } catch (SecurityException nfe) { } throw new JSExn("called a reflection method with the wrong number of arguments"); - } + }*/ } diff --git a/src/org/ibex/js/JSRegexp.java b/src/org/ibex/js/JSRegexp.java index f203426..cb2c89d 100644 --- a/src/org/ibex/js/JSRegexp.java +++ b/src/org/ibex/js/JSRegexp.java @@ -8,18 +8,23 @@ public class JSRegexp extends JS { private boolean global; private RE re; private int lastIndex; + + private JS pattern; + private int flags; - public JSRegexp(Object arg0, Object arg1) throws JSExn { + public JSRegexp(JS arg0, JS arg1) throws JSExn { if(arg0 instanceof JSRegexp) { JSRegexp r = (JSRegexp) arg0; this.global = r.global; this.re = r.re; this.lastIndex = r.lastIndex; + this.pattern = pattern; + this.flags = flags; } else { - String pattern = (String)arg0; + String pattern = JS.toString(arg0); String sFlags = null; int flags = 0; - if(arg1 != null) sFlags = (String)arg1; + if(arg1 != null) sFlags = JS.toString(arg1); if(sFlags == null) sFlags = ""; for(int i=0;i= s.length()) { lastIndex = 0; return null; } REMatch match = re.getMatch(s,start); @@ -50,7 +55,7 @@ public class JSRegexp extends JS { return match == null ? null : matchToExecResult(match,re,s); } case "test": { - String s = (String)a0; + String s = JS.toString(a0); if (!global) return B(re.getMatch(s) != null); int start = global ? lastIndex : 0; if(start < 0 || start >= s.length()) { lastIndex = 0; return null; } @@ -58,7 +63,7 @@ public class JSRegexp extends JS { lastIndex = match != null ? s.length() : match.getEndIndex(); return B(match != null); } - case "toString": return toString(a0); + case "toString": return JS.S(a0.coerceToString()); case "stringMatch": return stringMatch(a0,a1); case "stringSearch": return stringSearch(a0,a1); //#end @@ -71,87 +76,94 @@ public class JSRegexp extends JS { break; } } - return super.callMethod(method, a0, a1, a2, rest, nargs); + return super.callMethod(method_, a0, a1, a2, rest, nargs); } - public Object get(Object key) throws JSExn { + public JS get(JS key_) throws JSExn { + if(!JS.isString(key_)) return super.get(key_); + String key = JS.toString(key_); //#switch(key) case "exec": return METHOD; case "test": return METHOD; case "toString": return METHOD; case "lastIndex": return N(lastIndex); + case "source": return pattern; + case "global": return JS.B(global); + case "ignoreCase": return B(flags & RE.REG_ICASE); + case "multiline": return B(flags & RE.REG_MULTILINE); //#end - return super.get(key); + return super.get(key_); } - public void put(Object key, Object value) throws JSExn { - if(key.equals("lastIndex")) lastIndex = JS.toNumber(value).intValue(); + public void put(JS key, JS value) throws JSExn { + if(JS.isString(key)) { + if(JS.toString(key).equals("lastIndex")) { + lastIndex = JS.toInt(value); + return; + } + } super.put(key,value); } - private static Object matchToExecResult(REMatch match, RE re, String s) { + private static JS matchToExecResult(REMatch match, RE re, String s) { try { JS ret = new JS.O(); - ret.put("index", N(match.getStartIndex())); - ret.put("input",s); + ret.put(JS.S("index"), N(match.getStartIndex())); + ret.put(JS.S("input"),JS.S(s)); int n = re.getNumSubs(); - ret.put("length", N(n+1)); - ret.put("0",match.toString()); - for(int i=1;i<=n;i++) ret.put(Integer.toString(i),match.toString(i)); + ret.put(JS.S("length"), N(n+1)); + ret.put(ZERO,JS.S(match.toString())); + for(int i=1;i<=n;i++) ret.put(JS.N(i),JS.S(match.toString(i))); return ret; } catch (JSExn e) { throw new Error("this should never happen"); } } - public String toString() { - try { - StringBuffer sb = new StringBuffer(); - sb.append('/'); - sb.append(get("source")); - sb.append('/'); - if(global) sb.append('g'); - if(Boolean.TRUE.equals(get("ignoreCase"))) sb.append('i'); - if(Boolean.TRUE.equals(get("multiline"))) sb.append('m'); - return sb.toString(); - } catch (JSExn e) { - throw new Error("this should never happen"); - } + String coerceToString() { + StringBuffer sb = new StringBuffer(); + sb.append('/'); + sb.append(pattern); + sb.append('/'); + if(global) sb.append('g'); + if((flags & RE.REG_ICASE) != 0) sb.append('i'); + if((flags & RE.REG_MULTILINE) != 0) sb.append('m'); + return sb.toString(); } - public static Object stringMatch(Object o, Object arg0) throws JSExn { - String s = o.toString(); + public static JS stringMatch(JS o, JS arg0) throws JSExn { + String s = JS.toString(o); RE re; JSRegexp regexp = null; if(arg0 instanceof JSRegexp) { regexp = (JSRegexp) arg0; re = regexp.re; } else { - re = newRE(arg0.toString(),0); + re = newRE(JS.toString(arg0),0); } if(regexp == null) { REMatch match = re.getMatch(s); return matchToExecResult(match,re,s); } - if(!regexp.global) return regexp.callMethod("exec", s, null, null, null, 1); + if(!regexp.global) return regexp.callMethod(JS.S("exec"), o, null, null, null, 1); JSArray ret = new JSArray(); REMatch[] matches = re.getAllMatches(s); - for(int i=0;i 0 ? matches[matches.length-1].getEndIndex() : s.length(); return ret; } - public static Object stringSearch(Object o, Object arg0) throws JSExn { - String s = o.toString(); - RE re = arg0 instanceof JSRegexp ? ((JSRegexp)arg0).re : newRE(arg0.toString(),0); + public static JS stringSearch(JS o, JS arg0) throws JSExn { + String s = JS.toString(o); + RE re = arg0 instanceof JSRegexp ? ((JSRegexp)arg0).re : newRE(JS.toString(arg0),0); REMatch match = re.getMatch(s); return match == null ? N(-1) : N(match.getStartIndex()); } - public static Object stringReplace(Object o, Object arg0, Object arg1) throws JSExn { - String s = o.toString(); + public static JS stringReplace(JS o, JS arg0, JS arg1) throws JSExn { + String s = JS.toString(o); RE re; JSFunction replaceFunc = null; String replaceString = null; @@ -165,7 +177,7 @@ public class JSRegexp extends JS { if(arg1 instanceof JSFunction) replaceFunc = (JSFunction) arg1; else - replaceString = JS.toString(arg1.toString()); + replaceString = JS.toString(arg1); REMatch[] matches; if(regexp != null && regexp.global) { matches = re.getAllMatches(s); @@ -193,32 +205,32 @@ public class JSRegexp extends JS { if(replaceFunc != null) { int n = (regexp == null ? 0 : re.getNumSubs()); int numArgs = 3 + n; - Object[] rest = new Object[numArgs - 3]; - Object a0 = match.toString(); - Object a1 = null; - Object a2 = null; + JS[] rest = new JS[numArgs - 3]; + JS a0 = JS.S(match.toString()); + JS a1 = null; + JS a2 = null; for(int j=1;j<=n;j++) switch(j) { - case 1: a1 = match.toString(j); break; - case 2: a2 = match.toString(j); break; - default: rest[j - 3] = match.toString(j); break; + case 1: a1 = JS.S(match.toString(j)); break; + case 2: a2 = JS.S(match.toString(j)); break; + default: rest[j - 3] = JS.S(match.toString(j)); break; } switch(numArgs) { case 3: a1 = N(match.getStartIndex()); - a2 = s; + a2 = JS.S(s); break; case 4: a2 = N(match.getStartIndex()); - rest[0] = s; + rest[0] = JS.S(s); break; default: rest[rest.length - 2] = N(match.getStartIndex()); - rest[rest.length - 1] = s; + rest[rest.length - 1] = JS.S(s); } // note: can't perform pausing operations in here - sb.append((String)replaceFunc.call(a0, a1, a2, rest, numArgs)); + sb.append(JS.toString(replaceFunc.call(a0, a1, a2, rest, numArgs))); } else { sb.append(mySubstitute(match,replaceString,s)); @@ -226,7 +238,7 @@ public class JSRegexp extends JS { } int end = matches.length == 0 ? 0 : matches[matches.length-1].getEndIndex(); sb.append(sa,end,sa.length-end); - return sb.toString(); + return JS.S(sb.toString()); } private static String mySubstitute(REMatch match, String s, String source) { @@ -271,7 +283,7 @@ public class JSRegexp extends JS { } - public static Object stringSplit(String s, Object arg0, Object arg1, int nargs) { + public static JS stringSplit(String s, JS arg0, JS arg1, int nargs) throws JSExn { int limit = nargs < 2 ? Integer.MAX_VALUE : JS.toInt(arg1); if(limit < 0) limit = Integer.MAX_VALUE; if(limit == 0) return new JSArray(); @@ -286,7 +298,7 @@ public class JSRegexp extends JS { regexp = (JSRegexp) arg0; re = regexp.re; } else { - sep = arg0.toString(); + sep = JS.toString(arg0); } // special case this for speed. additionally, the code below doesn't properly handle @@ -294,7 +306,7 @@ public class JSRegexp extends JS { if(sep != null && sep.length()==0) { int len = s.length(); for(int i=0;i= Integer.MIN_VALUE && longval <= Integer.MAX_VALUE) this.number = new Integer((int)longval); + else this.number = new Long(longval); return NUMBER; } diff --git a/src/org/ibex/js/Parser.java b/src/org/ibex/js/Parser.java index 38d15ea..c6c9d9e 100644 --- a/src/org/ibex/js/Parser.java +++ b/src/org/ibex/js/Parser.java @@ -169,18 +169,18 @@ class Parser extends Lexer implements ByteCodes { case -1: throw pe("expected expression"); // all of these simply push values onto the stack - case NUMBER: b.add(parserLine, LITERAL, number); break; - case STRING: b.add(parserLine, LITERAL, string); break; + case NUMBER: b.add(parserLine, LITERAL, JS.N(number)); break; + case STRING: b.add(parserLine, LITERAL, JSString.intern(string)); break; case NULL: b.add(parserLine, LITERAL, null); break; - case TRUE: case FALSE: b.add(parserLine, LITERAL, JS.B(tok == TRUE)); break; + case TRUE: case FALSE: b.add(parserLine, LITERAL, tok == TRUE ? JS.T : JS.F); break; // (.foo) syntax case DOT: { consume(NAME); b.add(parserLine, TOPSCOPE); - b.add(parserLine, LITERAL, ""); + b.add(parserLine, LITERAL, JSString.intern("")); b.add(parserLine, GET); - b.add(parserLine, LITERAL, string); + b.add(parserLine, LITERAL, JSString.intern(string)); b.add(parserLine, GET); continueExpr(b, minPrecedence); break; @@ -253,7 +253,7 @@ class Parser extends Lexer implements ByteCodes { if (peekToken() != NAME && peekToken() != STRING) throw pe("expected NAME or STRING"); getToken(); - b.add(parserLine, LITERAL, string); // grab the key + b.add(parserLine, LITERAL, JSString.intern(string)); // grab the key consume(COLON); startExpr(b, NO_COMMA); // grab the value b.add(parserLine, PUT); // put the value into the object @@ -267,7 +267,7 @@ class Parser extends Lexer implements ByteCodes { } case NAME: { b.add(parserLine, TOPSCOPE); - b.add(parserLine, LITERAL, string); + b.add(parserLine, LITERAL, JSString.intern(string)); continueExprAfterAssignable(b,minPrecedence); break; } @@ -280,7 +280,7 @@ class Parser extends Lexer implements ByteCodes { // function prelude; arguments array is already on the stack b2.add(parserLine, TOPSCOPE); b2.add(parserLine, SWAP); - b2.add(parserLine, DECLARE, "arguments"); // declare arguments (equivalent to 'var arguments;') + b2.add(parserLine, DECLARE, JSString.intern("arguments")); // declare arguments (equivalent to 'var arguments;') b2.add(parserLine, SWAP); // set this.arguments and leave the value on the stack b2.add(parserLine, PUT); @@ -288,7 +288,7 @@ class Parser extends Lexer implements ByteCodes { numArgs++; if (peekToken() == NAME) { consume(NAME); // a named argument - String varName = string; + JS varName = JSString.intern(string); b2.add(parserLine, DUP); // dup the args array b2.add(parserLine, GET, JS.N(numArgs - 1)); // retrieve it from the arguments array @@ -515,7 +515,7 @@ class Parser extends Lexer implements ByteCodes { } else { consume(NAME); } - b.add(parserLine, LITERAL, string); + b.add(parserLine, LITERAL, JSString.intern(string)); continueExprAfterAssignable(b,minPrecedence); break; } @@ -616,7 +616,7 @@ class Parser extends Lexer implements ByteCodes { b.add(parserLine, TOPSCOPE); // push the current scope while(true) { consume(NAME); - b.add(parserLine, DECLARE, string); // declare it + b.add(parserLine, DECLARE, JSString.intern(string)); // declare it if (peekToken() == ASSIGN) { // if there is an '=' after the variable name consume(ASSIGN); startExpr(b, NO_COMMA); @@ -749,7 +749,7 @@ class Parser extends Lexer implements ByteCodes { // extended Ibex catch block: catch(e faultCode "foo.bar.baz") consume(NAME); b.add(parserLine, DUP); - b.add(parserLine, LITERAL, string); + b.add(parserLine, LITERAL, JSString.intern(string)); b.add(parserLine, GET); b.add(parserLine, DUP); b.add(parserLine, LITERAL, null); @@ -785,7 +785,7 @@ class Parser extends Lexer implements ByteCodes { b.add(parserLine, NEWSCOPE); b.add(parserLine, TOPSCOPE); b.add(parserLine, SWAP); - b.add(parserLine, LITERAL,exceptionVar); + b.add(parserLine, LITERAL,JSString.intern(exceptionVar)); b.add(parserLine, DECLARE); b.add(parserLine, SWAP); b.add(parserLine, PUT); @@ -850,7 +850,7 @@ class Parser extends Lexer implements ByteCodes { b.add(parserLine, PUSHKEYS); b.add(parserLine, DUP); - b.add(parserLine, LITERAL, "length"); + b.add(parserLine, LITERAL, JSString.intern("length")); b.add(parserLine, GET); // Stack is now: n, keys, obj, ... @@ -881,7 +881,7 @@ class Parser extends Lexer implements ByteCodes { b.add(parserLine, NEWSCOPE); if(hadVar) { - b.add(parserLine, DECLARE, varName); + b.add(parserLine, DECLARE, JSString.intern(varName)); b.add(parserLine, POP); } @@ -889,7 +889,7 @@ class Parser extends Lexer implements ByteCodes { b.add(parserLine, GET_PRESERVE); // key, index, keys, obj, LoopMarker, ... b.add(parserLine, TOPSCOPE); // scope, key, index, keys, obj, LoopMarker, ... b.add(parserLine, SWAP); // key, scope, index, keys, obj, LoopMarker, ... - b.add(parserLine, LITERAL, varName); // varName, key, scope, index, keys, obj, LoopMaker, ... + b.add(parserLine, LITERAL, JSString.intern(varName)); // varName, key, scope, index, keys, obj, LoopMaker, ... b.add(parserLine, SWAP); // key, varName, scope, index, keys, obj, LoopMarker, ... b.add(parserLine, PUT); // key, scope, index, keys, obj, LoopMarker, ... b.add(parserLine, POP); // scope, index, keys, obj, LoopMarker @@ -920,7 +920,7 @@ class Parser extends Lexer implements ByteCodes { if (peekToken() != SEMI) startExpr(e2, -1); else - e2.add(parserLine, JSFunction.LITERAL, Boolean.TRUE); // handle the for(foo;;foo) case + e2.add(parserLine, JSFunction.LITERAL, JS.T); // handle the for(foo;;foo) case consume(SEMI); if (label != null) b.add(parserLine, LABEL, label); b.add(parserLine, LOOP); diff --git a/src/org/ibex/js/PropertyFile.java b/src/org/ibex/js/PropertyFile.java index 3227922..f312a7e 100644 --- a/src/org/ibex/js/PropertyFile.java +++ b/src/org/ibex/js/PropertyFile.java @@ -5,10 +5,12 @@ import org.ibex.util.*; import java.util.*; import java.io.*; +// FIXME: Update for new api + /** A JS interface to a Java '.properties' file; very crude */ public class PropertyFile extends JS { - private final Properties p = new Properties(); + /*private final Properties p = new Properties(); private final Hash cache = new Hash(10, 3); private File f; @@ -47,5 +49,5 @@ public class PropertyFile extends JS { Object ret = p.get(toString(key)); if (ret != null) return ret; return new Minion(escape(toString(key))); - } + }*/ } diff --git a/src/org/ibex/js/Stream.java b/src/org/ibex/js/Stream.java index 72ee0a0..2344e76 100644 --- a/src/org/ibex/js/Stream.java +++ b/src/org/ibex/js/Stream.java @@ -17,15 +17,13 @@ public abstract class Stream extends JS implements JS.Cloneable { // Public Interface ////////////////////////////////////////////////////////////////////////////// - public static InputStream getInputStream(Object js) throws IOException { return ((Stream)((JS)js).unclone()).getInputStream();} + public static InputStream getInputStream(JS js) throws IOException { return ((Stream)js.unclone()).getInputStream();} public static class NotCacheableException extends Exception { } - // streams are "sealed" by default to prevent accidental object leakage - public void put(Object key, Object val) { } private Cache getCache = new Cache(100); - protected Object _get(Object key) { return null; } - public final Object get(Object key) { - Object ret = getCache.get(key); + protected JS _get(Object key) { return null; } + public final JS get(JS key) { + JS ret = (JS) getCache.get(key); if (ret == null) getCache.put(key, ret = _get(key)); return ret; } @@ -36,11 +34,12 @@ public abstract class Stream extends JS implements JS.Cloneable { protected String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); } /** HTTP or HTTPS resource */ + // FEATURE: Only instansiate only ibex.net.HTTP, share with all substreams public static class HTTP extends Stream { private String url; - public String toString() { return "Stream.HTTP:" + url; } + public String coerceToString() { return "Stream.HTTP:" + url; } public HTTP(String url) { while (url.endsWith("/")) url = url.substring(0, url.length() - 1); this.url = url; } - public Object _get(Object key) { return new HTTP(url + "/" + (String)key); } + public JS _get(JS key) throws JSExn { return new HTTP(url + "/" + JS.toString(key)); } public String getCacheKey(Vec path) throws NotCacheableException { return url; } public InputStream getInputStream() throws IOException { return new org.ibex.net.HTTP(url).GET(); } } @@ -59,10 +58,10 @@ public abstract class Stream extends JS implements JS.Cloneable { public static class File extends Stream { private String path; public File(String path) { this.path = path; } - public String toString() { return "file:" + path; } + public String coerceToString() { return "file:" + path; } public String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); /* already on disk */ } public InputStream getInputStream() throws IOException { return new FileInputStream(path); } - public Object _get(Object key) { return new File(path + java.io.File.separatorChar + (String)key); } + public JS _get(JS key) throws JSExn { return new File(path + java.io.File.separatorChar + JS.toString(key)); } } /** "unwrap" a Zip archive */ @@ -76,7 +75,7 @@ public abstract class Stream extends JS implements JS.Cloneable { this.path = path; } public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "!zip:"; } - public Object _get(Object key) { return new Zip(parent, path==null?(String)key:path+'/'+(String)key); } + public JS _get(JS key) throws JSExn { return new Zip(parent, path==null?JS.toString(key):path+'/'+JS.toString(key)); } public InputStream getInputStream() throws IOException { InputStream pis = parent.getInputStream(); ZipInputStream zis = new ZipInputStream(pis); @@ -94,7 +93,7 @@ public abstract class Stream extends JS implements JS.Cloneable { public Cab(Stream parent) { this(parent, null); } public Cab(Stream parent, String path) { this.parent = parent; this.path = path; } public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "!cab:"; } - public Object _get(Object key) { return new Cab(parent, path==null?(String)key:path+'/'+(String)key); } + public JS _get(JS key) throws JSExn { return new Cab(parent, path==null?JS.toString(key):path+'/'+JS.toString(key)); } public InputStream getInputStream() throws IOException { return new MSPack(parent.getInputStream()).getInputStream(path); } } diff --git a/src/org/ibex/js/Test.java b/src/org/ibex/js/Test.java index 92c6f95..5c7055e 100644 --- a/src/org/ibex/js/Test.java +++ b/src/org/ibex/js/Test.java @@ -6,20 +6,23 @@ public class Test extends JS { public static void main(String[] args) throws Exception { if(args.length == 0) { System.err.println("Usage Test filename"); System.exit(1); } JS f = JS.fromReader(args[0],0,new FileReader(args[0])); + System.out.println(((JSFunction)f).dump()); JSScope s = new JSScope(null); - s.put("sys",new Test()); + s.put(JS.S("sys"),new Test()); f = JS.cloneWithNewParentScope(f,s); - Object ret = f.call(null,null,null,null,0); + JS ret = f.call(null,null,null,null,0); System.out.println("Script returned: " + JS.toString(ret)); } - public Object get(Object key) throws JSExn { - if("print".equals(key)) return METHOD; + public JS get(JS key) throws JSExn { + if(!JS.isString(key)) return null; + if("print".equals(JS.toString(key))) return METHOD; return super.get(key); } - public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn { - if("print".equals(method)) { + public JS callMethod(JS method, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn { + if(!JS.isString(method)) return null; + if("print".equals(JS.toString(method))) { System.out.println(JS.toString(a0)); return null; } diff --git a/src/org/ibex/js/Trap.java b/src/org/ibex/js/Trap.java index e2566c5..248ae6a 100644 --- a/src/org/ibex/js/Trap.java +++ b/src/org/ibex/js/Trap.java @@ -10,12 +10,12 @@ package org.ibex.js; class Trap { JS trapee = null; ///< the box on which this trap was placed - Object name = null; ///< the property that the trap was placed on + JS name = null; ///< the property that the trap was placed on JSFunction f = null; ///< the function for this trap Trap next = null; ///< the next trap down the trap stack - Trap(JS b, String n, JSFunction f, Trap nx) { + Trap(JS b, JS n, JSFunction f, Trap nx) { trapee = b; name = n; this.f = f; this.next = nx; } @@ -32,7 +32,7 @@ class Trap { boolean readTrap() { return f.numFormalArgs == 0; } boolean writeTrap() { return f.numFormalArgs != 0; } - void invoke(Object value) throws JSExn { + void invoke(JS value) throws JSExn { Interpreter i = new Interpreter(putInvoker, false, null); i.stack.push(trapee); i.stack.push(name); @@ -40,7 +40,7 @@ class Trap { i.resume(); } - Object invoke() throws JSExn { + JS invoke() throws JSExn { Interpreter i = new Interpreter(getInvoker, false, null); i.stack.push(trapee); i.stack.push(name);