Revision: 4763
          http://sourceforge.net/p/vexi/code/4763
Author:   mkpg2
Date:     2015-02-16 01:01:31 +0000 (Mon, 16 Feb 2015)
Log Message:
-----------
Minor refactor.
- removed duplicated code for handling nested interpreters
- split out jpp code from Intepreter.java (incovenient for development).

Modified Paths:
--------------
    branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Scheduler.java
    branches/vexi3/org.vexi-library.js/src/main/jpp/org/ibex/js/Scope.jpp

Added Paths:
-----------
    
branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Interpreter.java
    branches/vexi3/org.vexi-library.js/src/main/jpp/org/ibex/js/JSArgs.jpp
    branches/vexi3/org.vexi-library.js/src/main/jpp/org/ibex/js/JSArgsTrap.jpp
    branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/JSUX.java

Removed Paths:
-------------
    branches/vexi3/org.vexi-library.js/src/main/jpp/org/ibex/js/Interpreter.jpp

Copied: 
branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Interpreter.java 
(from rev 4762, 
branches/vexi3/org.vexi-library.js/src/main/jpp/org/ibex/js/Interpreter.jpp)
===================================================================
--- 
branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Interpreter.java   
                            (rev 0)
+++ 
branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Interpreter.java   
    2015-02-16 01:01:31 UTC (rev 4763)
