better enumeration, no need for SWAP(n>1)
authorbrian <brian@brianweb.net>
Tue, 6 Jul 2004 15:14:59 +0000 (15:14 +0000)
committerbrian <brian@brianweb.net>
Tue, 6 Jul 2004 15:14:59 +0000 (15:14 +0000)
darcs-hash:20040706151459-24bed-45f05e24a44ed2c8c44cbf29344634f97ec6696d.gz

src/org/ibex/js/Directory.java
src/org/ibex/js/Interpreter.java
src/org/ibex/js/JS.java
src/org/ibex/js/JSArray.java
src/org/ibex/js/JSExn.java
src/org/ibex/js/Parser.java

index bad28fa..98469d5 100644 (file)
@@ -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++])); }
             };
     }
 }
index c17fcc3..6c8736b 100644 (file)
@@ -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);
+                }
+            }
+        }
     }
 }
index 5d1b58d..96ab988 100644 (file)
@@ -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);
index 305a5de..2171e4d 100644 (file)
@@ -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++);
                 }
             };
     }
index 1301199..4f5fb31 100644 (file)
@@ -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());
index bcc5033..dce9a24 100644 (file)
@@ -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