From e471904257ff52be6a0b77cd5946cb4219e36da5 Mon Sep 17 00:00:00 2001 From: brian Date: Tue, 6 Jul 2004 15:14:59 +0000 Subject: [PATCH] better enumeration, no need for SWAP(n>1) darcs-hash:20040706151459-24bed-45f05e24a44ed2c8c44cbf29344634f97ec6696d.gz --- src/org/ibex/js/Directory.java | 10 ++--- src/org/ibex/js/Interpreter.java | 76 ++++++++++++++++++-------------------- src/org/ibex/js/JS.java | 43 +++++++++++++++++---- src/org/ibex/js/JSArray.java | 15 ++++---- src/org/ibex/js/JSExn.java | 31 +++++----------- src/org/ibex/js/Parser.java | 74 ++++++++++++------------------------- 6 files changed, 116 insertions(+), 133 deletions(-) diff --git a/src/org/ibex/js/Directory.java b/src/org/ibex/js/Directory.java index bad28fa..98469d5 100644 --- a/src/org/ibex/js/Directory.java +++ b/src/org/ibex/js/Directory.java @@ -75,9 +75,9 @@ public class Directory extends JS { out.close(); } else { Directory d2 = new Directory(f2); - Enumeration e = ((JS)val).keys(); + JS.Enumeration e = val.keys(); while(e.hasMoreElements()) { - JS k = (JS)e.nextElement(); + JS k = e.nextElement(); JS v = val.get(k); d2.put(k, v); } @@ -109,10 +109,10 @@ public class Directory extends JS { public Enumeration keys() { final String[] elements = f.list(); - return new Enumeration() { + return new Enumeration(null) { int i = 0; - public boolean hasMoreElements() { return i < elements.length; } - public Object nextElement() { return FileNameEncoder.decode(elements[i++]); } + public boolean _hasMoreElements() { return i < elements.length; } + public JS _nextElement() { return JS.S(FileNameEncoder.decode(elements[i++])); } }; } } diff --git a/src/org/ibex/js/Interpreter.java b/src/org/ibex/js/Interpreter.java index c17fcc3..6c8736b 100644 --- a/src/org/ibex/js/Interpreter.java +++ b/src/org/ibex/js/Interpreter.java @@ -86,14 +86,7 @@ class Interpreter implements ByteCodes, Tokens { 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((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; - } + case SWAP: stack.swap(); break; case DUP: stack.push(stack.peek()); break; case NEWSCOPE: scope = new JSScope(scope); break; case OLDSCOPE: scope = scope.getParentScope(); break; @@ -120,11 +113,7 @@ class Interpreter implements ByteCodes, Tokens { case PUSHKEYS: { JS o = stack.peek(); - Enumeration e = o.keys(); - JSArray a = new JSArray(); - // FEATURE: Take advantage of the Enumeration, don't create a JSArray - while(e.hasMoreElements()) a.addElement((JS)e.nextElement()); - stack.push(a); + stack.push(o == null ? null : o.keys()); break; } @@ -135,7 +124,7 @@ class Interpreter implements ByteCodes, Tokens { case BREAK: case CONTINUE: - while(stack.size() > 0) { + while(!stack.empty()) { JS o = stack.pop(); if (o instanceof CallMarker) je("break or continue not within a loop"); if (o instanceof TryMarker) { @@ -169,7 +158,7 @@ class Interpreter implements ByteCodes, Tokens { case RETURN: { JS retval = stack.pop(); - while(stack.size() > 0) { + while(!stack.empty()) { Object o = stack.pop(); if (o instanceof TryMarker) { if(((TryMarker)o).finallyLoc < 0) continue; @@ -220,10 +209,7 @@ class Interpreter implements ByteCodes, Tokens { Trap t = null; TrapMarker tm = null; 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; - if(i==0) throw new Error("didn't find a call marker while doing cascade"); + CallMarker o = stack.findCall(); if(o instanceof TrapMarker) { tm = (TrapMarker) o; target = tm.trapee; @@ -274,10 +260,7 @@ class Interpreter implements ByteCodes, Tokens { Trap t = null; TrapMarker tm = 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"); + CallMarker o = stack.findCall(); if(o instanceof TrapMarker) { tm = (TrapMarker) o; target = tm.trapee; @@ -355,7 +338,7 @@ class Interpreter implements ByteCodes, Tokens { } case THROW: - throw new JSExn(stack.pop(), stack, f, pc, scope); + throw new JSExn(stack.pop(), this); /* FIXME GRAMMAR case MAKE_GRAMMAR: { @@ -501,7 +484,7 @@ class Interpreter implements ByteCodes, Tokens { if a handler is not found the exception is thrown */ void catchException(JSExn e) throws JSExn { - while(stack.size() > 0) { + while(!stack.empty()) { JS o = stack.pop(); if (o instanceof CatchMarker || o instanceof TryMarker) { boolean inCatch = o instanceof CatchMarker; @@ -664,27 +647,38 @@ class Interpreter implements ByteCodes, Tokens { 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]; + + boolean empty() { return sp == 0; } + void push(JS o) throws JSExn { if(sp == stack.length) grow(); stack[sp++] = o; } + JS peek() { if(sp == 0) throw new RuntimeException("Stack underflow"); return stack[sp-1]; } + final JS pop() { if(sp == 0) throw new RuntimeException("Stack underflow"); return stack[--sp]; } + void swap() throws JSExn { + if(sp < 2) throw new JSExn("stack overflow"); + JS tmp = stack[sp-2]; + stack[sp-2] = stack[sp-1]; + stack[sp-1] = tmp; } - public final JS pop() { - if(sp == 0) throw new RuntimeException("Stack underflow"); - return stack[--sp]; + CallMarker findCall() { + for(int i=sp-1;i>=0;i--) if(stack[i] instanceof CallMarker) return (CallMarker) stack[i]; + return null; } - private void grow() throws JSExn { + 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); - } - public int size() { return sp; } - public JS elementAt(int i) { return stack[i]; } + } - // FIXME: Eliminate all uses of SWAP n>1 so we don't need this - public void setElementAt(JS o, int i) { stack[i] = o; } + void backtrace(JSExn e) { + for(int i=sp-1;i>=0;i--) { + if (stack[i] instanceof CallMarker) { + CallMarker cm = (CallMarker)stack[i]; + if(cm.f == null) break; + String s = cm.f.sourceName + ":" + cm.f.line[cm.pc-1]; + if(cm instanceof Interpreter.TrapMarker) + s += " (trap on " + JS.debugToString(((Interpreter.TrapMarker)cm).key) + ")"; + e.addBacktrace(s); + } + } + } } } diff --git a/src/org/ibex/js/JS.java b/src/org/ibex/js/JS.java index 5d1b58d..96ab988 100644 --- a/src/org/ibex/js/JS.java +++ b/src/org/ibex/js/JS.java @@ -14,8 +14,7 @@ public abstract class JS { public final JS unclone() { return _unclone(); } public final JS jsclone() throws JSExn { return new Clone(this); } - // FEATURE: JSEnumeration - public Enumeration keys() throws JSExn { throw new JSExn("you can't enumerate the keys of this object (class=" + getClass().getName() +")"); } + public JS.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() +")"); } @@ -48,7 +47,7 @@ public abstract class JS { public static class O extends JS { private Hash entries; - public Enumeration keys() throws JSExn { return entries == null ? emptyEnumeration : entries.keys(); } + public Enumeration keys() throws JSExn { return entries == null ? (Enumeration)EMPTY_ENUMERATION : (Enumeration)new JavaEnumeration(null,entries.keys()); } 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); } @@ -107,6 +106,39 @@ public abstract class JS { public InputStream getInputStream() throws IOException { return clonee.getInputStream(); } } + public static abstract class Enumeration extends JS { + final Enumeration parent; + boolean done; + public Enumeration(Enumeration parent) { this.parent = parent; } + protected abstract boolean _hasMoreElements(); + protected abstract JS _nextElement() throws JSExn; + + public final boolean hasMoreElements() { + if(!done && !_hasMoreElements()) done = true; + return !done ? true : parent != null ? parent.hasMoreElements() : false; + } + public final JS nextElement() throws JSExn { return !done ? _nextElement() : parent != null ? parent.nextElement() : null; } + + public JS get(JS key) throws JSExn { + //#switch(JS.toString(key)) + case "hasMoreElements": return B(hasMoreElements()); + case "nextElement": return nextElement(); + //#end + return super.get(key); + } + } + public static class EmptyEnumeration extends Enumeration { + public EmptyEnumeration(Enumeration parent) { super(parent); } + protected boolean _hasMoreElements() { return false; } + protected JS _nextElement() { return null; } + } + public static class JavaEnumeration extends Enumeration { + private final java.util.Enumeration e; + public JavaEnumeration(Enumeration parent, java.util.Enumeration e) { super(parent); this.e = e; } + protected boolean _hasMoreElements() { return e.hasMoreElements(); } + protected JS _nextElement() { return (JS) e.nextElement(); } + } + // Static Interpreter Control Methods /////////////////////////////////////////////////////////////// /** log a message with the current JavaScript sourceName/line */ @@ -251,10 +283,7 @@ public abstract class JS { return ret; } - private static Enumeration emptyEnumeration = new Enumeration() { - public boolean hasMoreElements() { return false; } - public Object nextElement() { throw new NoSuchElementException(); } - }; + private static Enumeration EMPTY_ENUMERATION = new EmptyEnumeration(null); public static JS fromReader(String sourceName, int firstLine, Reader sourceCode) throws IOException { return JSFunction._fromReader(sourceName, firstLine, sourceCode); diff --git a/src/org/ibex/js/JSArray.java b/src/org/ibex/js/JSArray.java index 305a5de..2171e4d 100644 --- a/src/org/ibex/js/JSArray.java +++ b/src/org/ibex/js/JSArray.java @@ -95,6 +95,7 @@ public class JSArray extends JS.BT { if(i > oldSize) setSize(i); insertElementAt(val,i); } + return; } if(isString(key)) { if (JS.toString(key).equals("length")) { @@ -105,14 +106,12 @@ public class JSArray extends JS.BT { super.put(key,val); } - // FIXME: Needs to include super's keys - public Enumeration keys() { - return new Enumeration() { - private int n = size(); - public boolean hasMoreElements() { return n > 0; } - public Object nextElement() { - if(n == 0) throw new NoSuchElementException(); - return new Integer(--n); + public Enumeration keys() throws JSExn { + return new Enumeration(super.keys()) { + private int n = 0; + public boolean _hasMoreElements() { return n < size(); } + public JS _nextElement() { + return n >= size() ? null : JS.N(n++); } }; } diff --git a/src/org/ibex/js/JSExn.java b/src/org/ibex/js/JSExn.java index 1301199..4f5fb31 100644 --- a/src/org/ibex/js/JSExn.java +++ b/src/org/ibex/js/JSExn.java @@ -7,27 +7,16 @@ import java.io.*; /** An exception which can be thrown and caught by JavaScript code */ public class JSExn extends Exception { private Vec backtrace = new Vec(); - private JS js = null; + private JS js; public JSExn(String s) { this(JS.S(s)); } - public JSExn(JS js) { + public JSExn(JS js) { this(js,Interpreter.current()); } + public JSExn(JS js, Interpreter cx) { this.js = js; - if (Interpreter.current() != null) - fill(Interpreter.current().stack, Interpreter.current().f, Interpreter.current().pc, Interpreter.current().scope); + if(cx != null) fill(cx); } - public JSExn(JS js, Interpreter.Stack stack, JSFunction f, int pc, JSScope scope) { this.js = js; fill(stack, f, pc, scope); } - private void fill(Interpreter.Stack stack, JSFunction f, int pc, JSScope scope) { - addBacktrace(f.sourceName + ":" + f.line[pc]); - for(int i=stack.size()-1; i>=0; i--) { - JS element = stack.elementAt(i); - if (element instanceof Interpreter.CallMarker) { - Interpreter.CallMarker cm = (Interpreter.CallMarker)element; - if(cm.f == null) break; - String s = cm.f.sourceName + ":" + cm.f.line[cm.pc-1]; - if(cm instanceof Interpreter.TrapMarker) - s += " (trap on " + JS.debugToString(((Interpreter.TrapMarker)cm).key) + ")"; - addBacktrace(s); - } - } + private void fill(Interpreter cx) { + addBacktrace(cx.f.sourceName + ":" + cx.f.line[cx.pc]); + cx.stack.backtrace(this); } public void printStackTrace() { printStackTrace(System.err); } public void printStackTrace(PrintWriter pw) { @@ -41,9 +30,9 @@ public class JSExn extends Exception { public String toString() { return "JSExn: " + JS.debugToString(js); } public String getMessage() { return toString(); } public JS getObject() { return js; } - public void addBacktrace(String line) { backtrace.addElement(line); } - - + + void addBacktrace(String line) { backtrace.addElement(line); } + public static class IO extends JSExn { public IO(java.io.IOException ioe) { super("ibex.io: " + ioe.toString()); diff --git a/src/org/ibex/js/Parser.java b/src/org/ibex/js/Parser.java index bcc5033..dce9a24 100644 --- a/src/org/ibex/js/Parser.java +++ b/src/org/ibex/js/Parser.java @@ -432,7 +432,7 @@ class Parser extends Lexer implements ByteCodes { // first. If the object supports method calls, it will // return JS.METHOD b.add(parserLine, GET_PRESERVE); - int n = parseArgs(b,0); + int n = parseArgs(b); b.add(parserLine, CALLMETHOD, JS.N(n)); break; } @@ -475,7 +475,7 @@ class Parser extends Lexer implements ByteCodes { switch (tok) { case LP: { // invocation (not grouping) - int n = parseArgs(b,0); + int n = parseArgs(b); b.add(parserLine, CALL, JS.N(n)); break; } @@ -553,14 +553,12 @@ class Parser extends Lexer implements ByteCodes { } // parse a set of comma separated function arguments, assume LP has already been consumed - // if swap is true, (because the function is already on the stack) we will SWAP after each argument to keep it on top - private int parseArgs(JSFunction b, int pushdown) throws IOException { + private int parseArgs(JSFunction b) throws IOException { int i = 0; while(peekToken() != RP) { i++; if (peekToken() != COMMA) { startExpr(b, NO_COMMA); - if(pushdown != 0) b.add(parserLine, SWAP, JS.N(pushdown)); if (peekToken() == RP) break; } consume(COMMA); @@ -849,55 +847,32 @@ class Parser extends Lexer implements ByteCodes { consume(RP); b.add(parserLine, PUSHKEYS); - b.add(parserLine, DUP); - b.add(parserLine, LITERAL, JSString.intern("length")); - b.add(parserLine, GET); - // Stack is now: n, keys, obj, ... int size = b.size; b.add(parserLine, LOOP); b.add(parserLine, POP); - // Stack is now: LoopMarker, n, keys, obj, ... - // NOTE: This will break if the interpreter ever becomes more strict - // and prevents bytecode from messing with the Markers - b.add(parserLine, SWAP, JS.N(3)); - // Stack is now: Tn, keys, obj, LoopMarker, ... - - b.add(parserLine, LITERAL, JS.N(1)); - b.add(parserLine, SUB); - b.add(parserLine, DUP); - // Stack is now: index, keys, obj, LoopMarker - b.add(parserLine, LITERAL, JS.ZERO); - b.add(parserLine, LT); - // Stack is now index<0, index, keys, obj, LoopMarker, ... - - b.add(parserLine, JF, JS.N(5)); // if we're >= 0 jump 5 down (to NEWSCOPE) - // Move the LoopMarker back into place - this is sort of ugly - b.add(parserLine, SWAP, JS.N(3)); - b.add(parserLine, SWAP, JS.N(3)); - b.add(parserLine, SWAP, JS.N(3)); - // Stack is now: LoopMarker, -1, keys, obj, ... - b.add(parserLine, BREAK); + b.add(parserLine,SWAP); // get the keys enumeration object on top + b.add(parserLine,DUP); + b.add(parserLine,GET,JS.S("hasMoreElements")); + int size2 = b.size; + b.add(parserLine,JT); + b.add(parserLine,SWAP); + b.add(parserLine,BREAK); + b.set(size2, JS.N(b.size - size2)); + b.add(parserLine,DUP); + b.add(parserLine,GET,JS.S("nextElement")); + b.add(parserLine, NEWSCOPE); - if(hadVar) { - b.add(parserLine, DECLARE, JSString.intern(varName)); - b.add(parserLine, POP); - } - // Stack is now: index, keys, obj, LoopMarker, ... - 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, 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 - b.add(parserLine, POP); // index, keys, obj, LoopMarker, ... - // Move the LoopMarker back into place - this is sort of ugly - b.add(parserLine, SWAP, JS.N(3)); - b.add(parserLine, SWAP, JS.N(3)); - b.add(parserLine, SWAP, JS.N(3)); + b.add(parserLine,TOPSCOPE); + b.add(parserLine,SWAP); + b.add(parserLine, hadVar ? DECLARE : LITERAL, JSString.intern(varName)); + b.add(parserLine,SWAP); + b.add(parserLine,PUT); + b.add(parserLine,POP); + b.add(parserLine,POP); + b.add(parserLine,SWAP); parseStatement(b, null); @@ -906,10 +881,7 @@ class Parser extends Lexer implements ByteCodes { // jump here on break b.set(size, JS.N(b.size - size)); - b.add(parserLine, POP); // N - b.add(parserLine, POP); // KEYS - b.add(parserLine, POP); // OBJ - + b.add(parserLine, POP); } else { if (hadVar) pushBackToken(VAR, null); // yeah, this actually matters b.add(parserLine, NEWSCOPE); // grab a fresh scope -- 1.7.10.4