@@ -0,0 +1,1036 @@
+// Copyright 2000-2008 the Contributors, as shown in the revision logs.
+// Licensed under the Apache Software License 2.0 ("the License").
+// You may not use this file except in compliance with the License.
+
+
+package org.ibex.js;
+
+import org.ibex.js.parse.*;
+
+/*@PAGE(concept=Special Variables)
+ * 
+ * <h3>General Variables</h3>
+ * <p>The following special variables are accessible in general template 
script.</p>
+ * <dl>
+ *   <dt><code>thisbox</code></dt>
+ *   <dd><p>A reference to the Box defined by the XML node containing it.  It 
can also be thought
+ *   of as a Box property.</p></dd>
+ *   <dt><code>thisobj</code></dt>
+ *   <dd><p>A reference to the Object/Array/etc defined by the XML node 
containing it.</p></dd>
+ *   <dt><code>static</code></dt>
+ *   <dd><p>The static context of the template in which it is called.</p></dd>
+ *   <dt><code>vexi</code></dt>
+ *   <dd><p>The immutable global vexi object.</p></dd>
+ * </dl>
+ * 
+ * <h3>Function Variables</h3>
+ * <p>The following special variables are accessible in the body of a 
function.</p>
+ * <dl>
+ *   <dt><code>arguments</code></dt>
+ *   <dd><p>An immutable array of the arguments passed into a function. Its 
length is detirmined
+ *   when the function is called and not by the method signature. </p></dd>
+ *   <dt><code>callee</code></dt>
+ *   <dd><p>The current function being executed</p></dd>
+ *   <dt><code>this</code></dt>
+ *   <dd>
+ *     <p><code>this</code><i> is a keyword</i></p>
+ *     <p>The context of the execution. When a function is being accessed as 
the property of an
+ *     object the context is the object. For example in <code>a.f()</code> in 
the body of 
+ *     <code>f</code> this refers to <code>a</code>.</p>
+ *     
+ *     <p>If a function is executed as a var then <code>this==null</code>.</p>
+ *     
+ *     <p>In top level code the value of <code>this</code> may be assigned to 
a relevant
+ *     context.</p>
+ *   </dd>
+ * </dl>
+ * 
+ * <h3>Trap Variables</h3>
+ * <p>The following special variables are accessible in the body of a trap.</p>
+ * <dl>
+ *   <dt><code>cascade</code></dt>
+ *   <dd>
+ *     <p><code>cascade</code><i> is a keyword</i></p>
+ *     <p>In a read trap, read from cascade to get the value from traps lower 
in the read trap
+ *     chain or, if there are no further traps, the object property the trap 
is placed upon.
+ *     (Reading directly from the property will invoke the read trap chain 
again, possibly causing
+ *     an infinite loop.)</p>   
+ *     <p>In a write trap, putting to cascade will caused lower traps in the 
write trap chain to be
+ *     executed or, if there are no further traps, the value passed to cascade 
is put to the object
+ *     property the trap is placed upon. (Writing directly to the property 
will invoke the write
+ *     trap chain again, possibly causing an infinite loop.)</p>       
+ *   </dd>
+ *   <dt><code>trapname</code></dt>
+ *   <dd><p>The name of the property upon which a trap is been placed.</p></dd>
+ *   <dt><code>trapee</code></dt>
+ *   <dd><p>The object upon which a trap is been placed. <i>Synonym for 
this</i></p></dd>
+ *   <dt><code>callee</code></dt>
+ *   <dd><p>The trap function - useful for anonymous removal of traps.</p></dd>
+ * </dl>
+ * */
+/** Encapsulates a single JS interpreter (ie call stack) */
+public class Interpreter implements ByteCodes, Tokens, Constants {
+
+       
+       // FIXME - use Thread.cascadedTo==NULL instead
+       public final static JSNumber CASCADE_PREVENTED = new JSNumber.I(1);
+    
+    // Instance members and methods 
//////////////////////////////////////////////////////////////////////
+    
+       // FIXME - should not be public but devl core uses them
+    int pausecount;               ///< the number of times pause() has been 
invoked; -1 indicates unpauseable
+    public Function f = null;          ///< the currently-executing JSFunction
+    public Scope scope;                ///< the current top-level scope (LIFO 
stack via NEWSCOPE/OLDSCOPE)
+    final public Stack stack = new Stack(); ///< the object stack
+    public int pc = 0;                   ///< the program counter
+
+    final Thread thread;
+    final Interpreter old;
+   
+    public Interpreter(Thread thread){
+       this.thread = thread;
+       this.old = thread.currentInterpreter;
+       thread.currentInterpreter = this;
+    }
+    Interpreter(Thread thread, JSFunction f, boolean pauseable, JS[] args) {
+        this(thread);
+        this.f = f.f;
+        this.pausecount = pauseable ? 0 : -1;
+        this.scope = f.parentScope;
+        try {
+            stack.push(new CallMarker(null));    // the "root function 
returned" marker -- f==null
+            stack.push(new JSArgs(args, f, null)); // FIXME: temprorary bug fix
+        } catch(JSExn e) {
+            throw new Error("should never happen");
+        }
+    }
+    
+    Interpreter(Thread thread, JS.Trap t, JS val, boolean pauseOnCascade, JS 
trapname) {
+        this(thread);
+        this.pausecount = -1;
+        try {
+            setupTrap(t,val,new 
TrapMarker(null,t,val,pauseOnCascade),trapname);
+        } catch(JSExn e) {
+            throw new Error("should never happen");
+        }
+    }
+
+    private boolean get = false;
+    // FIXME: split this stuff out into a Script instance control object
+    //        so it's possible to make JS either single or multi threaded.
+    /** this is the only synchronization point we need in order to be 
threadsafe */
+    public synchronized Object run(Object o) throws JSExn {
+        if (f == null) throw new RuntimeException("function already finished");
+        if (scope == null) throw new RuntimeException("scope is null");
+        
+        if( o instanceof JSExn){
+               // Fill in empty exceptions. Exceptions coming created in 
background
+               // calls don't have their stack traces filled in.
+               ((JSExn)o).fillIfEmpty(this);
+               // Throw excpetion - move interpreter to catch block 
+               catchException((JSExn)o);
+        }else if (get)
+               stack.push(o);
+
+        return run();
+    }
+    
+    public void pause(String forwhat) throws JSExn {
+        if (pausecount == -1 || f == null) throw new JSExn("Cannot "+forwhat+" 
in foreground thread");
+        pausecount++;
+        switch(f.op[pc]) {
+            case Tokens.RETURN: case ByteCodes.PUT: get = false; break;
+            case ByteCodes.GET: case ByteCodes.GET_PRESERVE: case 
ByteCodes.CALLMETHOD: case ByteCodes.CALL: get = true; break;
+            default: throw new Error("paused on unexpected bytecode: " + 
f.op[pc]);
+        }
+    }
+
+    int getLine() {
+        return f == null || pc < 0 || pc >= f.size ? -1 : f.line[pc];
+    }
+
+    String getSourceName() {
+        return f == null ? null : f.sourceName;
+    } 
+    String getWhere(){ return getSourceName() + ":" + getLine(); }
+
+    private JSExn je(String s) { return new JSExn(getWhere() + " " + s); }
+
+    
+    // REMARK - encapsulate loop jump operations here, so that we can test
+    // interuption, but only when there is a chance we're in an infinite loop.
+    private void jump(int delta_pc) throws InterruptedException{
+        jumpAbs(pc+delta_pc);
+    }
+    private void jumpAbs(int pc) throws InterruptedException{
+       this.pc = pc;
+       if(java.lang.Thread.interrupted()) throw new InterruptedException();
+    }
+    
+    private JS run() throws JSExn {
+        // if pausecount changes after a get/put/call, we know we've been 
paused
+        if(pausecount>0)
+               pausecount --;
+
+        OUTER: for(;; pc++) {
+        try {
+               if(Instr.interpreter!=null) Instr.interpreter.handle(this); 
+
+            int op = f.op[pc];
+            Object arg = f.arg[pc];
+            if(op == FINALLY_DONE) {
+                FinallyData fd = (FinallyData) stack.pop();
+                if(fd == null) continue OUTER; // NOP
+                if(fd.exn != null) throw fd.exn;
+                op = fd.op;
+                arg = fd.arg;
+            }
+            
+            // DEBUG
+            // DevUtil.debugStack(this, op, arg);
+               
+            switch(op) {
+            case LITERAL: stack.push(arg); break;
+            // GUT ought to fetch constructors
+            case OBJECT: stack.push(new JS.Obj()); break;
+            case ARRAY: stack.push(new JSArray(JSU.toInt((JS)arg))); break;
+            //case DECLARE: scope.declare((JS)(arg==null ? stack.peek() : 
arg)); if(arg != null) stack.push((JS)arg); break;
+            case JT: if (JSU.isTruthy((JS)stack.pop())) 
jump(JSU.toInt((JS)arg) - 1); break;
+            case JF: if (!JSU.isTruthy((JS)stack.pop())) 
jump(JSU.toInt((JS)arg) - 1); break;
+            case JMP: jump(JSU.toInt((JS)arg) - 1); break;
+            case POP: stack.pop(); break;
+            case SWAP: stack.swap(); break;
+            case DUP: 
+               if(arg==null) stack.push(stack.peek()); 
+               else stack.push(stack.peek(JSU.toInt((JS)arg)));
+               break;
+            case NEWSCOPE: {
+                int n = JSU.toInt((JS)arg);
+                scope = new Scope(scope,(n>>>16)&0xffff,(n>>>0)&0xffff);
+                break;
+            }
+            case OLDSCOPE: scope = scope.parent; break;
+            case GLOBALSCOPE: stack.push(scope.getGlobal()); break;
+            case SCOPEGET: stack.push(scope.get((JS)arg)); break;
+            case SCOPEPUT: {
+                // FIXME: HACK: share this around more and find the callee.
+                Object val = stack.peek();
+                if (val != null && val instanceof JS[]) val = new 
JSArgs((JS[])val, null, null);
+                scope.put((JS)arg, (JS)val); break;
+            }
+            case ASSERT: if (!JSU.isTruthy((JS)stack.pop())) throw 
je("ibex.assertion.failed"); break;
+            case BITNOT: stack.push(JSU.N(~JSU.toLong((JS)stack.pop()))); 
break;
+            case BANG: stack.push(JSU.B(!JSU.isTruthy((JS)stack.pop()))); 
break;
+            case NEWFUNCTION: 
stack.push(JSU.cloneWithNewParentScope((Function)arg, scope)); break;
+            case LABEL: break;
+
+            case TYPEOF: {
+                Object o = stack.pop();
+                if (o == null) stack.push(SC_null);
+                else stack.push(((JS)o).type());
+                break;
+            }
+
+            case INSTANCEOF: {
+               JS c = (JS)stack.pop();
+               JS o = (JS)stack.pop();
+               if(c==null) throw new JSExn("Cannot instanceof null");
+               stack.push(JSU.B(o==null?false:o.instanceOf(c)));
+               break;
+            }
+            
+            case PUSHKEYS: {
+                JS o = (JS)stack.peek();
+                stack.push(o == null ? null : o.keys());
+                break;
+            }
+
+            case LOOP:
+            case SWITCH:
+                //stack.push(new LoopMarker(pc, (String)(pc > 0 && f.op[pc - 
1] == LABEL ? f.arg[pc - 1] : null), scope));
+                //stack.push(JSU.T);
+                stack.push(new LoopMarker(pc, pc > 0 && f.op[pc - 1] == LABEL 
? (String)f.arg[pc - 1] : (String)null, scope, op == LOOP));
+                if(op == LOOP) stack.push(JSU.T);
+                break;
+
+            case BREAK:
+            case CONTINUE:
+                while(!stack.empty()) {
+                    Object o = stack.pop();
+                    if (o instanceof CallMarker) throw je("Tried to 
'"+(op==BREAK?"break":"continue")+"' when not within a 
loop"+(op==BREAK?"/switch":""));
+                    if (o instanceof TryMarker) {
+                        if(((TryMarker)o).finallyLoc < 0) continue; // no 
finally block, keep going
+                        stack.push(new FinallyData(op, arg));
+                        scope = ((TryMarker)o).scope;
+                        pc = ((TryMarker)o).finallyLoc - 1;
+                        continue OUTER;
+                    }
+                    if (o instanceof LoopMarker) {
+                        //if (arg == null || 
arg.equals(((LoopMarker)o).label)) {
+                            //int loopInstructionLocation = 
((LoopMarker)o).location;
+                        LoopMarker lm = (LoopMarker) o;
+                        if(op == CONTINUE && !lm.canContinue) continue;
+                        if (arg == null || arg.equals(lm.label)) {
+                            int loopInstructionLocation = lm.location;
+                            int endOfLoop = 
JSU.toInt((JS)f.arg[loopInstructionLocation]) + loopInstructionLocation;
+                            scope = lm.scope;
+                            if (op == CONTINUE) { stack.push(o); 
stack.push(JSU.F); }
+                            if(op == BREAK) pc = endOfLoop - 1;
+                            else jumpAbs(loopInstructionLocation);
+                            continue OUTER;
+                        }
+                    }
+                }
+                throw new Error((op==BREAK?"BREAK":"CONTINUE")+" invoked but 
couldn't find LoopMarker at " +
+                                getSourceName() + ":" + getLine());
+
+            case TRY: {
+                int[] jmps = (int[]) arg;
+                // jmps[0] is how far away the catch block is, jmps[1] is how 
far away the finally block is
+                // each can be < 0 if the specified block does not exist
+                stack.push(new TryMarker(jmps[0] < 0 ? -1 : pc + jmps[0], 
jmps[1] < 0 ? -1 : pc + jmps[1], this));
+                break;
+            }
+
+            case RETURN: {
+                JS retval = (JS)stack.pop();
+                while(!stack.empty()) {
+                    Object o = stack.pop();
+                    if (o instanceof TryMarker) {
+                        if(((TryMarker)o).finallyLoc < 0) continue;
+                        stack.push(retval); 
+                        stack.push(new FinallyData(RETURN));
+                        scope = ((TryMarker)o).scope;
+                        pc = ((TryMarker)o).finallyLoc - 1;
+                        continue OUTER;
+                    } else if (o instanceof CallMarker) {
+                       boolean isWriteTrap = false;
+                        if (o instanceof TrapMarker) { // handles return 
component of a write trap
+                            TrapMarker tm = (TrapMarker) o;
+                            isWriteTrap = tm.t.isWriteTrap();
+                            if (isWriteTrap) {
+                                // REMOVED automatic cascading(FOOTNOTE:1)
+                                       thread.faction.cascadedTo = null;
+                                       retval = CASCADE_PREVENTED;
+                            }
+                        }
+                        CallMarker cm = (CallMarker) o;
+                        scope = cm.scope;
+                        pc = cm.pc - 1;
+                        f = cm.f;
+                        if(!isWriteTrap)
+                               stack.push(retval);
+                        if (f == null) return retval;
+                        continue OUTER;
+                    }
+                }
+                throw new Error("error: RETURN invoked but couldn't find a 
CallMarker!");
+            }
+                
+            case CASCADE: {
+                boolean write = JSU.T==arg;
+                JS val = write ? (JS)stack.pop() : null;
+                CallMarker o = stack.findCall();
+                if(!(o instanceof TrapMarker)) throw new JSExn("Tried to 
'cascade' while not in a trap");
+                TrapMarker tm = (TrapMarker) o;
+                JS key = tm.t.key();
+                JS target = tm.t.target();
+                if(tm.t.isWriteTrap() != write)
+                    throw new JSExn("Tried to do a "+(write?"write":"read") + 
" 'cascade' in a " + (write?"read":"write") + " trap");
+                JS.Trap t = write ? tm.t.nextWrite() : tm.t.nextRead();
+                while (t == null && target instanceof JS.Clone) {
+                    target = ((JS.Clone)target).clonee;
+                    t = target.getTrap(key);
+                    if (t != null) t = write ? t.findWrite() : t.findRead();
+                }
+                if(write) {
+                    stack.push(val);
+                }
+                if(t != null) {
+                    setupTrap(t,val,new 
TrapMarker(this,t,val,tm.pauseOnCascade), tm.trapargs.trapname);
+                    pc--; // we increment later
+                } else {
+                       thread.faction.cascadedTo = tm.trapargs.trapname;
+                    if(write) {
+                        if (tm.pauseOnCascade) { pc++; return val; }
+                        target.put(key,val);
+                    } else {
+                       if (tm.pauseOnCascade) {
+                               // FIXME should move this to setup of 
interpreter
+                               // we need it true for when we restart the 
interpreter
+                               get = true; 
+                               pc++; return null; }
+                       else{
+                            JS ret = target.get(key);
+                            if (ret != null && ret == JS.METHOD) ret = new 
Stub(target, key);
+                            stack.push(ret);
+                       }
+                    }
+                    if (pausecount > 0) { pc++; return null; }   // we were 
paused                    
+                }
+                break;
+            }
+                
+            case PUT: {
+                JS val = (JS)stack.pop();
+                JS key = (JS)stack.pop();
+                JS target = (JS)stack.peek();
+                if (target == null) throw je("Tried to put " + 
JSU.toString(val) + " to the " + JSU.toString(key) + " property on the null 
value");
+                if (key == null) throw je("Tried to assign \"" + 
JSU.toString(val) + "\" to the null key");
+                
+                JS.Trap t = target.getTrap(key);
+                if(t != null) t = t.findWrite();
+                
+                stack.push(val);
+                
+                if(t != null) {
+                    setupTrap(t,val,new TrapMarker(this,t,val,false),null);
+                    pc--; // we increment later
+                } else {
+                    target.put(key,val);
+                    if (pausecount > 0) { pc++; return null; }   // we were 
paused
+                }
+                break;
+            }
+
+            case GET:
+            case GET_PRESERVE: {
+                JS target, key;
+                if (op == GET) {
+                    key = arg == null ? (JS)stack.pop() : (JS)arg;
+                    target = (JS)stack.pop();
+                } else {
+                       if(arg==null){
+                        key = (JS)stack.pop();
+                        target = (JS)stack.peek();
+                        stack.push(key);
+                       }else{
+                               key = (JS)arg;
+                               target = (JS)stack.peek();
+                       }
+                }
+                JS ret = null;
+                if (key == null) throw je("Tried to get the null key from " + 
JSU.toString(target));
+                if (target == null) throw je("Tried to get property \"" + 
JSU.toString(key) + "\" from the null object");
+                
+                JS.Trap t = target.getTrap(key);
+                if (t != null) t = t.findRead();
+                 
+                if(t != null) {
+                       JS f = t.function();
+                       if(f instanceof JSFunction){
+                               setupTrap(t,null,new 
TrapMarker(this,t,null,false),null);
+                               pc--; // we increment later
+                               break;
+                       }
+                       // Trap was an override. Simply return the value. 
+                       ret = f;
+                }
+                if(ret==null) ret = target.get(key);
+                if (pausecount > 0) { pc++; return null; }   // we were paused
+
+                if (ret != null && ret == JS.METHOD) ret = new Stub(target, 
key);
+                stack.push(ret);
+                break;
+            }
+            
+            case NEW: {
+               JS[] jsargs = readArgs(arg);
+               JS constructor = (JS) stack.pop();
+                               if (constructor == null)
+                                       throw new JSExn("null is not a 
constructor");
+               stack.push(constructor.new_(jsargs));
+               break;
+            }
+            
+            case CALL: case CALLMETHOD: {
+                               JS[] jsargs = readArgs(arg);
+
+                               JS ret = null;
+                               JS callee = (JS) stack.pop();
+                               JS object = null;
+                               if (op == CALLMETHOD) {
+                                       if (callee == null) {
+                                               JS method = (JS) stack.pop();
+                                               object = (JS) stack.pop();
+                                               if (object instanceof 
JSPrimitive)
+                                                       throw new JSExn("Method 
'"
+                                                                       + 
JSU.toString(method)
+                                                                       + "' 
not defined for " + object.type());
+                                               throw new JSExn("Function '" + 
JSU.toString(method)
+                                                               + "' not found 
in " + JSU.toString(object));
+                                       } else {
+                                               stack.pop();
+                                               object = (JS) stack.pop();
+                                       }
+                               }
+                               if (callee == null)
+                                       throw new JSExn("Tried to call null 
object");
+
+                               switch (callee.callType()) {
+                               case JS.CALLTYPE_FUNCTION: {
+                                       stack.push(new CallMarker(this));
+                                       JSFunction jsfunc = (JSFunction) callee;
+                                       f = jsfunc.f;
+                                       stack.push(new JSArgs(jsargs, jsfunc, 
object));
+                                       scope = jsfunc.parentScope;
+                                       pc = -1;
+                                       break;
+                               }
+                               case JS.CALLTYPE_APPLY:
+                                       JS[] args = JSU.checkApply(jsargs);
+                                       JS this_ = 
jsargs.length>0?jsargs[0]:null; 
+                                       if(callee instanceof JSFunction.Apply){
+                                               stack.push(new 
CallMarker(this));
+                                               JSFunction jsfunc = 
(JSFunction) ((JSFunction.Apply) callee).f;
+                                               f = jsfunc.f;
+                                               stack.push(new JSArgs(args, 
jsfunc, this_));
+                                               scope = jsfunc.parentScope;
+                                               pc = -1;
+                                               break;
+                                       }
+                                       object = this_;
+                                       jsargs = args;
+                                       // fallthrough
+                               case JS.CALLTYPE_METHOD:
+                                       ret = callee.apply(object,jsargs);
+                                       if (pausecount > 0) {
+                                               pc++;
+                                               return null;
+                                       }
+                                       stack.push(ret);
+                                       break;
+                               }
+                               break;
+            }
+
+            case THROW:{
+               JS thrown = (JS)stack.pop();
+               // If already an exception then we recover the JSExn and 
rethrow it
+               if(thrown instanceof JSExn.Obj){
+                       JSExn je = ((JSExn.Obj)thrown).asJSExn();
+                       je.fillIfEmpty(this);
+                       throw je;
+               }else
+                       throw new JSExn(thrown, null, null, this);
+            }
+
+            case ADD_TRAP: case DEL_TRAP: {
+                // A trap addition/removal
+                JS val = (JS)stack.pop();
+                JS key = (JS)stack.pop();
+                JS js = (JS)stack.peek();
+                
+                // some checks to make sure the assignment is valid
+                // TODO: check val is a function
+                if (key == null) throw new JSExn("Tried to " + (op == ADD_TRAP 
? "add" : "remove")
+                        + " a trap function using a null key on a box or 
object");
+                if (val == null) throw new JSExn("Tried to " + (op == ADD_TRAP 
? "add" : "remove")
+                        + " a null value as a trap function to property '" + 
key.coerceToString() + "'");
+                if (js == null) throw new JSExn("Tried to " + (op == ADD_TRAP 
? "add" : "remove")
+                        + " a trap to property '" + key.coerceToString() + "' 
on a null object ");
+
+                if(op == ADD_TRAP) js.addTrap(key, val);
+                else js.delTrap(key, val);
+                break;
+            }
+
+            case ADD: {
+                int count = ((JSNumber)arg).toInt32();
+                if(count < 2) throw new Error("this should never happen");
+                if(count == 2) {
+                    // common case
+                    JS right = (JS)stack.pop();
+                    JS left = (JS)stack.pop();
+                    JS ret;
+                    if(left instanceof JSString || right instanceof JSString)
+                        ret = 
JSU.S(JSU.toString(left).concat(JSU.toString(right)));
+                    else {
+                        ret = add(left, right);
+                    }
+                    stack.push(ret);
+                } else {
+                    JS[] args = new JS[count];
+                    while(--count >= 0) args[count] = (JS)stack.pop();
+                    if(args[0] instanceof JSString) {
+                        StringBuffer sb = new StringBuffer(64);
+                        for(int i=0;i<args.length;i++) 
sb.append(JSU.toString(args[i]));
+                        stack.push(JSU.S(sb.toString()));
+                    } else {
+                        int numStrings = 0;
+                        for(int i=0;i<args.length;i++) if(args[i] instanceof 
JSString) numStrings++;
+                        if(numStrings == 0) {
+                            double d = 0.0;
+                            for(int i=0;i<args.length;i++) d += 
JSU.toDouble(args[i]);
+                            stack.push(JSU.N(d));
+                        } else {
+                            int i=0;
+                            StringBuffer sb = new StringBuffer(64);
+                            if(!(args[0] instanceof JSString || args[1] 
instanceof JSString)) {
+                                double d=0.0;
+                                do {
+                                    d += JSU.toDouble(args[i++]);
+                                } while(!(args[i] instanceof JSString));
+                                sb.append(JSU.toString(JSU.N(d)));
+                            }
+                            while(i < args.length) 
sb.append(JSU.toString(args[i++]));
+                            stack.push(JSU.S(sb.toString()));
+                        }
+                    }
+                }
+                break;
+            }
+
+            default: {
+                JS right = (JS)stack.pop();
+                JS left = (JS)stack.pop();
+                switch(op) {
+                        
+                case BITOR: stack.push(JSU.N(JSU.toLong(left) | 
JSU.toLong(right))); break;
+                case BITXOR: stack.push(JSU.N(JSU.toLong(left) ^ 
JSU.toLong(right))); break;
+                case BITAND: stack.push(JSU.N(JSU.toLong(left) & 
JSU.toLong(right))); break;
+
+                case SUB: stack.push(subtract(left, right)); break;
+                case MUL: stack.push(multiply(left, right)); break;
+                case DIV: stack.push(divide(left,right)); break; 
+                case MOD: stack.push(modulo(left, right)); break; 
+                        
+                case LSH: stack.push(JSU.N(JSU.toLong(left) << 
JSU.toLong(right))); break;
+                case RSH: stack.push(JSU.N(JSU.toLong(left) >> 
JSU.toLong(right))); break;
+                case URSH: stack.push(JSU.N(JSU.toLong(left) >>> 
JSU.toLong(right))); break;
+                        
+                case LT: {
+                    if(left instanceof JSString && right instanceof JSString)
+                        
stack.push(JSU.B(JSU.toString(left).compareTo(JSU.toString(right)) < 0));
+                    else
+                        stack.push(JSU.B(JSU.toDouble(left) < 
JSU.toDouble(right)));
+                    break;
+                }
+                case LE: {
+                    if(left instanceof JSString && right instanceof JSString)
+                        
stack.push(JSU.B(JSU.toString(left).compareTo(JSU.toString(right)) <= 0));
+                    else
+                        stack.push(JSU.B(JSU.toDouble(left) <= 
JSU.toDouble(right)));
+                    break;
+                }
+                case GT: {
+                    if(left instanceof JSString && right instanceof JSString)
+                        
stack.push(JSU.B(JSU.toString(left).compareTo(JSU.toString(right)) > 0));
+                    else
+                        stack.push(JSU.B(JSU.toDouble(left) > 
JSU.toDouble(right)));
+                    break;
+                }
+                case GE: {
+                    if(left instanceof JSString && right instanceof JSString)
+                        
stack.push(JSU.B(JSU.toString(left).compareTo(JSU.toString(right)) >= 0));
+                    else
+                        stack.push(JSU.B(JSU.toDouble(left) >= 
JSU.toDouble(right)));
+                    break;
+                }
+   
+                case SHEQ:
+                case SHNE:{
+                    boolean ret;
+                    if(left == null && right == null) ret = true;
+                    else if(left == null || right == null) ret = false;
+                    else ret = left.equals(right);
+                    stack.push(JSU.B(op == SHEQ ? ret : !ret)); break;
+                    
+                }
+                
+                case EQ:
+                case NE: {
+                    boolean ret = JSU.coerceEquals(left, right);
+                    stack.push(JSU.B(op == EQ ? ret : !ret)); break;
+                }
+
+                default: throw new Error("unknown opcode " + op);
+                } }
+            }
+
+        } catch(Exception e) {
+               if(!(e instanceof JSExn)){
+                       if(e instanceof InterruptedException) e = new 
JSExn((InterruptedException)e);
+                       else e = new JSExn(e);
+               }
+            catchException((JSExn)e);
+            pc--; // it'll get incremented on the next iteration
+        } // end try/catch
+        } // end for
+    }
+    
+    private JS[] readArgs(Object arg){
+               if (arg instanceof JSNumber) {
+                       return readArgs(((JSNumber) arg).toInt32());
+               } else
+                       return (JS[]) arg;
+    }
+    
+    private JS[] readArgs(int n){
+       // FIXME: we should be able to recycle JS[]'s here
+               JS[] jsargs = new JS[n];
+               for (int i = jsargs.length - 1; i >= 0; i--)
+                       jsargs[i] = (JS) stack.pop();
+               return jsargs;
+    }
+    
+    //public void setTraceLine(String s){      nextTraceLine = s;}
+    
+    /** tries to find a handler withing the call chain for this exception
+        if a handler is found the interpreter is setup to call the exception 
handler
+        if a handler is not found the exception is thrown
+    */
+    void catchException(JSExn e) throws JSExn {
+        while(!stack.empty()) {
+            Object o = stack.pop();
+            if (o instanceof CatchMarker || o instanceof TryMarker) {
+                boolean inCatch = o instanceof CatchMarker;
+                if(inCatch) {
+                    o = stack.pop();
+                    if(((TryMarker)o).finallyLoc < 0) continue; // no finally 
block, keep going
+                }
+                if(!inCatch && ((TryMarker)o).catchLoc >= 0) {
+                    // run the catch block, this will implicitly run the 
finally block, if it exists
+                    stack.push(o);
+                    stack.push(catchMarker);
+                    stack.push(e.asObject());
+                    f = ((TryMarker)o).f;
+                    scope = ((TryMarker)o).scope;
+                    pc = ((TryMarker)o).catchLoc;
+                    return;
+                } else {
+                    stack.push(new FinallyData(e));
+                    f = ((TryMarker)o).f;
+                    scope = ((TryMarker)o).scope;
+                    pc = ((TryMarker)o).finallyLoc;
+                    return;
+                }
+            }
+        }
+        throw e;
+    }
+
+    void setupTrap(JS.Trap t, JS val, TrapMarker cm, JS trapname) throws JSExn 
{
+        stack.push(cm);
+        JSArgsTrap ta =new JSArgsTrap(t, val, 
trapname==null?t.key():trapname); 
+        cm.trapargs = ta;
+        stack.push(ta);
+        JSFunction jsfunc = (JSFunction)t.function();
+        f = jsfunc.f;
+        scope = jsfunc.parentScope;
+        pc = 0;
+    }
+
+
+    // Markers 
//////////////////////////////////////////////////////////////////////
+
+    static class Marker {}
+    //String nextTraceLine = null;
+    static class CallMarker extends Marker implements Backtraceable{
+        final int pc;
+        final Scope scope;
+        final Function f;
+        //String traceLine = null;
+        public CallMarker(Interpreter cx) {
+            pc = cx == null ? -1 : cx.pc + 1;
+            scope = cx == null ? null : cx.scope;
+            f = cx == null ? null : cx.f;
+            /*if(cx!=null){
+               this.traceLine =        cx.nextTraceLine;
+               cx.nextTraceLine = null;
+            }*/
+        }
+
+        public String traceLine(){
+            if(f == null) return null;
+            String s = f.sourceName + ":" + f.line[pc-1];
+            if(this instanceof Interpreter.TrapMarker) 
+                s += " (trap on " + 
JSU.toString(((Interpreter.TrapMarker)this).t.key()) + ")";
+            //if(traceLine!=null)  s = traceLine +"/"+s;
+               return s;
+            
+        }
+    }
+    
+    static class TrapMarker extends CallMarker {
+        JS.Trap t;
+        JS val;
+        JSArgsTrap trapargs;
+        final boolean pauseOnCascade;     // FOOTNOTE 2
+        public TrapMarker(Interpreter cx, JS.Trap t, JS val, boolean 
pauseOnCascade) {
+            super(cx);
+            this.t = t;
+            this.val = val;
+            this.pauseOnCascade = pauseOnCascade;
+        }
+    }
+    
+    static class CatchMarker extends Marker { }
+    private static final CatchMarker catchMarker = new CatchMarker();
+    
+    static class LoopMarker extends Marker {
+        final public int location;
+        final public String label;
+        final public Scope scope;
+        final public boolean canContinue;
+        public LoopMarker(int location, String label, Scope scope, boolean 
canContinue) {
+            this.location = location;
+            this.label = label;
+            this.scope = scope;
+            this.canContinue = canContinue;
+        }
+    }
+    static class TryMarker extends Marker {
+        final public int catchLoc;
+        final public int finallyLoc;
+        final public Scope scope;
+        final public Function f;
+        public TryMarker(int catchLoc, int finallyLoc, Interpreter cx) {
+            this.catchLoc = catchLoc;
+            this.finallyLoc = finallyLoc;
+            this.scope = cx.scope;
+            this.f = cx.f;
+        }
+    }
+    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; 
this.exn = null; }
+        public FinallyData(JSExn exn) { this.exn = exn; this.op = -1; this.arg 
= null; } // Just throw this exn
+    }
+    
+
+    
+    
+    static class Stub extends JS.Immutable {
+        private JS method;
+        JS obj;
+        public Stub(JS obj, JS method) { this.obj = obj; this.method = method; 
}
+               public JS apply(JS this_, JS[] args) throws JSExn {
+                       return obj.callMethod(this_, method, args);
+               }
+        
+               public JS get(JS key) throws JSExn {
+                       if ("apply".equals(JSU.toString(key))) {
+                               return new JS.Immutable() {
+                                       public JS apply(JS this_, JS[] args) 
throws JSExn {
+                                               JS[] args2 = 
JSU.checkApply(args);
+                                               JS this_2 = 
args.length>0?args[0]:null; 
+                                               return Stub.this.apply(this_2, 
args2);
+                                       }
+                               };
+                       }
+                       return super.get(key);
+               }
+    }
+    
+    final class Stack {
+        private static final int MAX_STACK_SIZE = 512;
+        private Object[] stack = new Object[8];
+        private int sp = 0;
+        
+        boolean empty() { return sp == 0; }
+        void push(Object o) throws JSExn { if(sp == stack.length) grow(); 
stack[sp++] = o; }
+        Object peek() { return peek(0); }
+        Object peek(int i) { 
+               if(sp -i <= 0) throw new RuntimeException("stack underflow"); 
+               return stack[sp-1-i]; 
+        }
+        final Object pop() { if(sp == 0) throw new RuntimeException("stack 
underflow"); return stack[--sp]; }
+        void swap() throws JSExn {
+            if(sp < 2) throw new JSExn("stack overflow");
+            Object tmp = stack[sp-2];
+            stack[sp-2] = stack[sp-1];
+            stack[sp-1] = tmp;
+        }
+        CallMarker findCall() {
+            for(int i=sp-1;i>=0;i--) if(stack[i] instanceof CallMarker) return 
(CallMarker) stack[i];
+            return null;
+        }
+        /* Used in the debugger */
+               public int callCount() {
+                       int c = 0;
+                   for(int i=sp-1;i>=0;i--) if(stack[i] instanceof CallMarker) 
c++;
+               return c;
+               }
+        void grow() throws JSExn {
+            if(stack.length >= MAX_STACK_SIZE) throw new JSExn("stack 
overflow");
+            Object[] stack2 = new Object[stack.length * 2];
+            System.arraycopy(stack,0,stack2,0,stack.length);
+            stack = stack2;
+        }       
+        
+        private int previous(int current){
+               for(int i=current-1;i>=0;i--) {
+                       if (stack[i] instanceof Backtraceable) return i;
+               }
+               return -1;
+        }
+        
+        void backtrace(JSExn e) {
+               int i = sp;
+               // DEBUG (MAY add this to JSexn)
+               // e.addBacktrace("(Bytecode " + pc+")");
+                       
+               boolean topcall = true;
+               while( (i=previous(i))!=-1){
+                       Backtraceable cm = (Backtraceable)stack[i];
+                       if (topcall && cm instanceof CallMarker) {
+                               e.addBacktrace(f.sourceName + ":" + f.line[pc]);
+                               topcall = false;
+                       }
+                       e.addBacktrace(cm.traceLine());
+               }
+        }
+        
+        String stackframe(int offset) {
+               int i = sp;
+               boolean topcall = true;
+               while( (i=previous(i))!=-1){
+                       Backtraceable cm = (Backtraceable)stack[i];
+                       if (topcall && cm instanceof CallMarker) {
+                               if(offset==0) return f.sourceName + ":" + 
f.line[pc];
+                               topcall = false;
+                               offset--;
+                       }
+                       if(offset==0) return cm.traceLine();
+                       offset--;
+               }
+               return null;
+        }
+        
+        public String toString(){
+               String r = "";
+               for(int i=0; i<sp; i++){
+                       r+= JSU.toString(stack[i]);
+                       r+=", ";
+               }
+               return r;
+        }
+        /*
+        void backtrace(JSExn e) {
+               for(int i=sp-1;i>=0;i--) {
+                       if (stack[i] instanceof CallMarker) break;
+                if (stack[i] instanceof Backtraceable) {
+                       Backtraceable cm = (Backtraceable)stack[i];
+                    e.addBacktrace(cm.traceLine());
+                }
+            }
+               if(f!=null)
+                       e.addBacktrace(f.sourceName + ":" + f.line[pc]);
+               for(int i=sp-1;i>=0;i--) {
+                if (stack[i] instanceof CallMarker) {
+                       CallMarker cm = (CallMarker)stack[i];
+                    e.addBacktrace(cm.traceLine());
+                }
+            }
+        }*/
+    }
+
+       public void enterNonJSCall(Backtraceable call) throws JSExn{
+               stack.push(call);
+       }
+
+       public void exitNonJSCall(){
+               stack.pop();
+       }
+       
+       
+    static private JS add(JS left, JS right) throws JSExn{
+        JSNumber leftn = JSU.expectNumber(left);
+        JSNumber rightn = JSU.expectNumber(right);
+        int kind = JSNumber.dominant(leftn, rightn);
+        if(kind == JSNumber.N_INT32){
+            long r = (long)leftn.toInt32()+(long)rightn.toInt32();
+            return JSU.N(r);
+        }else if(kind == JSNumber.N_RATIONAL){
+            return JSU.N(leftn.toRational().add(rightn.toRational()));
+        }else{
+            return JSU.N(leftn.toDouble()+rightn.toDouble());
+        }
+    }
+
+    static private JS subtract(JS left, JS right) throws JSExn{
+        JSNumber leftn = JSU.expectNumber(left);
+        JSNumber rightn = JSU.expectNumber(right);
+        int kind = JSNumber.dominant(leftn, rightn);
+        if(kind == JSNumber.N_INT32){
+            long r = (long)leftn.toInt32()-(long)rightn.toInt32();
+            return JSU.N(r);
+        }else if(kind == JSNumber.N_RATIONAL){
+            return JSU.N(leftn.toRational().subtract(rightn.toRational()));
+        }else{
+            return JSU.N(leftn.toDouble()-rightn.toDouble());
+        }
+    }
+
+    static private JS multiply(JS left, JS right) throws JSExn{
+        JSNumber leftn = JSU.expectNumber(left);
+        JSNumber rightn = JSU.expectNumber(right);
+        int kind = JSNumber.dominant(leftn, rightn);
+        if(kind == JSNumber.N_INT32){
+            long r = (long)leftn.toInt32()*(long)rightn.toInt32();
+            return JSU.N(r);
+        }else if(kind == JSNumber.N_RATIONAL){
+            return JSU.N(leftn.toRational().multiply(rightn.toRational()));
+        }else{
+            return JSU.N(leftn.toDouble()*rightn.toDouble());
+        }
+    }
+    
+    static private JS divide(JS left, JS right) throws JSExn{
+        JSNumber leftn = JSU.expectNumber(left);
+        JSNumber rightn = JSU.expectNumber(right);
+        int kind = JSNumber.dominant(leftn, rightn);
+        if(kind == JSNumber.N_RATIONAL){
+            return JSU.N(leftn.toRational().divide(rightn.toRational()));
+        }else{
+            return JSU.N(leftn.toDouble()/rightn.toDouble());
+        }
+    }   
+    
+    static private JS modulo(JS left, JS right) throws JSExn{
+        JSNumber leftn = JSU.expectNumber(left);
+        JSNumber rightn = JSU.expectNumber(right);
+        int kind = JSNumber.dominant(leftn, rightn);
+        if(kind == JSNumber.N_INT32){
+            int r = leftn.toInt32()%rightn.toInt32();
+            return JSU.N(r);
+        }else if(kind == JSNumber.N_RATIONAL){
+            return JSU.N(leftn.toRational().mod(rightn.toRational()));
+        }else{
+            return JSU.N(leftn.toDouble()%rightn.toDouble());
+        }
+    }    
+    
+    public String toString(){
+       return getWhere();
+    }
+}
+
+/* FOOTNOTES
+ * 1. Automatic cascading has been ripped out. Fundamentally unclean, and only
+ * really there as syntactic sugar, even dubious on that level.
+ * 
+ * (Automatic cascading is a mechanism for making traps listener like... i.e.
+ * though do not effect the put operation, they just do something when one 
happens.
+ * In practise it makes implementation harder, the resulting JS code becomes 
less
+ * clear, and the same effect can be had in JS with the addition of a 
'cascade'.)
+ * 
+ * 
+ * 2. 'pause on cascade' is a mechanism with which at the
+ * final cascade (i.e. no more traps to be fired) the interpreter gives up 
control 
+ * (returns out) to let the original calling code decide what to do. 
+ * 
+ * This is necessary to allow (without hacks)
+ * a) traps in the get(JS) and put(JS,JS) methods as this would cause an 
infinite loop
+ * as they call themselves on the final cascade. 
+ * b) speed, as it allows us to store some properties as class fields and not 
force us
+ * to go through get/put to modify them. 
+ * c) ignoring of modified values/not putting a value at all (i.e. its just a 
virtual
+ * property that traps can 'listen' to). 
+ * (use cases are in org.vexi.core.Box.jpp) 
+ */
+

Modified: 
branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Scheduler.java
===================================================================
--- branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Scheduler.java 
2015-02-14 21:59:36 UTC (rev 4762)
+++ branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Scheduler.java 
2015-02-16 01:01:31 UTC (rev 4763)
@@ -212,16 +212,14 @@
         if (jsthread==null) {
             setJSThread(new Thread(this, t.function(), false, null));
         }
-        Interpreter old = jsthread.currentInterpreter;
-        jsthread.currentInterpreter = new Interpreter(jsthread, t, val, true, 
trapname);
-        jsthread.currentInterpreter.old = old;
+        Interpreter I = new Interpreter(jsthread, t, val, true, trapname);
         // REMARK - this thread is unpausable, so setting this static variable
         // cannot be interefered with by other js executions. Could be done in 
the
         // interpreter when returning instead of cascading as well.
         cascadedTo = null;
         // returns the cascaded value or 
         // Interpreter.CASCADE_PREVENTED if the trap returned instead 
-        return (JS)jsthread.currentInterpreter.run(null);
+        return (JS)I.run(null);
     }
 
     /** Execute write traps, part 2 */
@@ -270,14 +268,12 @@
         if (jsthread==null) {
             setJSThread(new Thread(this, t.function(), false, null));
         }
-        Interpreter old = jsthread.currentInterpreter;
-        jsthread.currentInterpreter = new Interpreter(jsthread, t, null, true, 
trapname);
-        jsthread.currentInterpreter.old = old;
+        Interpreter I = new Interpreter(jsthread, t, null, true, trapname);
         // REMARK - this thread is unpausable, so setting this static variable
         // cannot be interfered with by other js executions. Could be done in 
the
         // interpreter when returning instead of cascading as well.
         cascadedTo = null;
-        return (JS)jsthread.currentInterpreter.run(null);
+        return (JS)I.run(null);
         //
         //System.out.println("cascadedTo " + cascadedTo);
     }
@@ -312,10 +308,9 @@
         }
         Interpreter old = jsthread.currentInterpreter;
         // Always false. Restarting paused nested Interpreters not supported.
-        jsthread.currentInterpreter = new Interpreter(jsthread, 
(JSFunction)function, /*jsthread.pauseable*/false, args);
-        jsthread.currentInterpreter.old = old;
+        Interpreter I = new Interpreter(jsthread, (JSFunction)function, 
/*jsthread.pauseable*/false, args);
         try {
-            return (JS)jsthread.currentInterpreter.run(null);
+            return (JS)I.run(null);
         } finally {   
             if (isFirst) {
                 jsthread.destroy();

Deleted: 
branches/vexi3/org.vexi-library.js/src/main/jpp/org/ibex/js/Interpreter.jpp
===================================================================
--- branches/vexi3/org.vexi-library.js/src/main/jpp/org/ibex/js/Interpreter.jpp 
2015-02-14 21:59:36 UTC (rev 4762)
+++ branches/vexi3/org.vexi-library.js/src/main/jpp/org/ibex/js/Interpreter.jpp 
2015-02-16 01:01:31 UTC (rev 4763)
@@ -1,1063 +0,0 @@
-// Copyright 2000-2008 the Contributors, as shown in the revision logs.
-// Licensed under the Apache Software License 2.0 ("the License").
-// You may not use this file except in compliance with the License.
-
-
-package org.ibex.js;
-
-import org.ibex.js.parse.*;
-
-/*@PAGE(concept=Special Variables)
- * 
- * <h3>General Variables</h3>
- * <p>The following special variables are accessible in general template 
script.</p>
- * <dl>
- *   <dt><code>thisbox</code></dt>
- *   <dd><p>A reference to the Box defined by the XML node containing it.  It 
can also be thought
- *   of as a Box property.</p></dd>
- *   <dt><code>thisobj</code></dt>
- *   <dd><p>A reference to the Object/Array/etc defined by the XML node 
containing it.</p></dd>
- *   <dt><code>static</code></dt>
- *   <dd><p>The static context of the template in which it is called.</p></dd>
- *   <dt><code>vexi</code></dt>
- *   <dd><p>The immutable global vexi object.</p></dd>
- * </dl>
- * 
- * <h3>Function Variables</h3>
- * <p>The following special variables are accessible in the body of a 
function.</p>
- * <dl>
- *   <dt><code>arguments</code></dt>
- *   <dd><p>An immutable array of the arguments passed into a function. Its 
length is detirmined
- *   when the function is called and not by the method signature. </p></dd>
- *   <dt><code>callee</code></dt>
- *   <dd><p>The current function being executed</p></dd>
- *   <dt><code>this</code></dt>
- *   <dd>
- *     <p><code>this</code><i> is a keyword</i></p>
- *     <p>The context of the execution. When a function is being accessed as 
the property of an
- *     object the context is the object. For example in <code>a.f()</code> in 
the body of 
- *     <code>f</code> this refers to <code>a</code>.</p>
- *     
- *     <p>If a function is executed as a var then <code>this==null</code>.</p>
- *     
- *     <p>In top level code the value of <code>this</code> may be assigned to 
a relevant
- *     context.</p>
- *   </dd>
- * </dl>
- * 
- * <h3>Trap Variables</h3>
- * <p>The following special variables are accessible in the body of a trap.</p>
- * <dl>
- *   <dt><code>cascade</code></dt>
- *   <dd>
- *     <p><code>cascade</code><i> is a keyword</i></p>
- *     <p>In a read trap, read from cascade to get the value from traps lower 
in the read trap
- *     chain or, if there are no further traps, the object property the trap 
is placed upon.
- *     (Reading directly from the property will invoke the read trap chain 
again, possibly causing
- *     an infinite loop.)</p>   
- *     <p>In a write trap, putting to cascade will caused lower traps in the 
write trap chain to be
- *     executed or, if there are no further traps, the value passed to cascade 
is put to the object
- *     property the trap is placed upon. (Writing directly to the property 
will invoke the write
- *     trap chain again, possibly causing an infinite loop.)</p>       
- *   </dd>
- *   <dt><code>trapname</code></dt>
- *   <dd><p>The name of the property upon which a trap is been placed.</p></dd>
- *   <dt><code>trapee</code></dt>
- *   <dd><p>The object upon which a trap is been placed. <i>Synonym for 
this</i></p></dd>
- *   <dt><code>callee</code></dt>
- *   <dd><p>The trap function - useful for anonymous removal of traps.</p></dd>
- * </dl>
- * */
-/** Encapsulates a single JS interpreter (ie call stack) */
-public class Interpreter implements ByteCodes, Tokens, Constants {
-
-       
-       // FIXME - use Thread.cascadedTo==NULL instead
-       public final static JSNumber CASCADE_PREVENTED = new JSNumber.I(1);
-    
-    // Instance members and methods 
//////////////////////////////////////////////////////////////////////
-    
-       // FIXME - should not be public but devl core uses them
-    int pausecount;               ///< the number of times pause() has been 
invoked; -1 indicates unpauseable
-    public Function f = null;          ///< the currently-executing JSFunction
-    public Scope scope;                ///< the current top-level scope (LIFO 
stack via NEWSCOPE/OLDSCOPE)
-    final public Stack stack = new Stack(); ///< the object stack
-    public int pc = 0;                   ///< the program counter
-
-    final Thread thread;
-    Interpreter old = null;
-   
-    public Interpreter(Thread thread){this.thread = thread;}
-    Interpreter(Thread thread, JSFunction f, boolean pauseable, JS[] args) {
-        this(thread);
-        this.f = f.f;
-        this.pausecount = pauseable ? 0 : -1;
-        this.scope = f.parentScope;
-        try {
-            stack.push(new CallMarker(null));    // the "root function 
returned" marker -- f==null
-            stack.push(new JSArgs(args, f, null)); // FIXME: temprorary bug fix
-        } catch(JSExn e) {
-            throw new Error("should never happen");
-        }
-    }
-    
-    Interpreter(Thread thread, JS.Trap t, JS val, boolean pauseOnCascade, JS 
trapname) {
-        this(thread);
-        this.pausecount = -1;
-        try {
-            setupTrap(t,val,new 
TrapMarker(null,t,val,pauseOnCascade),trapname);
-        } catch(JSExn e) {
-            throw new Error("should never happen");
-        }
-    }
-
-    private boolean get = false;
-    // FIXME: split this stuff out into a Script instance control object
-    //        so it's possible to make JS either single or multi threaded.
-    /** this is the only synchronization point we need in order to be 
threadsafe */
-    public synchronized Object run(Object o) throws JSExn {
-        if (f == null) throw new RuntimeException("function already finished");
-        if (scope == null) throw new RuntimeException("scope is null");
-        
-        if( o instanceof JSExn){
-               // Fill in empty exceptions. Exceptions coming created in 
background
-               // calls don't have their stack traces filled in.
-               ((JSExn)o).fillIfEmpty(this);
-               // Throw excpetion - move interpreter to catch block 
-               catchException((JSExn)o);
-        }else if (get)
-               stack.push(o);
-
-        return run();
-    }
-    
-    public void pause(String forwhat) throws JSExn {
-        if (pausecount == -1 || f == null) throw new JSExn("Cannot "+forwhat+" 
in foreground thread");
-        pausecount++;
-        switch(f.op[pc]) {
-            case Tokens.RETURN: case ByteCodes.PUT: get = false; break;
-            case ByteCodes.GET: case ByteCodes.GET_PRESERVE: case 
ByteCodes.CALLMETHOD: case ByteCodes.CALL: get = true; break;
-            default: throw new Error("paused on unexpected bytecode: " + 
f.op[pc]);
-        }
-    }
-
-    int getLine() {
-        return f == null || pc < 0 || pc >= f.size ? -1 : f.line[pc];
-    }
-
-    String getSourceName() {
-        return f == null ? null : f.sourceName;
-    } 
-    String getWhere(){ return getSourceName() + ":" + getLine(); }
-
-    private JSExn je(String s) { return new JSExn(getWhere() + " " + s); }
-
-    
-    // REMARK - encapsulate loop jump operations here, so that we can test
-    // interuption, but only when there is a chance we're in an infinite loop.
-    private void jump(int delta_pc) throws InterruptedException{
-        jumpAbs(pc+delta_pc);
-    }
-    private void jumpAbs(int pc) throws InterruptedException{
-       this.pc = pc;
-       if(java.lang.Thread.interrupted()) throw new InterruptedException();
-    }
-    
-    private JS run() throws JSExn {
-        // if pausecount changes after a get/put/call, we know we've been 
paused
-        if(pausecount>0)
-               pausecount --;
-
-        OUTER: for(;; pc++) {
-        try {
-               if(Instr.interpreter!=null) Instr.interpreter.handle(this); 
-
-            int op = f.op[pc];
-            Object arg = f.arg[pc];
-            if(op == FINALLY_DONE) {
-                FinallyData fd = (FinallyData) stack.pop();
-                if(fd == null) continue OUTER; // NOP
-                if(fd.exn != null) throw fd.exn;
-                op = fd.op;
-                arg = fd.arg;
-            }
-            
-            // DEBUG
-            // DevUtil.debugStack(this, op, arg);
-               
-            switch(op) {
-            case LITERAL: stack.push(arg); break;
-            // GUT ought to fetch constructors
-            case OBJECT: stack.push(new JS.Obj()); break;
-            case ARRAY: stack.push(new JSArray(JSU.toInt((JS)arg))); break;
-            //case DECLARE: scope.declare((JS)(arg==null ? stack.peek() : 
arg)); if(arg != null) stack.push((JS)arg); break;
-            case JT: if (JSU.isTruthy((JS)stack.pop())) 
jump(JSU.toInt((JS)arg) - 1); break;
-            case JF: if (!JSU.isTruthy((JS)stack.pop())) 
jump(JSU.toInt((JS)arg) - 1); break;
-            case JMP: jump(JSU.toInt((JS)arg) - 1); break;
-            case POP: stack.pop(); break;
-            case SWAP: stack.swap(); break;
-            case DUP: 
-               if(arg==null) stack.push(stack.peek()); 
-               else stack.push(stack.peek(JSU.toInt((JS)arg)));
-               break;
-            case NEWSCOPE: {
-                int n = JSU.toInt((JS)arg);
-                scope = new Scope(scope,(n>>>16)&0xffff,(n>>>0)&0xffff);
-                break;
-            }
-            case OLDSCOPE: scope = scope.parent; break;
-            case GLOBALSCOPE: stack.push(scope.getGlobal()); break;
-            case SCOPEGET: stack.push(scope.get((JS)arg)); break;
-            case SCOPEPUT: {
-                // FIXME: HACK: share this around more and find the callee.
-                Object val = stack.peek();
-                if (val != null && val instanceof JS[]) val = new 
JSArgs((JS[])val, null, null);
-                scope.put((JS)arg, (JS)val); break;
-            }
-            case ASSERT: if (!JSU.isTruthy((JS)stack.pop())) throw 
je("ibex.assertion.failed"); break;
-            case BITNOT: stack.push(JSU.N(~JSU.toLong((JS)stack.pop()))); 
break;
-            case BANG: stack.push(JSU.B(!JSU.isTruthy((JS)stack.pop()))); 
break;
-            case NEWFUNCTION: 
stack.push(JSU.cloneWithNewParentScope((Function)arg, scope)); break;
-            case LABEL: break;
-
-            case TYPEOF: {
-                Object o = stack.pop();
-                if (o == null) stack.push(SC_null);
-                else stack.push(((JS)o).type());
-                break;
-            }
-
-            case INSTANCEOF: {
-               JS c = (JS)stack.pop();
-               JS o = (JS)stack.pop();
-               if(c==null) throw new JSExn("Cannot instanceof null");
-               stack.push(JSU.B(o==null?false:o.instanceOf(c)));
-               break;
-            }
-            
-            case PUSHKEYS: {
-                JS o = (JS)stack.peek();
-                stack.push(o == null ? null : o.keys());
-                break;
-            }
-
-            case LOOP:
-            case SWITCH:
-                //stack.push(new LoopMarker(pc, (String)(pc > 0 && f.op[pc - 
1] == LABEL ? f.arg[pc - 1] : null), scope));
-                //stack.push(JSU.T);
-                stack.push(new LoopMarker(pc, pc > 0 && f.op[pc - 1] == LABEL 
? (String)f.arg[pc - 1] : (String)null, scope, op == LOOP));
-                if(op == LOOP) stack.push(JSU.T);
-                break;
-
-            case BREAK:
-            case CONTINUE:
-                while(!stack.empty()) {
-                    Object o = stack.pop();
-                    if (o instanceof CallMarker) throw je("Tried to 
'"+(op==BREAK?"break":"continue")+"' when not within a 
loop"+(op==BREAK?"/switch":""));
-                    if (o instanceof TryMarker) {
-                        if(((TryMarker)o).finallyLoc < 0) continue; // no 
finally block, keep going
-                        stack.push(new FinallyData(op, arg));
-                        scope = ((TryMarker)o).scope;
-                        pc = ((TryMarker)o).finallyLoc - 1;
-                        continue OUTER;
-                    }
-                    if (o instanceof LoopMarker) {
-                        //if (arg == null || 
arg.equals(((LoopMarker)o).label)) {
-                            //int loopInstructionLocation = 
((LoopMarker)o).location;
-                        LoopMarker lm = (LoopMarker) o;
-                        if(op == CONTINUE && !lm.canContinue) continue;
-                        if (arg == null || arg.equals(lm.label)) {
-                            int loopInstructionLocation = lm.location;
-                            int endOfLoop = 
JSU.toInt((JS)f.arg[loopInstructionLocation]) + loopInstructionLocation;
-                            scope = lm.scope;
-                            if (op == CONTINUE) { stack.push(o); 
stack.push(JSU.F); }
-                            if(op == BREAK) pc = endOfLoop - 1;
-                            else jumpAbs(loopInstructionLocation);
-                            continue OUTER;
-                        }
-                    }
-                }
-                throw new Error((op==BREAK?"BREAK":"CONTINUE")+" invoked but 
couldn't find LoopMarker at " +
-                                getSourceName() + ":" + getLine());
-
-            case TRY: {
-                int[] jmps = (int[]) arg;
-                // jmps[0] is how far away the catch block is, jmps[1] is how 
far away the finally block is
-                // each can be < 0 if the specified block does not exist
-                stack.push(new TryMarker(jmps[0] < 0 ? -1 : pc + jmps[0], 
jmps[1] < 0 ? -1 : pc + jmps[1], this));
-                break;
-            }
-
-            case RETURN: {
-                JS retval = (JS)stack.pop();
-                while(!stack.empty()) {
-                    Object o = stack.pop();
-                    if (o instanceof TryMarker) {
-                        if(((TryMarker)o).finallyLoc < 0) continue;
-                        stack.push(retval); 
-                        stack.push(new FinallyData(RETURN));
-                        scope = ((TryMarker)o).scope;
-                        pc = ((TryMarker)o).finallyLoc - 1;
-                        continue OUTER;
-                    } else if (o instanceof CallMarker) {
-                       boolean isWriteTrap = false;
-                        if (o instanceof TrapMarker) { // handles return 
component of a write trap
-                            TrapMarker tm = (TrapMarker) o;
-                            isWriteTrap = tm.t.isWriteTrap();
-                            if (isWriteTrap) {
-                                // REMOVED automatic cascading(FOOTNOTE:1)
-                                       thread.faction.cascadedTo = null;
-                                       retval = CASCADE_PREVENTED;
-                            }
-                        }
-                        CallMarker cm = (CallMarker) o;
-                        scope = cm.scope;
-                        pc = cm.pc - 1;
-                        f = cm.f;
-                        if(!isWriteTrap)
-                               stack.push(retval);
-                        if (f == null) return retval;
-                        continue OUTER;
-                    }
-                }
-                throw new Error("error: RETURN invoked but couldn't find a 
CallMarker!");
-            }
-                
-            case CASCADE: {
-                boolean write = JSU.T==arg;
-                JS val = write ? (JS)stack.pop() : null;
-                CallMarker o = stack.findCall();
-                if(!(o instanceof TrapMarker)) throw new JSExn("Tried to 
'cascade' while not in a trap");
-                TrapMarker tm = (TrapMarker) o;
-                JS key = tm.t.key();
-                JS target = tm.t.target();
-                if(tm.t.isWriteTrap() != write)
-                    throw new JSExn("Tried to do a "+(write?"write":"read") + 
" 'cascade' in a " + (write?"read":"write") + " trap");
-                JS.Trap t = write ? tm.t.nextWrite() : tm.t.nextRead();
-                while (t == null && target instanceof JS.Clone) {
-                    target = ((JS.Clone)target).clonee;
-                    t = target.getTrap(key);
-                    if (t != null) t = write ? t.findWrite() : t.findRead();
-                }
-                if(write) {
-                    stack.push(val);
-                }
-                if(t != null) {
-                    setupTrap(t,val,new 
TrapMarker(this,t,val,tm.pauseOnCascade), tm.trapargs.trapname);
-                    pc--; // we increment later
-                } else {
-                       thread.faction.cascadedTo = tm.trapargs.trapname;
-                    if(write) {
-                        if (tm.pauseOnCascade) { pc++; return val; }
-                        target.put(key,val);
-                    } else {
-                       if (tm.pauseOnCascade) {
-                               // FIXME should move this to setup of 
interpreter
-                               // we need it true for when we restart the 
interpreter
-                               get = true; 
-                               pc++; return null; }
-                       else{
-                            JS ret = target.get(key);
-                            if (ret != null && ret == JS.METHOD) ret = new 
Stub(target, key);
-                            stack.push(ret);
-                       }
-                    }
-                    if (pausecount > 0) { pc++; return null; }   // we were 
paused                    
-                }
-                break;
-            }
-                
-            case PUT: {
-                JS val = (JS)stack.pop();
-                JS key = (JS)stack.pop();
-                JS target = (JS)stack.peek();
-                if (target == null) throw je("Tried to put " + 
JSU.toString(val) + " to the " + JSU.toString(key) + " property on the null 
value");
-                if (key == null) throw je("Tried to assign \"" + 
JSU.toString(val) + "\" to the null key");
-                
-                JS.Trap t = target.getTrap(key);
-                if(t != null) t = t.findWrite();
-                
-                stack.push(val);
-                
-                if(t != null) {
-                    setupTrap(t,val,new TrapMarker(this,t,val,false),null);
-                    pc--; // we increment later
-                } else {
-                    target.put(key,val);
-                    if (pausecount > 0) { pc++; return null; }   // we were 
paused
-                }
-                break;
-            }
-
-            case GET:
-            case GET_PRESERVE: {
-                JS target, key;
-                if (op == GET) {
-                    key = arg == null ? (JS)stack.pop() : (JS)arg;
-                    target = (JS)stack.pop();
-                } else {
-                       if(arg==null){
-                        key = (JS)stack.pop();
-                        target = (JS)stack.peek();
-                        stack.push(key);
-                       }else{
-                               key = (JS)arg;
-                               target = (JS)stack.peek();
-                       }
-                }
-                JS ret = null;
-                if (key == null) throw je("Tried to get the null key from " + 
JSU.toString(target));
-                if (target == null) throw je("Tried to get property \"" + 
JSU.toString(key) + "\" from the null object");
-                
-                JS.Trap t = target.getTrap(key);
-                if (t != null) t = t.findRead();
-                 
-                if(t != null) {
-                       JS f = t.function();
-                       if(f instanceof JSFunction){
-                               setupTrap(t,null,new 
TrapMarker(this,t,null,false),null);
-                               pc--; // we increment later
-                               break;
-                       }
-                       // Trap was an override. Simply return the value. 
-                       ret = f;
-                }
-                if(ret==null) ret = target.get(key);
-                if (pausecount > 0) { pc++; return null; }   // we were paused
-
-                if (ret != null && ret == JS.METHOD) ret = new Stub(target, 
key);
-                stack.push(ret);
-                break;
-            }
-            
-            case NEW: {
-               JS[] jsargs = readArgs(arg);
-               JS constructor = (JS) stack.pop();
-                               if (constructor == null)
-                                       throw new JSExn("null is not a 
constructor");
-               stack.push(constructor.new_(jsargs));
-               break;
-            }
-            
-            case CALL: case CALLMETHOD: {
-                               JS[] jsargs = readArgs(arg);
-
-                               JS ret = null;
-                               JS callee = (JS) stack.pop();
-                               JS object = null;
-                               if (op == CALLMETHOD) {
-                                       if (callee == null) {
-                                               JS method = (JS) stack.pop();
-                                               object = (JS) stack.pop();
-                                               if (object instanceof 
JSPrimitive)
-                                                       throw new JSExn("Method 
'"
-                                                                       + 
JSU.toString(method)
-                                                                       + "' 
not defined for " + object.type());
-                                               throw new JSExn("Function '" + 
JSU.toString(method)
-                                                               + "' not found 
in " + JSU.toString(object));
-                                       } else {
-                                               stack.pop();
-                                               object = (JS) stack.pop();
-                                       }
-                               }
-                               if (callee == null)
-                                       throw new JSExn("Tried to call null 
object");
-
-                               switch (callee.callType()) {
-                               case JS.CALLTYPE_FUNCTION: {
-                                       stack.push(new CallMarker(this));
-                                       JSFunction jsfunc = (JSFunction) callee;
-                                       f = jsfunc.f;
-                                       stack.push(new JSArgs(jsargs, jsfunc, 
object));
-                                       scope = jsfunc.parentScope;
-                                       pc = -1;
-                                       break;
-                               }
-                               case JS.CALLTYPE_APPLY:
-                                       JS[] args = JSU.checkApply(jsargs);
-                                       JS this_ = 
jsargs.length>0?jsargs[0]:null; 
-                                       if(callee instanceof JSFunction.Apply){
-                                               stack.push(new 
CallMarker(this));
-                                               JSFunction jsfunc = 
(JSFunction) ((JSFunction.Apply) callee).f;
-                                               f = jsfunc.f;
-                                               stack.push(new JSArgs(args, 
jsfunc, this_));
-                                               scope = jsfunc.parentScope;
-                                               pc = -1;
-                                               break;
-                                       }
-                                       object = this_;
-                                       jsargs = args;
-                                       // fallthrough
-                               case JS.CALLTYPE_METHOD:
-                                       ret = callee.apply(object,jsargs);
-                                       if (pausecount > 0) {
-                                               pc++;
-                                               return null;
-                                       }
-                                       stack.push(ret);
-                                       break;
-                               }
-                               break;
-            }
-
-            case THROW:{
-               JS thrown = (JS)stack.pop();
-               // If already an exception then we recover the JSExn and 
rethrow it
-               if(thrown instanceof JSExn.Obj){
-                       JSExn je = ((JSExn.Obj)thrown).asJSExn();
-                       je.fillIfEmpty(this);
-                       throw je;
-               }else
-                       throw new JSExn(thrown, null, null, this);
-            }
-
-            case ADD_TRAP: case DEL_TRAP: {
-                // A trap addition/removal
-                JS val = (JS)stack.pop();
-                JS key = (JS)stack.pop();
-                JS js = (JS)stack.peek();
-                
-                // some checks to make sure the assignment is valid
-                // TODO: check val is a function
-                if (key == null) throw new JSExn("Tried to " + (op == ADD_TRAP 
? "add" : "remove")
-                        + " a trap function using a null key on a box or 
object");
-                if (val == null) throw new JSExn("Tried to " + (op == ADD_TRAP 
? "add" : "remove")
-                        + " a null value as a trap function to property '" + 
key.coerceToString() + "'");
-                if (js == null) throw new JSExn("Tried to " + (op == ADD_TRAP 
? "add" : "remove")
-                        + " a trap to property '" + key.coerceToString() + "' 
on a null object ");
-
-                if(op == ADD_TRAP) js.addTrap(key, val);
-                else js.delTrap(key, val);
-                break;
-            }
-
-            case ADD: {
-                int count = ((JSNumber)arg).toInt32();
-                if(count < 2) throw new Error("this should never happen");
-                if(count == 2) {
-                    // common case
-                    JS right = (JS)stack.pop();
-                    JS left = (JS)stack.pop();
-                    JS ret;
-                    if(left instanceof JSString || right instanceof JSString)
-                        ret = 
JSU.S(JSU.toString(left).concat(JSU.toString(right)));
-                    else {
-                        ret = add(left, right);
-                    }
-                    stack.push(ret);
-                } else {
-                    JS[] args = new JS[count];
-                    while(--count >= 0) args[count] = (JS)stack.pop();
-                    if(args[0] instanceof JSString) {
-                        StringBuffer sb = new StringBuffer(64);
-                        for(int i=0;i<args.length;i++) 
sb.append(JSU.toString(args[i]));
-                        stack.push(JSU.S(sb.toString()));
-                    } else {
-                        int numStrings = 0;
-                        for(int i=0;i<args.length;i++) if(args[i] instanceof 
JSString) numStrings++;
-                        if(numStrings == 0) {
-                            double d = 0.0;
-                            for(int i=0;i<args.length;i++) d += 
JSU.toDouble(args[i]);
-                            stack.push(JSU.N(d));
-                        } else {
-                            int i=0;
-                            StringBuffer sb = new StringBuffer(64);
-                            if(!(args[0] instanceof JSString || args[1] 
instanceof JSString)) {
-                                double d=0.0;
-                                do {
-                                    d += JSU.toDouble(args[i++]);
-                                } while(!(args[i] instanceof JSString));
-                                sb.append(JSU.toString(JSU.N(d)));
-                            }
-                            while(i < args.length) 
sb.append(JSU.toString(args[i++]));
-                            stack.push(JSU.S(sb.toString()));
-                        }
-                    }
-                }
-                break;
-            }
-
-            default: {
-                JS right = (JS)stack.pop();
-                JS left = (JS)stack.pop();
-                switch(op) {
-                        
-                case BITOR: stack.push(JSU.N(JSU.toLong(left) | 
JSU.toLong(right))); break;
-                case BITXOR: stack.push(JSU.N(JSU.toLong(left) ^ 
JSU.toLong(right))); break;
-                case BITAND: stack.push(JSU.N(JSU.toLong(left) & 
JSU.toLong(right))); break;
-
-                case SUB: stack.push(subtract(left, right)); break;
-                case MUL: stack.push(multiply(left, right)); break;
-                case DIV: stack.push(divide(left,right)); break; 
-                case MOD: stack.push(modulo(left, right)); break; 
-                        
-                case LSH: stack.push(JSU.N(JSU.toLong(left) << 
JSU.toLong(right))); break;
-                case RSH: stack.push(JSU.N(JSU.toLong(left) >> 
JSU.toLong(right))); break;
-                case URSH: stack.push(JSU.N(JSU.toLong(left) >>> 
JSU.toLong(right))); break;
-                        
-                //#repeat </<=/>/>= LT/LE/GT/GE
-                case LT: {
-                    if(left instanceof JSString && right instanceof JSString)
-                        
stack.push(JSU.B(JSU.toString(left).compareTo(JSU.toString(right)) < 0));
-                    else
-                        stack.push(JSU.B(JSU.toDouble(left) < 
JSU.toDouble(right)));
-                    break;
-                }
-                //#end
-   
-                case SHEQ:
-                case SHNE:{
-                    boolean ret;
-                    if(left == null && right == null) ret = true;
-                    else if(left == null || right == null) ret = false;
-                    else ret = left.equals(right);
-                    stack.push(JSU.B(op == SHEQ ? ret : !ret)); break;
-                    
-                }
-                
-                case EQ:
-                case NE: {
-                    boolean ret = JSU.coerceEquals(left, right);
-                    stack.push(JSU.B(op == EQ ? ret : !ret)); break;
-                }
-
-                default: throw new Error("unknown opcode " + op);
-                } }
-            }
-
-        } catch(Exception e) {
-               if(!(e instanceof JSExn)){
-                       if(e instanceof InterruptedException) e = new 
JSExn((InterruptedException)e);
-                       else e = new JSExn(e);
-               }
-            catchException((JSExn)e);
-            pc--; // it'll get incremented on the next iteration
-        } // end try/catch
-        } // end for
-    }
-    
-    private JS[] readArgs(Object arg){
-               if (arg instanceof JSNumber) {
-                       return readArgs(((JSNumber) arg).toInt32());
-               } else
-                       return (JS[]) arg;
-    }
-    
-    private JS[] readArgs(int n){
-       // FIXME: we should be able to recycle JS[]'s here
-               JS[] jsargs = new JS[n];
-               for (int i = jsargs.length - 1; i >= 0; i--)
-                       jsargs[i] = (JS) stack.pop();
-               return jsargs;
-    }
-    
-    //public void setTraceLine(String s){      nextTraceLine = s;}
-    
-    /** tries to find a handler withing the call chain for this exception
-        if a handler is found the interpreter is setup to call the exception 
handler
-        if a handler is not found the exception is thrown
-    */
-    void catchException(JSExn e) throws JSExn {
-        while(!stack.empty()) {
-            Object o = stack.pop();
-            if (o instanceof CatchMarker || o instanceof TryMarker) {
-                boolean inCatch = o instanceof CatchMarker;
-                if(inCatch) {
-                    o = stack.pop();
-                    if(((TryMarker)o).finallyLoc < 0) continue; // no finally 
block, keep going
-                }
-                if(!inCatch && ((TryMarker)o).catchLoc >= 0) {
-                    // run the catch block, this will implicitly run the 
finally block, if it exists
-                    stack.push(o);
-                    stack.push(catchMarker);
-                    stack.push(e.asObject());
-                    f = ((TryMarker)o).f;
-                    scope = ((TryMarker)o).scope;
-                    pc = ((TryMarker)o).catchLoc;
-                    return;
-                } else {
-                    stack.push(new FinallyData(e));
-                    f = ((TryMarker)o).f;
-                    scope = ((TryMarker)o).scope;
-                    pc = ((TryMarker)o).finallyLoc;
-                    return;
-                }
-            }
-        }
-        throw e;
-    }
-
-    void setupTrap(JS.Trap t, JS val, TrapMarker cm, JS trapname) throws JSExn 
{
-        stack.push(cm);
-        TrapArgs ta =new TrapArgs(t, val, trapname==null?t.key():trapname); 
-        cm.trapargs = ta;
-        stack.push(ta);
-        JSFunction jsfunc = (JSFunction)t.function();
-        f = jsfunc.f;
-        scope = jsfunc.parentScope;
-        pc = 0;
-    }
-
-
-    // Markers 
//////////////////////////////////////////////////////////////////////
-
-    static class Marker {}
-    //String nextTraceLine = null;
-    static class CallMarker extends Marker implements Backtraceable{
-        final int pc;
-        final Scope scope;
-        final Function f;
-        //String traceLine = null;
-        public CallMarker(Interpreter cx) {
-            pc = cx == null ? -1 : cx.pc + 1;
-            scope = cx == null ? null : cx.scope;
-            f = cx == null ? null : cx.f;
-            /*if(cx!=null){
-               this.traceLine =        cx.nextTraceLine;
-               cx.nextTraceLine = null;
-            }*/
-        }
-
-        public String traceLine(){
-            if(f == null) return null;
-            String s = f.sourceName + ":" + f.line[pc-1];
-            if(this instanceof Interpreter.TrapMarker) 
-                s += " (trap on " + 
JSU.toString(((Interpreter.TrapMarker)this).t.key()) + ")";
-            //if(traceLine!=null)  s = traceLine +"/"+s;
-               return s;
-            
-        }
-    }
-    
-    static class TrapMarker extends CallMarker {
-        JS.Trap t;
-        JS val;
-        TrapArgs trapargs;
-        final boolean pauseOnCascade;     // FOOTNOTE 2
-        public TrapMarker(Interpreter cx, JS.Trap t, JS val, boolean 
pauseOnCascade) {
-            super(cx);
-            this.t = t;
-            this.val = val;
-            this.pauseOnCascade = pauseOnCascade;
-        }
-    }
-    
-    static class CatchMarker extends Marker { }
-    private static final CatchMarker catchMarker = new CatchMarker();
-    
-    static class LoopMarker extends Marker {
-        final public int location;
-        final public String label;
-        final public Scope scope;
-        final public boolean canContinue;
-        public LoopMarker(int location, String label, Scope scope, boolean 
canContinue) {
-            this.location = location;
-            this.label = label;
-            this.scope = scope;
-            this.canContinue = canContinue;
-        }
-    }
-    static class TryMarker extends Marker {
-        final public int catchLoc;
-        final public int finallyLoc;
-        final public Scope scope;
-        final public Function f;
-        public TryMarker(int catchLoc, int finallyLoc, Interpreter cx) {
-            this.catchLoc = catchLoc;
-            this.finallyLoc = finallyLoc;
-            this.scope = cx.scope;
-            this.f = cx.f;
-        }
-    }
-    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; 
this.exn = null; }
-        public FinallyData(JSExn exn) { this.exn = exn; this.op = -1; this.arg 
= null; } // Just throw this exn
-    }
-
-    static class JSArgs extends JS.Immutable implements JSArrayLike{
-        private final JS[] args;
-        private final JS callee;
-        private final JS this_;
-        
-        public JSArgs(JS[] args, JS callee, JS this_) { this.args = args; 
this.callee = callee; this.this_ = this_; }
-        
-        public JS getElement(int i) throws JSExn {  return i>=args.length ? 
null : args[i]; }
-        public JS get(JS key) throws JSExn {
-            if(JSU.isInt(key)) {
-                int i = JSU.toInt32(key);
-                return getElement(i);
-            }
-            //#switch(JSU.toString(key))
-               case "callee": return callee;
-            case "length": return JSU.N(args.length);
-            case "this": return this_;
-            //#end
-            return super.get(key);
-        }
-        public JS.Keys keys() throws JSExn { return new 
JSArrayLike.Keys(this);}
-               public JS[] toArray() { return args;}
-               public int size(){ return args.length; }
-    }
-
-    static class TrapArgs extends JS.Immutable {
-        private Trap t;
-        private JS val;
-        private JS trapname;
-        public TrapArgs(Trap t, JS val, JS trapname) { 
-               this.t = t; this.trapname = trapname; this.val = val; 
-        }
-        public JS get(JS key) throws JSExn {
-            if(JSU.isInt(key) && JSU.toInt(key) == 0) return val;
-            //#switch(JSU.toString(key))
-            case "trapee": return t.target();
-            case "callee": return t.function();
-            case "trapname": return trapname;
-            case "length": return t.isWriteTrap() ? NC_1 : NC_0;
-            //#end
-            return super.get(key);
-        }
-        public void put(JS key, JS val) throws JSExn {
-               String prop = JSU.toString(key);
-               if(prop.equals("trapname")){
-                       trapname = val;
-               }else
-                       super.put(key, val);
-        }
-        
-    }
-    
-    
-    static class Stub extends JS.Immutable {
-        private JS method;
-        JS obj;
-        public Stub(JS obj, JS method) { this.obj = obj; this.method = method; 
}
-               public JS apply(JS this_, JS[] args) throws JSExn {
-                       return obj.callMethod(this_, method, args);
-               }
-        
-               public JS get(JS key) throws JSExn {
-                       if ("apply".equals(JSU.toString(key))) {
-                               return new JS.Immutable() {
-                                       public JS apply(JS this_, JS[] args) 
throws JSExn {
-                                               JS[] args2 = 
JSU.checkApply(args);
-                                               JS this_2 = 
args.length>0?args[0]:null; 
-                                               return Stub.this.apply(this_2, 
args2);
-                                       }
-                               };
-                       }
-                       return super.get(key);
-               }
-    }
-    
-    final class Stack {
-        private static final int MAX_STACK_SIZE = 512;
-        private Object[] stack = new Object[8];
-        private int sp = 0;
-        
-        boolean empty() { return sp == 0; }
-        void push(Object o) throws JSExn { if(sp == stack.length) grow(); 
stack[sp++] = o; }
-        Object peek() { return peek(0); }
-        Object peek(int i) { 
-               if(sp -i <= 0) throw new RuntimeException("stack underflow"); 
-               return stack[sp-1-i]; 
-        }
-        final Object pop() { if(sp == 0) throw new RuntimeException("stack 
underflow"); return stack[--sp]; }
-        void swap() throws JSExn {
-            if(sp < 2) throw new JSExn("stack overflow");
-            Object tmp = stack[sp-2];
-            stack[sp-2] = stack[sp-1];
-            stack[sp-1] = tmp;
-        }
-        CallMarker findCall() {
-            for(int i=sp-1;i>=0;i--) if(stack[i] instanceof CallMarker) return 
(CallMarker) stack[i];
-            return null;
-        }
-        /* Used in the debugger */
-               public int callCount() {
-                       int c = 0;
-                   for(int i=sp-1;i>=0;i--) if(stack[i] instanceof CallMarker) 
c++;
-               return c;
-               }
-        void grow() throws JSExn {
-            if(stack.length >= MAX_STACK_SIZE) throw new JSExn("stack 
overflow");
-            Object[] stack2 = new Object[stack.length * 2];
-            System.arraycopy(stack,0,stack2,0,stack.length);
-            stack = stack2;
-        }       
-        
-        private int previous(int current){
-               for(int i=current-1;i>=0;i--) {
-                       if (stack[i] instanceof Backtraceable) return i;
-               }
-               return -1;
-        }
-        
-        void backtrace(JSExn e) {
-               int i = sp;
-               // DEBUG (MAY add this to JSexn)
-               // e.addBacktrace("(Bytecode " + pc+")");
-                       
-               boolean topcall = true;
-               while( (i=previous(i))!=-1){
-                       Backtraceable cm = (Backtraceable)stack[i];
-                       if (topcall && cm instanceof CallMarker) {
-                               e.addBacktrace(f.sourceName + ":" + f.line[pc]);
-                               topcall = false;
-                       }
-                       e.addBacktrace(cm.traceLine());
-               }
-        }
-        
-        String stackframe(int offset) {
-               int i = sp;
-               boolean topcall = true;
-               while( (i=previous(i))!=-1){
-                       Backtraceable cm = (Backtraceable)stack[i];
-                       if (topcall && cm instanceof CallMarker) {
-                               if(offset==0) return f.sourceName + ":" + 
f.line[pc];
-                               topcall = false;
-                               offset--;
-                       }
-                       if(offset==0) return cm.traceLine();
-                       offset--;
-               }
-               return null;
-        }
-        
-        public String toString(){
-               String r = "";
-               for(int i=0; i<sp; i++){
-                       r+= JSU.toString(stack[i]);
-                       r+=", ";
-               }
-               return r;
-        }
-        /*
-        void backtrace(JSExn e) {
-               for(int i=sp-1;i>=0;i--) {
-                       if (stack[i] instanceof CallMarker) break;
-                if (stack[i] instanceof Backtraceable) {
-                       Backtraceable cm = (Backtraceable)stack[i];
-                    e.addBacktrace(cm.traceLine());
-                }
-            }
-               if(f!=null)
-                       e.addBacktrace(f.sourceName + ":" + f.line[pc]);
-               for(int i=sp-1;i>=0;i--) {
-                if (stack[i] instanceof CallMarker) {
-                       CallMarker cm = (CallMarker)stack[i];
-                    e.addBacktrace(cm.traceLine());
-                }
-            }
-        }*/
-    }
-
-       public void enterNonJSCall(Backtraceable call) throws JSExn{
-               stack.push(call);
-       }
-
-       public void exitNonJSCall(){
-               stack.pop();
-       }
-       
-       
-    static private JS add(JS left, JS right) throws JSExn{
-        JSNumber leftn = JSU.expectNumber(left);
-        JSNumber rightn = JSU.expectNumber(right);
-        int kind = JSNumber.dominant(leftn, rightn);
-        if(kind == JSNumber.N_INT32){
-            long r = (long)leftn.toInt32()+(long)rightn.toInt32();
-            return JSU.N(r);
-        }else if(kind == JSNumber.N_RATIONAL){
-            return JSU.N(leftn.toRational().add(rightn.toRational()));
-        }else{
-            return JSU.N(leftn.toDouble()+rightn.toDouble());
-        }
-    }
-
-    static private JS subtract(JS left, JS right) throws JSExn{
-        JSNumber leftn = JSU.expectNumber(left);
-        JSNumber rightn = JSU.expectNumber(right);
-        int kind = JSNumber.dominant(leftn, rightn);
-        if(kind == JSNumber.N_INT32){
-            long r = (long)leftn.toInt32()-(long)rightn.toInt32();
-            return JSU.N(r);
-        }else if(kind == JSNumber.N_RATIONAL){
-            return JSU.N(leftn.toRational().subtract(rightn.toRational()));
-        }else{
-            return JSU.N(leftn.toDouble()-rightn.toDouble());
-        }
-    }
-
-    static private JS multiply(JS left, JS right) throws JSExn{
-        JSNumber leftn = JSU.expectNumber(left);
-        JSNumber rightn = JSU.expectNumber(right);
-        int kind = JSNumber.dominant(leftn, rightn);
-        if(kind == JSNumber.N_INT32){
-            long r = (long)leftn.toInt32()*(long)rightn.toInt32();
-            return JSU.N(r);
-        }else if(kind == JSNumber.N_RATIONAL){
-            return JSU.N(leftn.toRational().multiply(rightn.toRational()));
-        }else{
-            return JSU.N(leftn.toDouble()*rightn.toDouble());
-        }
-    }
-    
-    static private JS divide(JS left, JS right) throws JSExn{
-        JSNumber leftn = JSU.expectNumber(left);
-        JSNumber rightn = JSU.expectNumber(right);
-        int kind = JSNumber.dominant(leftn, rightn);
-        if(kind == JSNumber.N_RATIONAL){
-            return JSU.N(leftn.toRational().divide(rightn.toRational()));
-        }else{
-            return JSU.N(leftn.toDouble()/rightn.toDouble());
-        }
-    }   
-    
-    static private JS modulo(JS left, JS right) throws JSExn{
-        JSNumber leftn = JSU.expectNumber(left);
-        JSNumber rightn = JSU.expectNumber(right);
-        int kind = JSNumber.dominant(leftn, rightn);
-        if(kind == JSNumber.N_INT32){
-            int r = leftn.toInt32()%rightn.toInt32();
-            return JSU.N(r);
-        }else if(kind == JSNumber.N_RATIONAL){
-            return JSU.N(leftn.toRational().mod(rightn.toRational()));
-        }else{
-            return JSU.N(leftn.toDouble()%rightn.toDouble());
-        }
-    }    
-    
-    public String toString(){
-       return getWhere();
-    }
-}
-
-/* FOOTNOTES
- * 1. Automatic cascading has been ripped out. Fundamentally unclean, and only
- * really there as syntactic sugar, even dubious on that level.
- * 
- * (Automatic cascading is a mechanism for making traps listener like... i.e.
- * though do not effect the put operation, they just do something when one 
happens.
- * In practise it makes implementation harder, the resulting JS code becomes 
less
- * clear, and the same effect can be had in JS with the addition of a 
'cascade'.)
- * 
- * 
- * 2. 'pause on cascade' is a mechanism with which at the
- * final cascade (i.e. no more traps to be fired) the interpreter gives up 
control 
- * (returns out) to let the original calling code decide what to do. 
- * 
- * This is necessary to allow (without hacks)
- * a) traps in the get(JS) and put(JS,JS) methods as this would cause an 
infinite loop
- * as they call themselves on the final cascade. 
- * b) speed, as it allows us to store some properties as class fields and not 
force us
- * to go through get/put to modify them. 
- * c) ignoring of modified values/not putting a value at all (i.e. its just a 
virtual
- * property that traps can 'listen' to). 
- * (use cases are in org.vexi.core.Box.jpp) 
- */
-

Added: branches/vexi3/org.vexi-library.js/src/main/jpp/org/ibex/js/JSArgs.jpp
===================================================================
--- branches/vexi3/org.vexi-library.js/src/main/jpp/org/ibex/js/JSArgs.jpp      
                        (rev 0)
+++ branches/vexi3/org.vexi-library.js/src/main/jpp/org/ibex/js/JSArgs.jpp      
2015-02-16 01:01:31 UTC (rev 4763)
@@ -0,0 +1,26 @@
+package org.ibex.js;
+
+class JSArgs extends JS.Immutable implements JSArrayLike{
+    private final JS[] args;
+    private final JS callee;
+    private final JS this_;
+    
+    public JSArgs(JS[] args, JS callee, JS this_) { this.args = args; 
this.callee = callee; this.this_ = this_; }
+    
+    public JS getElement(int i) throws JSExn {  return i>=args.length ? null : 
args[i]; }
+    public JS get(JS key) throws JSExn {
+        if(JSU.isInt(key)) {
+            int i = JSU.toInt32(key);
+            return getElement(i);
+        }
+        //#switch(JSU.toString(key))
+       case "callee": return callee;
+        case "length": return JSU.N(args.length);
+        case "this": return this_;
+        //#end
+        return super.get(key);
+    }
+    public JS.Keys keys() throws JSExn { return new JSArrayLike.Keys(this);}
+       public JS[] toArray() { return args;}
+       public int size(){ return args.length; }
+}

Added: 
branches/vexi3/org.vexi-library.js/src/main/jpp/org/ibex/js/JSArgsTrap.jpp
===================================================================
--- branches/vexi3/org.vexi-library.js/src/main/jpp/org/ibex/js/JSArgsTrap.jpp  
                        (rev 0)
+++ branches/vexi3/org.vexi-library.js/src/main/jpp/org/ibex/js/JSArgsTrap.jpp  
2015-02-16 01:01:31 UTC (rev 4763)
@@ -0,0 +1,28 @@
+package org.ibex.js;
+
+class JSArgsTrap extends JS.Immutable {
+    private Trap t;
+    private JS val;
+    JS trapname;
+    public JSArgsTrap(Trap t, JS val, JS trapname) { 
+       this.t = t; this.trapname = trapname; this.val = val; 
+    }
+    public JS get(JS key) throws JSExn {
+        if(JSU.isInt(key) && JSU.toInt(key) == 0) return val;
+        //#switch(JSU.toString(key))
+        case "trapee": return t.target();
+        case "callee": return t.function();
+        case "trapname": return trapname;
+        case "length": return t.isWriteTrap() ? NC_1 : NC_0;
+        //#end
+        return super.get(key);
+    }
+    public void put(JS key, JS val) throws JSExn {
+       String prop = JSU.toString(key);
+       if(prop.equals("trapname")){
+               trapname = val;
+       }else
+               super.put(key, val);
+    }
+    
+}
\ No newline at end of file

Modified: branches/vexi3/org.vexi-library.js/src/main/jpp/org/ibex/js/Scope.jpp
===================================================================
--- branches/vexi3/org.vexi-library.js/src/main/jpp/org/ibex/js/Scope.jpp       
2015-02-14 21:59:36 UTC (rev 4762)
+++ branches/vexi3/org.vexi-library.js/src/main/jpp/org/ibex/js/Scope.jpp       
2015-02-16 01:01:31 UTC (rev 4763)
@@ -5,7 +5,7 @@
 package org.ibex.js; 
 
 import org.ibex.js.Interpreter.JSArgs;
-import org.ibex.js.Interpreter.TrapArgs;
+import org.ibex.js.Interpreter.JSArgsTrap;
 import org.ibex.js.parse.Parser;
 
 /** Implementation of a JavaScript Scope */
@@ -58,7 +58,7 @@
                        // in these scopes.
                        if (i==base) {
                                if (o instanceof JSArgs)mode = MODE_FUNC;
-                               else if(o instanceof TrapArgs)mode = MODE_TRAP;
+                               else if(o instanceof JSArgsTrap)mode = 
MODE_TRAP;
                        }
                }
             

Added: branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/JSUX.java
===================================================================
--- branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/JSUX.java      
                        (rev 0)
+++ branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/JSUX.java      
2015-02-16 01:01:31 UTC (rev 4763)
@@ -0,0 +1,135 @@
+package org.ibex.js;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeSet;
+
+import org.ibex.js.JS.Enumeration;
+
+// TODO merge most of this with js project somehow
+public class JSUX {
+       static public boolean isDouble(JS js){ return js instanceof JSNumber.D; 
}
+       static public boolean isInt(JS js){ return js instanceof JSNumber.I; }
+       static public boolean isBool(JS js){ return js instanceof JSBoolean; }
+       
+       static public String messageWithTrace(JSExn e){
+               StringWriter sw = new StringWriter();
+               e.printStackTrace(new PrintWriter(sw));
+               return sw.toString();
+       }
+       
+       static public String backtrace(JSExn e) {
+               StringBuilder r = new StringBuilder();
+               String msg = e.asObject().getMessage();
+               if(msg!=null)
+                       r.append(msg+"\n");
+        for (int i=0; i < e.backtrace.size(); i++){
+            r.append("    at " + (String) e.backtrace.get(i));
+            r.append("\n");
+        }
+               return r.toString();
+       }
+       
+       static public String backtrace(Interpreter i) {
+               JSExn e = new JSExn("");
+               e.fillIfEmpty(i);
+               return backtrace(e);
+       }
+       
+    static public Object jsToDynamic(JS js) throws JSExn{
+        // VERIFY - can js send every type even if the value
+       // doesn't put it in that types range (double, but no
+       // decimal, long with a small value ...)
+       if(js == null) return null;
+       if(js instanceof JSNumber){
+               return JSU.toNumber((JSNumber)js);
+       }
+               if(js instanceof JSBoolean)
+                       return Boolean.valueOf(JSU.toBoolean(js));
+        if(js instanceof JSArrayLike){
+               JSArrayLike js_ = (JSArrayLike)js;
+            List r = new ArrayList(js_.size());
+            for(int i=0; i<js_.size(); i++){
+                r.add(jsToDynamic(js_.get(JSU.N(i))));
+            }
+            return r;
+        }
+        if(js instanceof JS.Obj){
+            Map r = new HashMap();
+            Enumeration I = js.keys().iterator();
+            while(I.hasNext()){
+                JS keyJS = I.next();
+                String key = JSU.toString(keyJS);
+                r.put(key, jsToDynamic(js.get(keyJS)));
+            }
+            return r;
+        }
+        return JSU.toString(js);
+        //throw new JSExn("Cannot convert request: " + js);
+    }
+       
+       // TBD - remerge this with the JSProxy version
+    static public JS dynamicToJs(Object o) throws JSExn{
+        if(o==null) return null;
+       if(o instanceof Object[]){
+            Object[] o_ =(Object[])o;
+            JSArray r = new JSArray(o_.length);
+            for(int i=0; i<o_.length; i++){
+                r.add(dynamicToJs(o_[i]));
+            }
+            return r;
+        }
+       if(o instanceof List){
+               List o_ =(List)o;
+            JSArray r = new JSArray(o_.size());
+            for(int i=0; i<o_.size(); i++){
+                r.add(dynamicToJs(o_.get(i)));
+            }
+            return r;
+        }
+        if(o instanceof Map){
+            Map o_ = (Map)o;
+            JS r =  new JS.Obj();
+            Iterator I = o_.keySet().iterator();
+            while(I.hasNext()){
+                Object k = I.next();
+                Object v = o_.get(k);
+                r.put(dynamicToJs(k), dynamicToJs(v));
+            }
+            return r;
+        }
+        if(o instanceof Number){
+            return JSU.N((Number)o);
+        }
+        if(o instanceof Boolean){
+               return JSU.B(((Boolean)o).booleanValue());
+        }
+        if(o instanceof String){
+            return JSU.S((String) o);
+        }
+        
+        throw new JSExn("Cannot convert response: " + o);   
+    }
+    
+    
+    static public String format(JSFunction f){
+       return f.f.sourceName+":"+f.f.firstLine;
+    }
+    
+    static public Collection<String> listMethods(JS obj) throws JSExn{
+               TreeSet<String> methods = new TreeSet();
+               for(Object key: obj.keySet()){
+                       JS value = obj.get((JS)key);
+                       if(value instanceof JSFunction){
+                               methods.add(key.toString());
+                       }                               
+               }
+               return methods;
+    }
+}


Property changes on: 
branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/JSUX.java
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.


------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=190641631&iu=/4140/ostg.clktrk
_______________________________________________
Vexi-svn mailing list
Vexi-svn@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/vexi-svn

Reply via email to