--- /dev/null
+package org.ibex.js;
+
+abstract class JSNumber extends JSPrimitive {
+ boolean jsequals(JS o) {
+ if(o == this) return true;
+ if(o instanceof JSNumber) {
+ JSNumber n = (JSNumber) o;
+ if(this instanceof D || n instanceof D) return n.toDouble() == toDouble();
+ return n.toLong() == toLong();
+ } else if(o instanceof JSString) {
+ String s = ((JSString)o).s.trim();
+ try {
+ if(this instanceof D || s.indexOf('.') != -1) return Double.parseDouble(s) == toDouble();
+ return Long.parseLong(s) == toLong();
+ } catch(NumberFormatException e) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ // FEATURE: Better hash function? (if d != (int) d then do something double specific)
+ public int hashCode() { return toInt(); }
+
+ abstract int toInt();
+ long toLong() { return toInt(); }
+ boolean toBoolean() { return toInt() != 0; }
+ double toDouble() { return toLong(); }
+ float toFloat() { return (float) toDouble(); }
+
+ final static class I extends JSNumber {
+ final int i;
+ I(int i) { this.i = i; }
+ int toInt() { return i; }
+ public String coerceToString() { return Integer.toString(i); }
+ }
+
+ final static class L extends JSNumber {
+ private final long l;
+ L(long l) { this.l = l; }
+ int toInt() { return (int) l; }
+ long toLong() { return l; }
+ public String coerceToString() { return Long.toString(l); }
+ }
+
+ final static class D extends JSNumber {
+ private final double d;
+ D(double d) { this.d = d; }
+ int toInt() { return (int) d; }
+ long toLong() { return (long) d; }
+ double toDouble() { return d; }
+ boolean toBoolean() { return d == d && d != 0.0; }
+ public String coerceToString() { return d == (long) d ? Long.toString((long)d) : Double.toString(d); }
+ }
+
+ final static class B extends JSNumber {
+ private final boolean b;
+ B(boolean b) { this.b = b; }
+ int toInt() { return b ? 1 : 0; }
+ public String coerceToString() { return b ? "true" : "false"; }
+ }
+}
--- /dev/null
+package org.ibex.js;
+
+class JSPrimitive extends JS {
+ public JS callMethod(JS method, JS arg0, JS arg1, JS arg2, JS[] rest, int alength) throws JSExn {
+ //#switch(JS.toString(method))
+ case "toFixed": throw new JSExn("toFixed() not implemented");
+ case "toExponential": throw new JSExn("toExponential() not implemented");
+ case "toPrecision": throw new JSExn("toPrecision() not implemented");
+ case "toString": return this instanceof JSString ? this : JS.S(JS.toString(this));
+ //#end
+
+ String s = coerceToString();
+ int slength = s.length();
+
+ //#switch(JS.toString(method))
+ case "substring": {
+ int a = alength >= 1 ? JS.toInt(arg0) : 0;
+ int b = alength >= 2 ? JS.toInt(arg1) : slength;
+ if (a > slength) a = slength;
+ if (b > slength) b = slength;
+ if (a < 0) a = 0;
+ if (b < 0) b = 0;
+ if (a > b) { int tmp = a; a = b; b = tmp; }
+ return JS.S(s.substring(a,b));
+ }
+ case "substr": {
+ int start = alength >= 1 ? JS.toInt(arg0) : 0;
+ int len = alength >= 2 ? JS.toInt(arg1) : Integer.MAX_VALUE;
+ if (start < 0) start = slength + start;
+ if (start < 0) start = 0;
+ if (len < 0) len = 0;
+ if (len > slength - start) len = slength - start;
+ if (len <= 0) return JS.S("");
+ return JS.S(s.substring(start,start+len));
+ }
+ case "charAt": {
+ int p = alength >= 1 ? JS.toInt(arg0) : 0;
+ if (p < 0 || p >= slength) return JS.S("");
+ return JS.S(s.substring(p,p+1));
+ }
+ case "charCodeAt": {
+ int p = alength >= 1 ? JS.toInt(arg0) : 0;
+ if (p < 0 || p >= slength) return JS.N(Double.NaN);
+ return JS.N(s.charAt(p));
+ }
+ case "concat": {
+ StringBuffer sb = new StringBuffer(slength*2).append(s);
+ for(int i=0;i<alength;i++) sb.append(i==0?arg0:i==1?arg1:i==2?arg2:rest[i-3]);
+ return JS.S(sb.toString());
+ }
+ case "indexOf": {
+ String search = alength >= 1 ? JS.toString(arg0) : "null";
+ int start = alength >= 2 ? JS.toInt(arg1) : 0;
+ // Java's indexOf handles an out of bounds start index, it'll return -1
+ return JS.N(s.indexOf(search,start));
+ }
+ case "lastIndexOf": {
+ String search = alength >= 1 ? JS.toString(arg0) : "null";
+ int start = alength >= 2 ? JS.toInt(arg1) : 0;
+ // Java's indexOf handles an out of bounds start index, it'll return -1
+ return JS.N(s.lastIndexOf(search,start));
+ }
+ case "match": return JSRegexp.stringMatch(this,arg0);
+ case "replace": return JSRegexp.stringReplace(this,arg0,arg1);
+ case "search": return JSRegexp.stringSearch(this,arg0);
+ case "split": return JSRegexp.stringSplit(this,arg0,arg1,alength);
+ case "toLowerCase": return JS.S(s.toLowerCase());
+ case "toUpperCase": return JS.S(s.toUpperCase());
+ case "slice": {
+ int a = alength >= 1 ? JS.toInt(arg0) : 0;
+ int b = alength >= 2 ? JS.toInt(arg1) : slength;
+ if (a < 0) a = slength + a;
+ if (b < 0) b = slength + b;
+ if (a < 0) a = 0;
+ if (b < 0) b = 0;
+ if (a > slength) a = slength;
+ if (b > slength) b = slength;
+ if (a > b) return JS.S("");
+ return JS.S(s.substring(a,b));
+ }
+ //#end
+ return super.callMethod(method,arg0,arg1,arg2,rest,alength);
+ }
+
+ public JS get(JS key) throws JSExn {
+ //#switch(JS.toString(key))
+ case "length": return JS.N(JS.toString(this).length());
+ case "substring": return METHOD;
+ case "charAt": return METHOD;
+ case "charCodeAt": return METHOD;
+ case "concat": return METHOD;
+ case "indexOf": return METHOD;
+ case "lastIndexOf": return METHOD;
+ case "match": return METHOD;
+ case "replace": return METHOD;
+ case "search": return METHOD;
+ case "slice": return METHOD;
+ case "split": return METHOD;
+ case "toLowerCase": return METHOD;
+ case "toUpperCase": return METHOD;
+ case "toString": return METHOD;
+ case "substr": return METHOD;
+ case "toPrecision": return METHOD;
+ case "toExponential": return METHOD;
+ case "toFixed": return METHOD;
+ //#end
+ return super.get(key);
+ }
+}
--- /dev/null
+package org.ibex.js;
+
+import org.ibex.util.*;
+
+class JSString extends JSPrimitive {
+ final String s;
+ public JSString(String s) { this.s = s; }
+ public int hashCode() { return s.hashCode(); }
+
+ public boolean jsequals(JS o) {
+ if(o == this) return true;
+ if(o instanceof JSString) {
+ return ((JSString)o).s.equals(s);
+ } else if(o instanceof JSNumber) {
+ return o.jsequals(this);
+ } else {
+ return false;
+ }
+ }
+
+ private final static Hash internHash = new Hash();
+ static synchronized JS intern(String s) {
+ synchronized(internHash) {
+ JS js = (JS)internHash.get(s);
+ if(js == null) internHash.put(s,js = new Intern(s));
+ return js;
+ }
+ }
+ static class Intern extends JSString {
+ public Intern(String s) { super(s); }
+ protected void finalize() { synchronized(internHash) { internHash.put(s,null); } }
+ }
+
+ String coerceToString() { return s; }
+}
--- /dev/null
+package org.ibex.js;
+
+import java.io.*;
+
+public class Test extends JS {
+ static JS.UnpauseCallback up = null;
+ static String action;
+
+ public static void main(String[] args) throws Exception {
+ if(args.length == 0) { System.err.println("Usage Test filename"); System.exit(1); }
+ JS f = JS.fromReader(args[0],1,new FileReader(args[0]));
+ System.out.println(((JSFunction)f).dump());
+ JS s = new JS.O();
+ s.put(JS.S("sys"),new Test());
+ f = JS.cloneWithNewGlobalScope(f,s);
+ //JS ret = f.call(null,null,null,null,0);
+ Interpreter i = new Interpreter((JSFunction)f, true, new Interpreter.JSArgs(f));
+ JS ret = i.resume();
+ while(up != null) {
+ JS.UnpauseCallback up = Test.up; Test.up = null;
+ if("throw".equals(action)) ret = up.unpause(new JSExn("this was thrown to a paused context"));
+ else if("bgget".equals(action)) ret = up.unpause(JS.S("I'm returning this from a get request"));
+ else {
+ System.out.println("got a background put " + action);
+ ret = up.unpause();
+ }
+ }
+ System.out.println("Script returned: " + JS.toString(ret));
+ }
+
+ public JS get(JS key) throws JSExn {
+ if(!JS.isString(key)) return null;
+ if("print".equals(JS.toString(key))) return METHOD;
+ if("clone".equals(JS.toString(key))) return METHOD;
+ if("firethis".equals(JS.toString(key))) return METHOD;
+ if("bgget".equals(JS.toString(key))) {
+ action = "bgget";
+ try {
+ up = JS.pause();
+ } catch(NotPauseableException e) {
+ throw new Error("should never happen");
+ }
+ return null;
+ }
+ return super.get(key);
+ }
+
+ public void put(JS key, JS val) throws JSExn {
+ if("bgput".equals(JS.toString(key))) {
+ action = JS.toString(val);
+ try {
+ up = JS.pause();
+ } catch(NotPauseableException e) {
+ throw new Error("should never happen");
+ }
+ return;
+ }
+ if("exit".equals(JS.toString(key))) {
+ System.exit(JS.toInt(val));
+ return;
+ }
+ super.put(key,val);
+ }
+
+ public JS callMethod(JS method, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
+ if(!JS.isString(method)) return null;
+ if("print".equals(JS.toString(method))) {
+ System.out.println(JS.debugToString(a0));
+ return null;
+ }
+ if("clone".equals(JS.toString(method))) return a0 == null ? null : a0.jsclone();
+ if("firethis".equals(JS.toString(method))) {
+ String action = JS.toString(a0);
+ JS target = a1;
+ JS key = a2;
+ if(action.equals("get")) return a1.getAndTriggerTraps(key);
+ else if(action.equals("put")) a1.putAndTriggerTraps(key,JS.S("some value"));
+ else if(action.equals("trigger")) return target.justTriggerTraps(key,JS.S("some trigger value"));
+ return null;
+ }
+ return null;
+ }
+}