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
[email protected]
https://lists.sourceforge.net/lists/listinfo/vexi-svn