ByteArray(byte[] bytes, String cacheKey) { this.bytes = bytes; this.cacheKey = cacheKey; }
public String getCacheKey() throws NotCacheableException { return cacheKey; }
public InputStream getInputStream(String path) throws IOException {
- if (!"".equals(path)) throw new JS.Exn("can't get subresources of a byte[] resource");
+ if (!"".equals(path)) throw new JSExn("can't get subresources of a byte[] resource");
return new ByteArrayInputStream(bytes);
}
}
ZipInputStream zis = new ZipInputStream(pis);
ZipEntry ze = zis.getNextEntry();
while(ze != null && !ze.getName().equals(path)) ze = zis.getNextEntry();
- if (ze == null) throw new JS.Exn("requested file (" + path + ") not found in archive");
+ if (ze == null) throw new JSExn("requested file (" + path + ") not found in archive");
return new KnownLength.KnownLengthInputStream(zis, (int)ze.getSize());
}
}
}
/** Appends the SOAP representation of <code>o</code> to <code>sb</code> */
- void appendObject(String name, Object o, StringBuffer sb) throws JS.Exn {
+ void appendObject(String name, Object o, StringBuffer sb) throws JSExn {
if (o instanceof Number) {
if ((double)((Number)o).intValue() == ((Number)o).doubleValue()) {
sb.append(" <" + name + " xsi:type=\"xsd:int\">");
} catch (IOException e) {
if (Log.on) Log.log(this, "caught IOException while attempting to send a ByteStream via SOAP");
if (Log.on) Log.log(this, e);
- throw new JS.Exn("caught IOException while attempting to send a ByteStream via SOAP");
+ throw new JSExn("caught IOException while attempting to send a ByteStream via SOAP");
}
} else if (o instanceof String) {
}
}
- protected String send(JSArray args, HTTP http) throws JS.Exn, IOException {
+ protected String send(JSArray args, HTTP http) throws JSExn, IOException {
// build up the request
StringBuffer content = new StringBuffer();
content.append("SOAPAction: " + action + "\r\n\r\n");
final Box who = Box.whoIs(root, mousex, mousey);
Scheduler.add(new Scheduler.Task() { public void perform() {
Platform.clipboardReadEnabled = true;
- root.putAndTriggerJSTraps("Press3", Boolean.TRUE);
+ root.putAndTriggerTraps("Press3", Boolean.TRUE);
Platform.clipboardReadEnabled = false;
}});
}
Box b = (Box)keywatchers.elementAt(i);
for(Box cur = b; cur != null; cur = cur.parent)
if (!cur.test(cur.VISIBLE)) continue outer;
- b.putAndTriggerJSTraps("KeyPressed", key);
+ b.putAndTriggerTraps("KeyPressed", key);
}
Platform.clipboardReadEnabled = false;
}
Box b = (Box)keywatchers.elementAt(i);
for(Box cur = b; cur != null; cur = cur.parent)
if (!cur.test(cur.VISIBLE)) continue outer;
- b.putAndTriggerJSTraps("KeyReleased", key);
+ b.putAndTriggerTraps("KeyReleased", key);
}
}});
}
// Root gets motion events outside itself (if trapped, of course)
if (!root.inside(oldmousex, oldmousey) && !root.inside(mousex, mousey) && (button1 || button2 || button3))
- root.putAndTriggerJSTraps("Move", Boolean.TRUE);
+ root.putAndTriggerTraps("Move", Boolean.TRUE);
root.Move(oldmousex, oldmousey, mousex, mousey);
if (!cursor.equals(oldcursor)) syncCursor();
Scheduler.add(new Scheduler.Task() { public void perform() {
root.x = x;
root.y = y;
- root.putAndTriggerJSTraps("PosChange", Boolean.TRUE);
+ root.putAndTriggerTraps("PosChange", Boolean.TRUE);
}});
}
Scheduler.add(this);
}
- public void perform() { boxContainingMouse.putAndTriggerJSTraps(name, value); }
+ public void perform() { boxContainingMouse.putAndTriggerTraps(name, value); }
public String toString() { return "SimpleMessage [name=" + name + ", value=" + value + "]"; }
}
if (staticscript == null) return staticJSScope;
JSFunction temp = staticscript;
staticscript = null;
- temp.cloneWithNewParentJSScope(staticJSScope).call(null, null, null, null, 0);
+ temp.cloneWithNewParentScope(staticJSScope).call(null, null, null, null, 0);
return staticJSScope;
}
for (int i=0; children != null && i<children.size(); i++) {
Box kid = new Box();
((Template)children.elementAt(i)).apply(kid, xwt, pis);
- b.putAndTriggerJSTraps(JS.N(b.numchildren), kid);
+ b.putAndTriggerTraps(JS.N(b.numchildren), kid);
}
- if (script != null) script.cloneWithNewParentJSScope(pis).call(null, null, null, null, 0);
+ if (script != null) script.cloneWithNewParentScope(pis).call(null, null, null, null, 0);
for(int i=0; keys != null && i<keys.length; i++)
- if (vals[i] instanceof String && ((String)vals[i]).charAt(0) == '$') b.putAndTriggerJSTraps(keys[i], pis.get(vals[i]));
- else if ("image".equals(keys[i])) b.putAndTriggerJSTraps("image", resolveStringToResource((String)vals[i], xwt, true));
- else if (keys[i] != null) b.putAndTriggerJSTraps(keys[i], vals[i]);
+ if (vals[i] instanceof String && ((String)vals[i]).charAt(0) == '$') b.putAndTriggerTraps(keys[i], pis.get(vals[i]));
+ else if ("image".equals(keys[i])) b.putAndTriggerTraps("image", resolveStringToResource((String)vals[i], xwt, true));
+ else if (keys[i] != null) b.putAndTriggerTraps(keys[i], vals[i]);
}
Hash h = new Hash(c.len * 2, 3);
for(int i=0; i<c.len; i++) {
if (c.keys[i] == null) continue;
- if (c.keys[i].endsWith(":image")) {
- String uri = (String)c.urimap.get(c.keys[i].substring(0, c.keys[i].indexOf(':')));
- c.keys[i] = c.keys[i].substring(c.keys[i].lastIndexOf(':') + 1);
- c.vals[i] = uri + "." + c.vals[i];
- }
- if ((c.keys[i].equals("preapply") || c.keys[i].endsWith(":preapply")) && c.localName.equals("template")) {
- String uri = "";
- if (c.keys[i].endsWith(":preapply")) {
- uri = "." + c.urimap.get(c.keys[i].substring(0, c.keys[i].indexOf(':')));
- c.keys[i] = c.keys[i].substring(c.keys[i].lastIndexOf(':') + 1);
- }
+ if (c.keys[i].equals("fill") || c.keys[i].equals("font")) c.vals[i] = c.uris[i] + "." + c.vals[i];
+ if (c.keys[i].equals("preapply")) {
+ String uri = c.uris[i];
StringTokenizer tok = new StringTokenizer(c.vals[i].toString(), " ");
while(tok.hasMoreTokens()) t.preapply.addElement(uri + tok.nextToken());
c.keys[i] = c.keys[c.keys.length - 1];
try {
String contentString = t.content.toString();
if (contentString.trim().length() > 0)
- thisscript = JS.parse(t.fileName + (isstatic ? "._" : ""), t.content_start, new StringReader(contentString));
+ thisscript = JSFunction.fromReader(t.fileName + (isstatic ? "._" : ""),
+ t.content_start,
+ new StringReader(contentString));
} catch (IOException ioe) {
if (Log.on) Log.log(this, " ERROR: " + ioe.getMessage());
thisscript = null;
declare("$" + key);
put("$" + key, target);
}
- public PerInstantiationJSScope(JSScope parentJSScope, XWT xwt, PerInstantiationJSScope parentBoxPis, JSScope myStatic) {
- super(parentJSScope);
+ public PerInstantiationJSScope(JSScope parentScope, XWT xwt, PerInstantiationJSScope parentBoxPis, JSScope myStatic) {
+ super(parentScope);
this.parentBoxPis = parentBoxPis;
this.xwt = xwt;
this.myStatic = myStatic;
* convert.
* </ol>
*/
-class XMLRPC extends JSCallable {
+class XMLRPC extends JS {
/** the url to connect to */
protected String url = null;
// Methods to make outbound XML-RPC request ///////////////////////////////////////////////////
/** Appends the XML-RPC representation of <code>o</code> to <code>sb</code> */
- void appendObject(Object o, StringBuffer sb) throws JS.Exn {
+ void appendObject(Object o, StringBuffer sb) throws JSExn {
if (o == null) {
- throw new JS.Exn("attempted to send a null value via XML-RPC");
+ throw new JSExn("attempted to send a null value via XML-RPC");
} else if (o instanceof Number) {
if ((double)((Number)o).intValue() == ((Number)o).doubleValue()) {
} catch (IOException e) {
if (Log.on) Log.log(this, "caught IOException while attempting to send a ByteStream via XML-RPC");
if (Log.on) Log.log(this, e);
- throw new JS.Exn("caught IOException while attempting to send a ByteStream via XML-RPC");
+ throw new JSExn("caught IOException while attempting to send a ByteStream via XML-RPC");
}
} else if (o instanceof String) {
sb.append("</dateTime.iso8601></value>\n");
} else if (o instanceof JSArray) {
- if (tracker.get(o) != null) throw new JS.Exn("attempted to send multi-ref data structure via XML-RPC");
+ if (tracker.get(o) != null) throw new JSExn("attempted to send multi-ref data structure via XML-RPC");
tracker.put(o, Boolean.TRUE);
sb.append(" <value><array><data>\n");
JSArray a = (JSArray)o;
sb.append(" </data></array></value>\n");
} else if (o instanceof JS) {
- if (tracker.get(o) != null) throw new JS.Exn("attempted to send multi-ref data structure via XML-RPC");
+ if (tracker.get(o) != null) throw new JSExn("attempted to send multi-ref data structure via XML-RPC");
tracker.put(o, Boolean.TRUE);
JS j = (JS)o;
sb.append(" <value><struct>\n");
sb.append(" </struct></value>\n");
} else {
- throw new JS.Exn("attempt to send object of type " + o.getClass().getName() + " via XML-RPC");
+ throw new JSExn("attempt to send object of type " + o.getClass().getName() + " via XML-RPC");
}
}
- public Object call_(JSArray args) throws JS.Exn, IOException {
+ public Object call_(JSArray args) throws JSExn, IOException {
if (Log.verbose) Log.log(this, "call to " + url + " : " + methodname);
if (tracker == null) tracker = new Hash();
}
}
- protected String send(JSArray args, HTTP http) throws JS.Exn, IOException {
+ protected String send(JSArray args, HTTP http) throws JSExn, IOException {
StringBuffer content = new StringBuffer();
content.append("\r\n");
content.append("<?xml version=\"1.0\"?>\n");
return content.toString();
}
- protected Object recieve(BufferedReader br) throws JS.Exn, IOException {
+ protected Object recieve(BufferedReader br) throws JSExn, IOException {
// parse XML reply
try {
new Helper().parse(br);
} catch (XML.XMLException e) {
if (Log.on) Log.log(this, "reply from server was not well-formed XML: " + e);
- throw new JS.Exn("reply from server was not well-formed XML: " + e);
+ throw new JSExn("reply from server was not well-formed XML: " + e);
}
- if (fault) throw new JS.Exn(objects.elementAt(0));
+ if (fault) throw new JSExn(objects.elementAt(0));
if (objects.size() == 0) return null;
return objects.elementAt(0);
}
- public final Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JS.Exn {
+ public final Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
JSArray args = new JSArray();
for(int i=0; i<nargs; i++) args.addElement(i==0?a0:i==1?a1:i==2?a2:rest[i-3]);
return call(args);
}
- public final Object call(final JSArray args) throws JS.Exn {
- final Callback callback = JSContext.pause();
- new java.lang.Thread() {
- public void run() {
- try {
- final Object ret = call_(args);
- Scheduler.add(new Scheduler.Task() { public void perform() { callback.call(ret); } });
- } catch (IOException se) {
- if (Log.on) Log.log(this, se);
- throw new JS.Exn("socket exception: " + se);
- }
- } }.start();
- return null;
+ public final Object call(final JSArray args) throws JSExn {
+ try {
+ final JS.UnpauseCallback callback = JS.pause();
+ new java.lang.Thread() {
+ public void run() {
+ try {
+ final Object ret = call_(args);
+ Scheduler.add(new Scheduler.Task() {
+ public void perform() {
+ try {
+ callback.unpause(null);
+ } catch (JS.PausedException pe) {
+ // okay
+ }
+ }
+ });
+ } catch (IOException se) {
+ if (Log.on) Log.log(this, se);
+ throw new JSExn("socket exception: " + se);
+ }
+ } }.start();
+ return null;
+ } catch (NotPauseableException npe) {
+ throw new JSExn("cannot invoke an XML-RPC call in the foreground thread");
+ }
}
/** When you get a property from an XMLRPC, it just returns another XMLRPC with the property name tacked onto methodname. */
import org.bouncycastle.util.encoders.Base64;
/** Singleton class that provides all functionality in the xwt.* namespace */
-public final class XWT extends JSCallable {
+public final class XWT extends JS {
public final Res rr;
public XWT(Res rr) { this.rr = rr; }
/** lets us put multi-level get/put/call keys all in the same method */
- private class Sub extends JSCallable {
+ private class Sub extends JS {
String key;
Sub(String key) { this.key = key; }
public String toString() { return "XWTSUB " + key; }
if (Surface.button1 && !Surface.button2 && !Surface.button3) return new Integer(1);
else if (!Surface.button1 && Surface.button2 && !Surface.button3) return new Integer(2);
else if (!Surface.button1 && !Surface.button2 && Surface.button3) return new Integer(3);
- else return new Integer(0);
+ else return ZERO;
case "undocumented": return new Sub("undocumented");
case "undocumented.internal": return new Sub("undocumented.internal");
case "thread.yield": return METHOD;
public void put(Object name, final Object value) {
//#switch(name)
case "thread":
- Scheduler.add(new Scheduler.Task() { public void perform() { JSContext.invokePauseable((JSFunction)value); } });
+ Scheduler.add(new Scheduler.Task() {
+ public void perform() {
+ try {
+ JS.invokePauseable((JSFunction)value);
+ } catch (JS.PausedException pe) {
+ // okay; wait for ourselves to be re-enqueued
+ }
+ }
+ });
case "ui.clipboard": Platform.setClipBoard((String)value);
case "ui.frame": Platform.createSurface((Box)value, true, true);
case "ui.window": Platform.createSurface((Box)value, false, true);
//#end
}
- public Object callMethod(Object name, Object a, Object b, Object c, Object[] rest, int nargs) throws JS.Exn {
+ public Object callMethod(Object name, Object a, Object b, Object c, Object[] rest, int nargs) throws JSExn {
if (name.equals("date")) {
JSArray args = new JSArray();
for(int i=0; i<nargs; i++) args.addElement(i==0?a:i==1?b:i==2?c:rest[i-3]);
} else if (nargs == 3 && name.equals("soap")) {
if (name.equals("soap")) {
return new SOAP((String)a, "", (String)b, (String)c);
+ /* FIXME
} else if (name.equals("graft")) {
- if (a instanceof Box) throw new JS.Exn("can't graft onto Boxes (yet)");
- if (a instanceof Number) throw new JS.Exn("can't graft onto Numbers (yet)");
- if (a instanceof String) throw new JS.Exn("can't graft onto Strings (yet)");
+ if (a instanceof Box) throw new JSExn("can't graft onto Boxes (yet)");
+ if (a instanceof Number) throw new JSExn("can't graft onto Numbers (yet)");
+ if (a instanceof String) throw new JSExn("can't graft onto Strings (yet)");
if (a instanceof Res) return new Res.Graft((Res)a, b, c);
return new JS.Graft((JS)a, b, c);
+ */
}
} else if (nargs == 1) {
//#switch(name)
case "res.unzip": return new Res.Zip((Res)a);
case "res.uncab": return new Res.Cab((Res)a);
case "res.cache": try { return new Res.CachedRes((Res)a, "resources", true); }
- catch (Res.NotCacheableException e) { throw new JS.Exn("this resource cannot be cached"); }
+ catch (Res.NotCacheableException e) { throw new JSExn("this resource cannot be cached"); }
case "res.url":
String url = (String)a;
if (url.startsWith("http://")) return new Res.HTTP(url);
else if (url.startsWith("https://")) return new Res.HTTP(url);
else if (url.startsWith("data:")) return new Res.ByteArray(Base64.decode(url.substring(5)), null);
else if (url.startsWith("utf8:")) return new Res.ByteArray(url.substring(5).getBytes(), null);
- throw new JS.Exn("invalid resource specifier " + url);
+ throw new JSExn("invalid resource specifier " + url);
case "thread.sleep": sleep(JS.toInt(a)); return null;
case "log.println": Log.logJS(this, a== null ? "**null**" : a.toString()); return null;
case "log.dump": Log.recursiveLog("","",a); return null;
}
public static void sleep(final int i) {
- final Callback callback = JSContext.pause();
- final long currentTime = System.currentTimeMillis();
- new Thread() {
- public void run() {
- try { Thread.sleep(i); } catch (InterruptedException e) { }
- Scheduler.add(new Scheduler.Task() { public void perform() { callback.call(null); } });
- }
- }.start();
+ try {
+ final JS.UnpauseCallback callback = JS.pause();
+ final long currentTime = System.currentTimeMillis();
+ new Thread() {
+ public void run() {
+ try { Thread.sleep(i); } catch (InterruptedException e) { }
+ Scheduler.add(new Scheduler.Task() {
+ public void perform() {
+ try {
+ callback.unpause(null);
+ } catch (JS.PausedException pe) {
+ // okay
+ }
+ }
+ });
+ }
+ }.start();
+ } catch (JS.NotPauseableException npe) {
+ throw new JSExn("you cannot sleep or yield in the foreground thread");
+ }
}
public static final JSMath xwtMath = new JSMath() {
- private JS gs = new JSScope.Global(null);
+ private JS gs = new JSScope.Global();
public String toString() { return "XWTMATH"; }
public Object get(Object key) {
//#switch(key)
};
public static final JS xwtString = new JS() {
- private JS gs = new JSScope.Global(null);
+ private JS gs = new JSScope.Global();
public void put(Object key, Object val) { }
public Object get(Object key) {
//#switch(key)
}
}
public void whitespace(char[] ch, int start, int length) {}
- public JS doParse(InputStream is) throws JS.Exn {
+ public JS doParse(InputStream is) throws JSExn {
try {
BufferedReader r = new BufferedReader(new InputStreamReader(is));
parse(r);
} catch (XML.XMLException e) {
- throw new JS.Exn("error parsing XML: " + e.toString());
+ throw new JSExn("error parsing XML: " + e.toString());
} catch (IOException e) {
if (Log.on) Log.log(this, "IO Exception while reading from file");
if (Log.on) Log.log(this, e);
- throw new JS.Exn("error reading from Resource");
+ throw new JSExn("error reading from Resource");
}
return obStack.size() >= 1 ? (JS)obStack.elementAt(0) : null;
}
/** pop an element; push a JS.JSArray containing the keys of the popped element */
public static final byte PUSHKEYS = -16;
- /** pops [arg+2] elements, pushes the former top element, then pushes back the rest (retaining order) */
+ /** push the top element down so that (arg) elements are on top of it; all other elements retain ordering */
public static final byte SWAP = -17;
- /** execute the ForthBlock pointed to by the literal in a fresh scope with parentJSScope==THIS */
+ /** execute the bytecode block pointed to by the literal in a fresh scope with parentScope==THIS */
public static final byte NEWSCOPE = -18;
- /** execute the ForthBlock pointed to by the literal in a fresh scope with parentJSScope==THIS */
+ /** execute the bytecode block pointed to by the literal in a fresh scope with parentScope==THIS */
public static final byte OLDSCOPE = -19;
/** push a copy of the top stack element */
+++ /dev/null
-// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
-
-package org.xwt.js;
-import org.xwt.util.*;
-
-/**
- * Misc static methods used internally by the JS engine
- */
-class Internal {
- // Package Helper Methods //////////////////////////////////////////////////////////////
-
- private static Object wrapInt(int i) { return new Integer(i); }
- static Object callMethodOnPrimitive(Object o, Object method, JSArray args) {
- int alength = args.length();
- String s;
- if(o instanceof Number) {
- if(method.equals("toFixed")) throw new JS.Exn("toFixed() not implemented");
- if(method.equals("toExponential")) throw new JS.Exn("toExponential() not implemented");
- if(method.equals("toPrecision")) throw new JS.Exn("toPrecision() not implemented");
- if(method.equals("toString")) {
- int radix = alength >= 1 ? JS.toInt(args.elementAt(0)) : 10;
- return Long.toString(((Number)o).longValue(),radix);
- }
- } else if(o instanceof Boolean) {
- // No methods for Booleans
- }
- s = JS.toString(o);
- int slength = s.length();
- if(method.equals("substring")) {
- int a = alength >= 1 ? JS.toInt(args.elementAt(0)) : 0;
- int b = alength >= 2 ? JS.toInt(args.elementAt(1)) : 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 s.substring(a,b);
- }
- if(method.equals("substr")) {
- int start = alength >= 1 ? JS.toInt(args.elementAt(0)) : 0;
- int len = alength >= 2 ? JS.toInt(args.elementAt(1)) : 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 "";
- return s.substring(start,start+len);
- }
- if(method.equals("charAt")) {
- int p = alength >= 1 ? JS.toInt(args.elementAt(0)) : 0;
- if(p < 0 || p >= slength) return "";
- return s.substring(p,p+1);
- }
- if(method.equals("charCodeAt")) {
- int p = alength >= 1 ? JS.toInt(args.elementAt(0)) : 0;
- if(p < 0 || p >= slength) return new Double(Double.NaN);
- return wrapInt(s.charAt(p));
- }
- if(method.equals("concat")) {
- StringBuffer sb = new StringBuffer(slength*2).append(s);
- for(int i=0;i<alength;i++) sb.append(args.elementAt(i));
- return sb.toString();
- }
- if(method.equals("indexOf")) {
- String search = alength >= 1 ? args.elementAt(0).toString() : "null";
- int start = alength >= 2 ? JS.toInt(args.elementAt(1)) : 0;
- // Java's indexOf handles an out of bounds start index, it'll return -1
- return wrapInt(s.indexOf(search,start));
- }
- if(method.equals("lastIndexOf")) {
- String search = alength >= 1 ? args.elementAt(0).toString() : "null";
- int start = alength >= 2 ? JS.toInt(args.elementAt(1)) : 0;
- // Java's indexOf handles an out of bounds start index, it'll return -1
- return wrapInt(s.lastIndexOf(search,start));
- }
- if(method.equals("match")) {
- return JSRegexp.stringMatch(s,args);
- }
- if(method.equals("replace")) {
- return JSRegexp.stringReplace(s,args);
- }
- if(method.equals("search")) {
- return JSRegexp.stringSearch(s,args);
- }
- if(method.equals("slice")) {
- int a = alength >= 1 ? JS.toInt(args.elementAt(0)) : 0;
- int b = alength >= 2 ? JS.toInt(args.elementAt(1)) : 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 "";
- return s.substring(a,b);
- }
- if(method.equals("split")){
- return JSRegexp.stringSplit(s,args);
- }
- if(method.equals("toLowerCase")) return s.toLowerCase();
- if(method.equals("toUpperCase")) return s.toUpperCase();
- if(method.equals("toString")) return s;
- throw new JS.Exn("Attempted to call non-existent method: " + method);
- }
-
- static Object getFromPrimitive(Object o, Object key) {
- boolean returnJSCallable = false;
- if(o instanceof Boolean) {
- // no properties for Booleans
- } else if(o instanceof Number) {
- if(key.equals("toPrecision") || key.equals("toExponential") || key.equals("toFixed"))
- returnJSCallable = true;
- }
- if(!returnJSCallable) {
- // the string stuff applies to everything
- String s = o.toString();
-
- if(key.equals("length")) return wrapInt(s.length());
-
- // this is sort of ugly, but this list should never change
- // These should provide a complete (enough) implementation of the ECMA-262 String object
- if(key.equals("substring") || key.equals("charAt") || key.equals("charCodeAt") || key.equals("concat") ||
- key.equals("indexOf") || key.equals("lastIndexOf") || key.equals("match") || key.equals("replace") ||
- key.equals("seatch") || key.equals("slice") || key.equals("split") || key.equals("toLowerCase") ||
- key.equals("toUpperCase") || key.equals("toString") || key.equals("substr")
- )
- returnJSCallable = true;
- }
- if(returnJSCallable) {
- final Object target = o;
- final String method = key.toString();
- return new JSCallable() {
- public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) {
- JSArray args = new JSArray();
- for(int i=0; i<nargs; i++) args.addElement(i==0?a0:i==1?a1:i==2?a2:rest[i-3]);
- return callMethodOnPrimitive(target,method,args);
- }
- };
- }
- return null;
- }
-
- static class JSCallableStub extends JSCallable {
- private Object method;
- JS obj;
- public JSCallableStub(JS obj, Object method) { this.obj = obj; this.method = method; }
- public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) {
- return ((JSCallable)obj).callMethod(method, a0, a1, a2, rest, nargs);
- }
- }
-}
import java.util.*;
import java.io.*;
+/** Encapsulates a single JS interpreter (ie call stack) */
class Interpreter implements ByteCodes, Tokens {
- static Object eval(final JSContext cx) throws JS.Exn {
- final int initialPauseCount = cx.pausecount;
- OUTER: for(;; cx.pc++) {
+
+ // Thread-Interpreter Mapping /////////////////////////////////////////////////////////////////////////
+
+ static Interpreter current() { return (Interpreter)threadToInterpreter.get(Thread.currentThread()); }
+ private static Hashtable threadToInterpreter = new Hashtable();
+
+
+ // Instance members and methods //////////////////////////////////////////////////////////////////////
+
+ int pausecount; ///< the number of times pause() has been invoked; -1 indicates unpauseable
+ JSFunction f = null; ///< the currently-executing JSFunction
+ JSScope scope; ///< the current top-level scope (LIFO stack via NEWSCOPE/OLDSCOPE)
+ Vec stack = new Vec(); ///< the object stack
+ int pc = 0; ///< the program counter
+
+ Interpreter(JSFunction f, boolean pauseable, JSArray args) {
+ stack.push(new Interpreter.CallMarker(this)); // the "root function returned" marker -- f==null
+ this.f = f;
+ this.pausecount = pauseable ? 0 : -1;
+ this.scope = new JSScope(f.parentScope);
+ stack.push(args);
+ }
+
+ /** this is the only synchronization point we need in order to be threadsafe */
+ synchronized Object resume() {
+ Thread t = Thread.currentThread();
+ Interpreter old = (Interpreter)threadToInterpreter.get(t);
+ threadToInterpreter.put(t, this);
+ try {
+ return run();
+ } finally {
+ if (old == null) threadToInterpreter.remove(t);
+ else threadToInterpreter.put(t, old);
+ }
+ }
+
+ private static JSExn je(String s) { return new JSExn(JS.getSourceName() + ":" + JS.getLine() + " " + s); }
+
+ // FIXME: double check the trap logic
+ private Object run() throws JSExn {
+
+ // if pausecount changes after a get/put/call, we know we've been paused
+ final int initialPauseCount = pausecount;
+
+ OUTER: for(;; pc++) {
try {
- if (cx.f == null || cx.pc >= cx.f.size) return cx.stack.pop();
- int op = cx.f.op[cx.pc];
- Object arg = cx.f.arg[cx.pc];
+ if (f == null) return stack.pop();
+ int op = f.op[pc];
+ Object arg = f.arg[pc];
if(op == FINALLY_DONE) {
- FinallyData fd = (FinallyData) cx.stack.pop();
+ FinallyData fd = (FinallyData) stack.pop();
if(fd == null) continue OUTER; // NOP
op = fd.op;
arg = fd.arg;
}
switch(op) {
- case LITERAL: cx.stack.push(arg); break;
- case OBJECT: cx.stack.push(new JS()); break;
- case ARRAY: cx.stack.push(new JSArray(JS.toNumber(arg).intValue())); break;
- case DECLARE: cx.scope.declare((String)(arg==null ? cx.stack.peek() : arg)); if(arg != null) cx.stack.push(arg); break;
- case TOPSCOPE: cx.stack.push(cx.scope); break;
- case JT: if (JS.toBoolean(cx.stack.pop())) cx.pc += JS.toNumber(arg).intValue() - 1; break;
- case JF: if (!JS.toBoolean(cx.stack.pop())) cx.pc += JS.toNumber(arg).intValue() - 1; break;
- case JMP: cx.pc += JS.toNumber(arg).intValue() - 1; break;
- case POP: cx.stack.pop(); break;
+ case LITERAL: stack.push(arg); break;
+ case OBJECT: stack.push(new JS()); break;
+ case ARRAY: stack.push(new JSArray(JS.toNumber(arg).intValue())); break;
+ case DECLARE: scope.declare((String)(arg==null ? stack.peek() : arg)); if(arg != null) stack.push(arg); break;
+ case TOPSCOPE: stack.push(scope); break;
+ case JT: if (JS.toBoolean(stack.pop())) pc += JS.toNumber(arg).intValue() - 1; break;
+ case JF: if (!JS.toBoolean(stack.pop())) pc += JS.toNumber(arg).intValue() - 1; break;
+ case JMP: pc += JS.toNumber(arg).intValue() - 1; break;
+ case POP: stack.pop(); break;
case SWAP: {
int depth = (arg == null ? 1 : JS.toInt(arg));
- Object save = cx.stack.elementAt(cx.stack.size() - 1);
- for(int i=cx.stack.size() - 1; i > cx.stack.size() - 1 - depth; i--)
- cx.stack.setElementAt(cx.stack.elementAt(i-1), i);
- cx.stack.setElementAt(save, cx.stack.size() - depth - 1);
+ Object save = stack.elementAt(stack.size() - 1);
+ for(int i=stack.size() - 1; i > stack.size() - 1 - depth; i--)
+ stack.setElementAt(stack.elementAt(i-1), i);
+ stack.setElementAt(save, stack.size() - depth - 1);
break; }
- case DUP: cx.stack.push(cx.stack.peek()); break;
- case NEWSCOPE: cx.scope = new JSScope(cx.scope); break;
- case OLDSCOPE: cx.scope = cx.scope.getParentJSScope(); break;
- case ASSERT: if (!JS.toBoolean(cx.stack.pop())) throw je("assertion failed"); break;
- case BITNOT: cx.stack.push(new Long(~JS.toLong(cx.stack.pop()))); break;
- case BANG: cx.stack.push(new Boolean(!JS.toBoolean(cx.stack.pop()))); break;
- case NEWFUNCTION: cx.stack.push(((JSFunction)arg).cloneWithNewParentJSScope(cx.scope)); break;
+ case DUP: stack.push(stack.peek()); break;
+ case NEWSCOPE: scope = new JSScope(scope); break;
+ case OLDSCOPE: scope = scope.getParentScope(); break;
+ case ASSERT: if (!JS.toBoolean(stack.pop())) throw je("assertion failed"); break;
+ case BITNOT: stack.push(JS.N(~JS.toLong(stack.pop()))); break;
+ case BANG: stack.push(JS.B(!JS.toBoolean(stack.pop()))); break;
+ case NEWFUNCTION: stack.push(((JSFunction)arg).cloneWithNewParentScope(scope)); break;
case LABEL: break;
case TYPEOF: {
- Object o = cx.stack.pop();
- if (o == null) cx.stack.push(null);
- else if (o instanceof JS) cx.stack.push(((JS)o).typeName());
- else if (o instanceof String) cx.stack.push("string");
- else if (o instanceof Number) cx.stack.push("number");
- else if (o instanceof Boolean) cx.stack.push("boolean");
- else cx.stack.push("unknown");
+ Object o = stack.pop();
+ if (o == null) stack.push(null);
+ else if (o instanceof JS) stack.push(((JS)o).typeName());
+ else if (o instanceof String) stack.push("string");
+ else if (o instanceof Number) stack.push("number");
+ else if (o instanceof Boolean) stack.push("boolean");
+ else stack.push("unknown");
break;
}
case PUSHKEYS: {
- Object o = cx.stack.peek();
+ Object o = stack.peek();
Enumeration e = ((JS)o).keys();
JSArray a = new JSArray();
while(e.hasMoreElements()) a.addElement(e.nextElement());
- cx.stack.push(a);
+ stack.push(a);
break;
}
case LOOP:
- cx.stack.push(new LoopMarker(cx.pc, cx.pc > 0 && cx.f.op[cx.pc - 1] == LABEL ?
- (String)cx.f.arg[cx.pc - 1] : (String)null, cx.scope));
- cx.stack.push(Boolean.TRUE);
+ stack.push(new LoopMarker(pc, pc > 0 && f.op[pc - 1] == LABEL ? (String)f.arg[pc - 1] : (String)null, scope));
+ stack.push(Boolean.TRUE);
break;
case BREAK:
case CONTINUE:
- while(cx.stack.size() > 0) {
- Object o = cx.stack.pop();
- if (o instanceof CallMarker) ee("break or continue not within a loop");
+ while(stack.size() > 0) {
+ Object o = stack.pop();
+ if (o instanceof CallMarker) je("break or continue not within a loop");
if (o instanceof TryMarker) {
if(((TryMarker)o).finallyLoc < 0) continue; // no finally block, keep going
- cx.stack.push(new FinallyData(op, arg));
- cx.scope = ((TryMarker)o).scope;
- cx.pc = ((TryMarker)o).finallyLoc - 1;
+ 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;
- int endOfLoop = ((Integer)cx.f.arg[loopInstructionLocation]).intValue() + loopInstructionLocation;
- cx.scope = ((LoopMarker)o).scope;
- if (op == CONTINUE) { cx.stack.push(o); cx.stack.push(Boolean.FALSE); }
- cx.pc = op == BREAK ? endOfLoop - 1 : loopInstructionLocation;
+ int endOfLoop = ((Integer)f.arg[loopInstructionLocation]).intValue() + loopInstructionLocation;
+ scope = ((LoopMarker)o).scope;
+ if (op == CONTINUE) { stack.push(o); stack.push(Boolean.FALSE); }
+ pc = op == BREAK ? endOfLoop - 1 : loopInstructionLocation;
continue OUTER;
}
}
}
throw new Error("CONTINUE/BREAK invoked but couldn't find LoopMarker at " +
- cx.getSourceName() + ":" + cx.getLine());
+ JS.getSourceName() + ":" + JS.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
- cx.stack.push(new TryMarker(jmps[0] < 0 ? -1 : cx.pc + jmps[0], jmps[1] < 0 ? -1 : cx.pc + jmps[1], cx.scope));
+ stack.push(new TryMarker(jmps[0] < 0 ? -1 : pc + jmps[0], jmps[1] < 0 ? -1 : pc + jmps[1], scope));
break;
}
case RETURN: {
- Object retval = cx.stack.pop();
- while(cx.stack.size() > 0) {
- Object o = cx.stack.pop();
+ Object retval = stack.pop();
+ while(stack.size() > 0) {
+ Object o = stack.pop();
if (o instanceof TryMarker) {
if(((TryMarker)o).finallyLoc < 0) continue;
- cx.stack.push(retval);
- cx.stack.push(new FinallyData(RETURN));
- cx.scope = ((TryMarker)o).scope;
- cx.pc = ((TryMarker)o).finallyLoc - 1;
+ stack.push(retval);
+ stack.push(new FinallyData(RETURN));
+ scope = ((TryMarker)o).scope;
+ pc = ((TryMarker)o).finallyLoc - 1;
continue OUTER;
} else if (o instanceof CallMarker) {
- if (cx.scope instanceof JSTrap.JSTrapScope) {
- JSTrap.JSTrapScope ts = (JSTrap.JSTrapScope)cx.scope;
+ if (scope instanceof Trap.TrapScope) {
+ Trap.TrapScope ts = (Trap.TrapScope)scope;
if (!ts.cascadeHappened) {
ts.cascadeHappened = true;
- JSTrap t = ts.t.next;
+ Trap t = ts.t.next;
while (t != null && t.f.numFormalArgs == 0) t = t.next;
if (t == null) {
((JS)ts.t.trapee).put(t.name, ts.val);
- if (cx.pausecount > initialPauseCount) return null; // we were paused
+ if (pausecount > initialPauseCount) { pc++; return null; } // we were paused
} else {
- cx.stack.push(o);
+ stack.push(o);
JSArray args = new JSArray();
args.addElement(ts.val);
- cx.stack.push(args);
- cx.f = t.f;
- cx.scope = new JSTrap.JSTrapScope(cx.f.parentJSScope, t, ts.val);
- cx.pc = -1;
+ stack.push(args);
+ f = t.f;
+ scope = new Trap.TrapScope(f.parentScope, t, ts.val);
+ pc = -1;
break;
}
}
}
- cx.scope = ((CallMarker)o).scope;
- cx.pc = ((CallMarker)o).pc;
- cx.f = (JSFunction)((CallMarker)o).f;
- cx.stack.push(retval);
+ scope = ((CallMarker)o).scope;
+ pc = ((CallMarker)o).pc;
+ f = (JSFunction)((CallMarker)o).f;
+ stack.push(retval);
continue OUTER;
}
}
}
case PUT: {
- Object val = cx.stack.pop();
- Object key = cx.stack.pop();
- Object target = cx.stack.peek();
+ Object val = stack.pop();
+ Object key = stack.pop();
+ Object target = stack.peek();
if (target == null)
throw je("tried to put a value to the " + key + " property on the null value");
if (!(target instanceof JS))
throw je("tried to put a value to the " + key + " property on a " + target.getClass().getName());
if (key == null)
throw je("tried to assign \"" + (val==null?"(null)":val.toString()) + "\" to the null key");
- JSTrap t = null;
- if (target instanceof JSTrap.JSTrappable) {
- t = ((JSTrap.JSTrappable)target).getTrap(val);
+ Trap t = null;
+ if (target instanceof JS) {
+ t = ((JS)target).getTrap(val);
while (t != null && t.f.numFormalArgs == 0) t = t.next;
- } else if (target instanceof JSTrap.JSTrapScope && key.equals("cascade")) {
- JSTrap.JSTrapScope ts = (JSTrap.JSTrapScope)target;
+ } else if (target instanceof Trap.TrapScope && key.equals("cascade")) {
+ Trap.TrapScope ts = (Trap.TrapScope)target;
t = ts.t.next;
ts.cascadeHappened = true;
while (t != null && t.f.numFormalArgs == 0) t = t.next;
if (t == null) target = ts.t.trapee;
}
if (t != null) {
- cx.stack.push(new CallMarker(cx));
+ stack.push(new CallMarker(this));
JSArray args = new JSArray();
args.addElement(val);
- cx.stack.push(args);
- cx.f = t.f;
- cx.scope = new JSTrap.JSTrapScope(cx.f.parentJSScope, t, val);
- cx.pc = -1;
+ stack.push(args);
+ f = t.f;
+ scope = new Trap.TrapScope(f.parentScope, t, val);
+ pc = -1;
break;
}
((JS)target).put(key, val);
- if (cx.pausecount > initialPauseCount) return null; // we were paused
- cx.stack.push(val);
+ if (pausecount > initialPauseCount) { pc++; return null; } // we were paused
+ stack.push(val);
break;
}
case GET_PRESERVE: {
Object o, v;
if (op == GET) {
- v = arg == null ? cx.stack.pop() : arg;
- o = cx.stack.pop();
+ v = arg == null ? stack.pop() : arg;
+ o = stack.pop();
} else {
- v = cx.stack.pop();
- o = cx.stack.peek();
- cx.stack.push(v);
+ v = stack.pop();
+ o = stack.peek();
+ stack.push(v);
}
Object ret = null;
if (v == null) throw je("tried to get the null key from " + o);
- if (o == null) throw je("tried to get property \"" + v + "\" from the null value @" + cx.pc + "\n" + cx.f.dump());
+ if (o == null) throw je("tried to get property \"" + v + "\" from the null value @" + pc + "\n" + f.dump());
if (o instanceof String || o instanceof Number || o instanceof Boolean) {
- ret = Internal.getFromPrimitive(o,v);
- cx.stack.push(ret);
+ ret = getFromPrimitive(o,v);
+ stack.push(ret);
break;
} else if (o instanceof JS) {
- JSTrap t = null;
- if (o instanceof JSTrap.JSTrappable) {
- t = ((JSTrap.JSTrappable)o).getTrap(v);
+ Trap t = null;
+ if (o instanceof JS) {
+ t = ((JS)o).getTrap(v);
while (t != null && t.f.numFormalArgs != 0) t = t.next;
- } else if (o instanceof JSTrap.JSTrapScope && v.equals("cascade")) {
- t = ((JSTrap.JSTrapScope)o).t.next;
+ } else if (o instanceof Trap.TrapScope && v.equals("cascade")) {
+ t = ((Trap.TrapScope)o).t.next;
while (t != null && t.f.numFormalArgs != 0) t = t.next;
- if (t == null) o = ((JSTrap.JSTrapScope)o).t.trapee;
+ if (t == null) o = ((Trap.TrapScope)o).t.trapee;
}
if (t != null) {
- cx.stack.push(new CallMarker(cx));
+ stack.push(new CallMarker(this));
JSArray args = new JSArray();
- cx.stack.push(args);
- cx.f = t.f;
- cx.scope = new JSTrap.JSTrapScope(cx.f.parentJSScope, t, null);
- ((JSTrap.JSTrapScope)cx.scope).cascadeHappened = true;
- cx.pc = -1;
+ stack.push(args);
+ f = t.f;
+ scope = new Trap.TrapScope(f.parentScope, t, null);
+ ((Trap.TrapScope)scope).cascadeHappened = true;
+ pc = -1;
break;
}
ret = ((JS)o).get(v);
- if (ret == JSCallable.METHOD) ret = new Internal.JSCallableStub((JS)o, v);
- if (cx.pausecount > initialPauseCount) return null; // we were paused
- cx.stack.push(ret);
+ if (ret == JS.METHOD) ret = new Stub((JS)o, v);
+ if (pausecount > initialPauseCount) { pc++; return null; } // we were paused
+ stack.push(ret);
break;
}
throw je("tried to get property " + v + " from a " + o.getClass().getName());
int numArgs = JS.toInt(arg);
Object method = null;
Object ret = null;
- Object object = cx.stack.pop();
+ Object object = stack.pop();
- if (op == CALL) {
- object = cx.stack.pop();
- } else {
- if (object == JSCallable.METHOD) {
- method = cx.stack.pop();
- object = cx.stack.pop();
+ if (op == CALLMETHOD) {
+ if (object == JS.METHOD) {
+ method = stack.pop();
+ object = stack.pop();
} else {
- cx.stack.pop();
- cx.stack.pop();
+ stack.pop();
+ stack.pop();
}
}
if (object instanceof String || object instanceof Number || object instanceof Boolean) {
- JSArray arguments = new JSArray();
- for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j);
- ret = Internal.callMethodOnPrimitive(object, method, arguments);
+ if (numArgs > 2) throw new JSExn("too many arguments to primitive method");
+ Object arg1 = numArgs >= 2 ? stack.pop() : null;
+ Object arg0 = numArgs >= 1 ? stack.pop() : null;
+ ret = callMethodOnPrimitive(object, method, arg0, arg1, numArgs);
} else if (object instanceof JSFunction) {
// FEATURE: use something similar to call0/call1/call2 here
JSArray arguments = new JSArray();
- for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j);
- cx.stack.push(new CallMarker(cx));
- cx.stack.push(arguments);
- cx.f = (JSFunction)object;
- cx.scope = new JSScope(cx.f.parentJSScope);
- cx.pc = -1;
+ for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(stack.pop(), j);
+ stack.push(new CallMarker(this));
+ stack.push(arguments);
+ f = (JSFunction)object;
+ scope = new JSScope(f.parentScope);
+ pc = -1;
break;
- } else if (object instanceof JSCallable) {
- JSCallable c = (JSCallable)object;
+ } else if (object instanceof JS) {
+ JS c = (JS)object;
Object[] rest = numArgs > 3 ? new Object[numArgs - 3] : null;
- for(int i=numArgs - 1; i>2; i--) rest[i-3] = cx.stack.pop();
- Object a2 = numArgs <= 2 ? null : cx.stack.pop();
- Object a1 = numArgs <= 1 ? null : cx.stack.pop();
- Object a0 = numArgs <= 0 ? null : cx.stack.pop();
+ for(int i=numArgs - 1; i>2; i--) rest[i-3] = stack.pop();
+ Object a2 = numArgs <= 2 ? null : stack.pop();
+ Object a1 = numArgs <= 1 ? null : stack.pop();
+ Object a0 = numArgs <= 0 ? null : stack.pop();
ret = method == null ? c.call(a0, a1, a2, rest, numArgs) : c.callMethod(method, a0, a1, a2, rest, numArgs);
} else {
- throw new JS.Exn("can't call a " + object.getClass().getName() + " @" + cx.pc + "\n" + cx.f.dump());
+ throw new JSExn("can't call a " + object + " @" + pc + "\n" + f.dump());
}
- if (cx.pausecount > initialPauseCount) return null;
- cx.stack.push(ret);
+ if (pausecount > initialPauseCount) { pc++; return null; }
+ stack.push(ret);
break;
}
case THROW: {
- Object o = cx.stack.pop();
- if(o instanceof JS.Exn) throw (JS.Exn)o;
- throw new JS.Exn(o);
+ Object o = stack.pop();
+ if(o instanceof JSExn) throw (JSExn)o;
+ throw new JSExn(o);
}
case ASSIGN_SUB: case ASSIGN_ADD: {
- Object val = cx.stack.pop();
- Object old = cx.stack.pop();
- Object key = cx.stack.pop();
- Object obj = cx.stack.peek();
+ Object val = stack.pop();
+ Object old = stack.pop();
+ Object key = stack.pop();
+ Object obj = stack.peek();
if (val instanceof JSFunction && obj instanceof JSScope) {
JSScope parent = (JSScope)obj;
- while(parent.getParentJSScope() != null) parent = parent.getParentJSScope();
- if (parent instanceof JSTrap.JSTrappable) {
- JSTrap.JSTrappable b = (JSTrap.JSTrappable)parent;
- if (op == ASSIGN_ADD) JSTrap.addTrap(b, key, (JSFunction)val);
- else JSTrap.delTrap(b, key, (JSFunction)val);
+ while(parent.getParentScope() != null) parent = parent.getParentScope();
+ if (parent instanceof JS) {
+ JS b = (JS)parent;
+ if (op == ASSIGN_ADD) b.addTrap(key, (JSFunction)val);
+ else b.delTrap(key, (JSFunction)val);
// skip over the "normal" implementation of +=/-=
- cx.pc += ((Integer)arg).intValue() - 1;
+ pc += ((Integer)arg).intValue() - 1;
break;
}
}
// use the "normal" implementation
- cx.stack.push(key);
- cx.stack.push(old);
- cx.stack.push(val);
+ stack.push(key);
+ stack.push(old);
+ stack.push(val);
break;
}
if(count < 2) throw new Error("this should never happen");
if(count == 2) {
// common case
- Object right = cx.stack.pop();
- Object left = cx.stack.pop();
+ Object right = stack.pop();
+ Object left = stack.pop();
if(left instanceof String || right instanceof String)
- cx.stack.push(JS.toString(left).concat(JS.toString(right)));
- else cx.stack.push(new Double(JS.toDouble(left) + JS.toDouble(right)));
+ stack.push(JS.toString(left).concat(JS.toString(right)));
+ else stack.push(JS.N(JS.toDouble(left) + JS.toDouble(right)));
} else {
Object[] args = new Object[count];
- while(--count >= 0) args[count] = cx.stack.pop();
+ while(--count >= 0) args[count] = stack.pop();
if(args[0] instanceof String) {
StringBuffer sb = new StringBuffer(64);
for(int i=0;i<args.length;i++) sb.append(JS.toString(args[i]));
- cx.stack.push(sb.toString());
+ stack.push(sb.toString());
} else {
int numStrings = 0;
for(int i=0;i<args.length;i++) if(args[i] instanceof String) numStrings++;
if(numStrings == 0) {
double d = 0.0;
for(int i=0;i<args.length;i++) d += JS.toDouble(args[i]);
- cx.stack.push(new Double(d));
+ stack.push(JS.N(d));
} else {
int i=0;
StringBuffer sb = new StringBuffer(64);
do {
d += JS.toDouble(args[i++]);
} while(!(args[i] instanceof String));
- sb.append(JS.toString(new Double(d)));
+ sb.append(JS.toString(JS.N(d)));
}
while(i < args.length) sb.append(JS.toString(args[i++]));
- cx.stack.push(sb.toString());
+ stack.push(sb.toString());
}
}
}
}
default: {
- Object right = cx.stack.pop();
- Object left = cx.stack.pop();
+ Object right = stack.pop();
+ Object left = stack.pop();
switch(op) {
- case BITOR: cx.stack.push(new Long(JS.toLong(left) | JS.toLong(right))); break;
- case BITXOR: cx.stack.push(new Long(JS.toLong(left) ^ JS.toLong(right))); break;
- case BITAND: cx.stack.push(new Long(JS.toLong(left) & JS.toLong(right))); break;
-
- case SUB: cx.stack.push(new Double(JS.toDouble(left) - JS.toDouble(right))); break;
- case MUL: cx.stack.push(new Double(JS.toDouble(left) * JS.toDouble(right))); break;
- case DIV: cx.stack.push(new Double(JS.toDouble(left) / JS.toDouble(right))); break;
- case MOD: cx.stack.push(new Double(JS.toDouble(left) % JS.toDouble(right))); break;
+ case BITOR: stack.push(JS.N(JS.toLong(left) | JS.toLong(right))); break;
+ case BITXOR: stack.push(JS.N(JS.toLong(left) ^ JS.toLong(right))); break;
+ case BITAND: stack.push(JS.N(JS.toLong(left) & JS.toLong(right))); break;
+
+ case SUB: stack.push(JS.N(JS.toDouble(left) - JS.toDouble(right))); break;
+ case MUL: stack.push(JS.N(JS.toDouble(left) * JS.toDouble(right))); break;
+ case DIV: stack.push(JS.N(JS.toDouble(left) / JS.toDouble(right))); break;
+ case MOD: stack.push(JS.N(JS.toDouble(left) % JS.toDouble(right))); break;
- case LSH: cx.stack.push(new Long(JS.toLong(left) << JS.toLong(right))); break;
- case RSH: cx.stack.push(new Long(JS.toLong(left) >> JS.toLong(right))); break;
- case URSH: cx.stack.push(new Long(JS.toLong(left) >>> JS.toLong(right))); break;
+ case LSH: stack.push(JS.N(JS.toLong(left) << JS.toLong(right))); break;
+ case RSH: stack.push(JS.N(JS.toLong(left) >> JS.toLong(right))); break;
+ case URSH: stack.push(JS.N(JS.toLong(left) >>> JS.toLong(right))); break;
case LT: case LE: case GT: case GE: {
- if (left == null) left = new Integer(0);
- if (right == null) right = new Integer(0);
+ if (left == null) left = JS.N(0);
+ if (right == null) right = JS.N(0);
int result = 0;
if (left instanceof String || right instanceof String) {
result = left.toString().compareTo(right.toString());
} else {
result = (int)java.lang.Math.ceil(JS.toDouble(left) - JS.toDouble(right));
}
- cx.stack.push(new Boolean((op == LT && result < 0) || (op == LE && result <= 0) ||
- (op == GT && result > 0) || (op == GE && result >= 0)));
+ stack.push(JS.B((op == LT && result < 0) || (op == LE && result <= 0) ||
+ (op == GT && result > 0) || (op == GE && result >= 0)));
break;
}
if (l == null) { Object tmp = r; r = l; l = tmp; }
if (l == null && r == null) ret = true;
else if (r == null) ret = false; // l != null, so its false
- else if (l instanceof Boolean) ret = new Boolean(JS.toBoolean(r)).equals(l);
+ else if (l instanceof Boolean) ret = JS.B(JS.toBoolean(r)).equals(l);
else if (l instanceof Number) ret = JS.toNumber(r).doubleValue() == JS.toNumber(l).doubleValue();
else if (l instanceof String) ret = r != null && l.equals(r.toString());
else ret = l.equals(r);
- cx.stack.push(new Boolean(op == EQ ? ret : !ret)); break;
+ stack.push(JS.B(op == EQ ? ret : !ret)); break;
}
default: throw new Error("unknown opcode " + op);
} }
}
- } catch(JS.Exn e) {
- while(cx.stack.size() > 0) {
- Object o = cx.stack.pop();
+ } catch(JSExn e) {
+ while(stack.size() > 0) {
+ Object o = stack.pop();
if (o instanceof CatchMarker || o instanceof TryMarker) {
boolean inCatch = o instanceof CatchMarker;
if(inCatch) {
- o = cx.stack.pop();
+ 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
- cx.stack.push(o);
- cx.stack.push(catchMarker);
- cx.stack.push(e.getObject());
- cx.scope = ((TryMarker)o).scope;
- cx.pc = ((TryMarker)o).catchLoc - 1;
+ stack.push(o);
+ stack.push(catchMarker);
+ stack.push(e.getObject());
+ scope = ((TryMarker)o).scope;
+ pc = ((TryMarker)o).catchLoc - 1;
continue OUTER;
} else {
- cx.stack.push(e);
- cx.stack.push(new FinallyData(THROW));
- cx.scope = ((TryMarker)o).scope;
- cx.pc = ((TryMarker)o).finallyLoc - 1;
+ stack.push(e);
+ stack.push(new FinallyData(THROW));
+ scope = ((TryMarker)o).scope;
+ pc = ((TryMarker)o).finallyLoc - 1;
continue OUTER;
}
}
} // end for
}
- // Exception Stuff ////////////////////////////////////////////////////////////////
-
- static class EvaluatorException extends RuntimeException { public EvaluatorException(String s) { super(s); } }
- static EvaluatorException ee(String s) {
- throw new EvaluatorException(JSContext.getSourceName() + ":" + JSContext.getLine() + " " + s);
- }
- static JS.Exn je(String s) {
- throw new JS.Exn(JSContext.getSourceName() + ":" + JSContext.getLine() + " " + s);
- }
// Markers //////////////////////////////////////////////////////////////////////
int pc;
JSScope scope;
JSFunction f;
- public CallMarker(JSContext cx) { pc = cx.pc + 1; scope = cx.scope; f = cx.f; }
+ public CallMarker(Interpreter cx) { pc = cx.pc + 1; scope = cx.scope; f = cx.f; }
}
public static class CatchMarker { public CatchMarker() { } }
public FinallyData(int op, Object arg) { this.op = op; this.arg = arg; }
public FinallyData(int op) { this(op,null); }
}
+
+
+ // Operations on Primitives //////////////////////////////////////////////////////////////////////
+
+ static Object callMethodOnPrimitive(Object o, Object method, Object arg0, Object arg1, int alength) {
+ if (o instanceof Number) {
+ //#switch(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": {
+ int radix = alength >= 1 ? JS.toInt(arg0) : 10;
+ return Long.toString(((Number)o).longValue(),radix);
+ }
+ //#end
+ } else if (o instanceof Boolean) {
+ // No methods for Booleans
+ throw new JSExn("attempt to call a method on a Boolean");
+ }
+
+ String s = JS.toString(o);
+ int slength = s.length();
+ //#switch(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 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 "";
+ return s.substring(start,start+len);
+ }
+ case "charAt": {
+ int p = alength >= 1 ? JS.toInt(arg0) : 0;
+ if (p < 0 || p >= slength) return "";
+ return 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": {
+ // FIXME takes variable number of arguments...
+ /*
+ StringBuffer sb = new StringBuffer(slength*2).append(s);
+ for(int i=0;i<alength;i++) sb.append(args.elementAt(i));
+ return sb.toString();
+ */
+ }
+ case "indexOf": {
+ String search = alength >= 1 ? arg0.toString() : "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 ? arg0.toString() : "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(s,arg0);
+ case "replace": return JSRegexp.stringReplace(s,(String)arg0,arg1);
+ case "search": return JSRegexp.stringSearch(s,arg0);
+ case "split": return JSRegexp.stringSplit(s,(String)arg0,arg1);
+ case "toLowerCase": return s.toLowerCase();
+ case "toUpperCase": return s.toUpperCase();
+ case "toString": return s;
+ 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 "";
+ return s.substring(a,b);
+ }
+ //#end
+ throw new JSExn("Attempted to call non-existent method: " + method);
+ }
+
+ static Object getFromPrimitive(Object o, Object key) {
+ boolean returnJS = false;
+ if (o instanceof Boolean) {
+ throw new JSExn("cannot call methods on Booleans");
+ } else if (o instanceof Number) {
+ if (key.equals("toPrecision") || key.equals("toExponential") || key.equals("toFixed"))
+ returnJS = true;
+ }
+ if (!returnJS) {
+ // the string stuff applies to everything
+ String s = o.toString();
+
+ // this is sort of ugly, but this list should never change
+ // These should provide a complete (enough) implementation of the ECMA-262 String object
+
+ //#switch(key)
+ case "length": return JS.N(s.length());
+ case "substring": returnJS = true; break;
+ case "charAt": returnJS = true; break;
+ case "charCodeAt": returnJS = true; break;
+ case "concat": returnJS = true; break;
+ case "indexOf": returnJS = true; break;
+ case "lastIndexOf": returnJS = true; break;
+ case "match": returnJS = true; break;
+ case "replace": returnJS = true; break;
+ case "seatch": returnJS = true; break;
+ case "slice": returnJS = true; break;
+ case "split": returnJS = true; break;
+ case "toLowerCase": returnJS = true; break;
+ case "toUpperCase": returnJS = true; break;
+ case "toString": returnJS = true; break;
+ case "substr": returnJS = true; break;
+ //#end
+ }
+ if (returnJS) {
+ final Object target = o;
+ final String method = key.toString();
+ return new JS() {
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) {
+ if (nargs > 2) throw new JSExn("cannot call that method with that many arguments");
+ return callMethodOnPrimitive(target, method, a0, a1, nargs);
+ }
+ };
+ }
+ return null;
+ }
+
+ private static class Stub extends JS {
+ private Object method;
+ JS obj;
+ public Stub(JS obj, Object method) { this.obj = obj; this.method = method; }
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) {
+ return ((JS)obj).callMethod(method, a0, a1, a2, rest, nargs);
+ }
+ }
}
// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
-
package org.xwt.js;
+
import org.xwt.util.*;
import org.xwt.*;
import java.io.*;
import java.util.*;
-/**
- * The public API for the JS engine. JS itself is actually a class
- * implementing the absolute minimal amount of functionality for an
- * Object which can be manipulated by JavaScript code.
- */
+// FIXME: grafts
+/** The minimum set of functionality required for objects which are manipulated by JavaScript */
public class JS {
- // Public Helper Methods //////////////////////////////////////////////////////////////////////
+ // Static Interpreter Control Methods ///////////////////////////////////////////////////////////////
+
+ public static int getLine() {
+ Interpreter c = Interpreter.current();
+ return c.f == null || c.pc < 0 || c.pc >= c.f.size ? -1 : c.f.line[c.pc];
+ }
+
+ public static String getSourceName() {
+ Interpreter c = Interpreter.current();
+ return c.f == null ? null : c.f.sourceName;
+ }
+
+ public static class PausedException extends Exception { PausedException() { } }
+
+ public static void invokePauseable(JSFunction function) throws JS.PausedException {
+ Interpreter i = new Interpreter(function, true, new JSArray());
+ int oldpausecount = i.pausecount;
+ i.resume();
+ if (i.pausecount > oldpausecount) throw new PausedException();
+ }
+
+ public static class NotPauseableException extends Exception { NotPauseableException() { } }
- /** parse and compile a function */
- public static JSFunction parse(String sourceName, int firstLine, Reader sourceCode) throws IOException {
- return new JSFunction(sourceName, firstLine, sourceCode, null);
+ /** returns a callback which will restart the context; expects a value to be pushed onto the stack when unpaused */
+ public static UnpauseCallback pause() throws NotPauseableException {
+ Interpreter i = Interpreter.current();
+ if (i.pausecount == -1) throw new NotPauseableException();
+ i.pausecount++;
+ return new JS.UnpauseCallback(i);
}
+ public static class UnpauseCallback {
+ Interpreter i;
+ UnpauseCallback(Interpreter i) { this.i = i; }
+ public void unpause(Object o) throws PausedException {
+ i.stack.push(o);
+ i.resume();
+ }
+ }
+
+
+
+ // Static Helper Methods ///////////////////////////////////////////////////////////////////////////////////
+
/** coerce an object to a Boolean */
public static boolean toBoolean(Object o) {
if (o == null) return false;
/** coerce an object to a Number */
public static Number toNumber(Object o) {
- if (o == null) return new Long(0);
+ if (o == null) return ZERO;
if (o instanceof Number) return ((Number)o);
// NOTE: There are about 3 pages of rules in ecma262 about string to number conversions
// We aren't even close to following all those rules. We probably never will be.
if (o instanceof String)
- try { return new Double((String)o); } catch (NumberFormatException e) { return new Double(Double.NaN); }
- if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? new Long(1) : new Long(0);
+ try { return N((String)o); } catch (NumberFormatException e) { return N(Double.NaN); }
+ if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? N(1) : ZERO;
if (o instanceof JS) return ((JS)o).coerceToNumber();
throw new Error("toNumber() got object of type " + o.getClass().getName() + " which we don't know how to handle");
}
}
return o.toString();
}
+
// Instance Methods ////////////////////////////////////////////////////////////////////
+
+ public static final Integer ZERO = new Integer(0);
// this gets around a wierd fluke in the Java type checking rules for ?..:
public static final Object T = Boolean.TRUE;
public static final Object F = Boolean.FALSE;
- // FEATURE: be smart here; perhaps intern
+ // FIXME: be much smarter here
public static final Number N(int i) { return new Integer(i); }
public static final Number N(long l) { return new Long(l); }
public static final Number N(double d) { return new Double(d); }
+ public static final Number N(String s) { return new Double(s); }
public static final Boolean B(boolean b) { return b ? Boolean.TRUE : Boolean.FALSE; }
+ public static final Boolean B(int i) { return i==0 ? Boolean.FALSE : Boolean.TRUE; }
private static Enumeration emptyEnumeration = new Enumeration() {
public boolean hasMoreElements() { return false; }
public Object get(Object key) { return entries == null ? null : entries.get(key, null); }
public void put(Object key, Object val) { if (entries == null) entries = new Hash(); entries.put(key, null, val); }
- // note that we don't actually implement trappable... we just provide these for subclasses which choose to
- public JSTrap getTrap(Object key) { return entries == null ? null : (JSTrap)entries.get(key, JSTrap.class); }
- public void putTrap(Object key, JSTrap t) { if (entries == null) entries = new Hash(); entries.put(key, JSTrap.class, t); }
- public Number coerceToNumber() { throw new JS.Exn("tried to coerce a JavaScript object to a Number"); }
- public String coerceToString() { throw new JS.Exn("tried to coerce a JavaScript object to a String"); }
- public boolean coerceToBoolean() { throw new JS.Exn("tried to coerce a JavaScript object to a Boolean"); }
+ // Trap support //////////////////////////////////////////////////////////////////////////////
- public String typeName() { return "object"; }
+ /** override and return true to allow placing traps on this object */
+ protected boolean isTrappable() { return false; }
+ /** performs a put, triggering traps if present; traps are run in an unpauseable interpreter */
+ public final void putAndTriggerTraps(Object key, Object value) {
+ Trap t = getTrap(key);
+ if (t != null) t.invoke(key, value);
+ else put(key, value);
+ }
- // Inner Classes /////////////////////////////////////////////////////////////////////////
+ /** performs a get, triggering traps if present; traps are run in an unpauseable interpreter */
+ public final Object getAndTriggerTraps(Object key) {
+ Trap t = getTrap(key);
+ if (t != null) return t.invoke(key);
+ else return get(key);
+ }
- /** An exception which can be thrown and caught by JavaScript code */
- public static class Exn extends RuntimeException {
- private Object js = null;
- public Exn(Object js) { this.js = js; }
- public String toString() { return "JS.Exn: " + js; }
- public String getMessage() { return toString(); }
- public Object getObject() { return js; }
- }
+ /** retrieve a trap from the entries hash */
+ protected final Trap getTrap(Object key) {
+ return !isTrappable() || entries == null ? null : (Trap)entries.get(key, Trap.class);
+ }
- /** the result of a graft */
- // FIXME: uber-broken
- public static class Graft extends JSCallable {
- private JS graftee;
- private Object replaced_key;
- private Object replaced_val;
- public Graft(Object graftee, Object key, Object val) {
- if (graftee instanceof JSArray) throw new JS.Exn("can't graft onto JSArrays (yet)");
- if (graftee instanceof JSCallable) throw new JS.Exn("can't graft onto JSCallables (yet)");
- if (graftee instanceof JSScope) throw new JS.Exn("can't graft onto JSScopes (yet)");
- this.graftee = (JS)graftee;
- replaced_key = key;
- replaced_val = val;
- }
- public boolean equals(Object o) { return (this == o || graftee.equals(o)); }
- public int hashCode() { return graftee.hashCode(); }
- public Object get(Object key) { return replaced_key.equals(key) ? replaced_val : graftee.get(key); }
- public void put(Object key, Object val) { graftee.put(key, val); }
- /*
- public Object call(Object method, JSArray args) {
- if (replaced_key.equals(method)) return ((JSCallable)replaced_val).call(null, args);
- else if (graftee instanceof JSCallable) return ((JSCallable)graftee).call(method, args);
- else throw new JS.Exn("cannot call this value");
- }
- */
- public Number coerceToNumber() { return graftee.coerceToNumber(); }
- public String coerceToString() { return graftee.coerceToString(); }
- public boolean coerceToBoolean() { return graftee.coerceToBoolean(); }
- public String typeName() { return graftee.typeName(); }
- public Enumeration keys() {
- return new Enumeration() {
- Enumeration graftee_enumeration = graftee.keys();
- boolean returned_replaced = false;
- public boolean hasMoreElements() {
- if (graftee_enumeration.hasMoreElements()) return true;
- return !returned_replaced;
- }
- public Object nextElement() {
- if (!graftee_enumeration.hasMoreElements()) {
- if (returned_replaced) throw new NoSuchElementException();
- returned_replaced = true;
- return replaced_key;
- } else {
- Object ret = graftee_enumeration.nextElement();
- if (!returned_replaced && ret.equals(replaced_key)) returned_replaced = true;
- return ret;
- }
- }
- };
- }
+ /** retrieve a trap from the entries hash */
+ protected final void putTrap(Object key, Trap value) {
+ if (!isTrappable()) return;
+ if (entries == null) entries = new Hash();
+ entries.put(key, Trap.class, value);
}
-}
+ /** adds a trap, avoiding duplicates */
+ protected final void addTrap(Object name, JSFunction f) {
+ for(Trap t = getTrap(name); t != null; t = t.next) if (t.f == f) return;
+ putTrap(name, new Trap(this, name.toString(), f, (Trap)getTrap(name)));
+ }
+
+ /** deletes a trap, if present */
+ protected final void delTrap(Object name, JSFunction f) {
+ Trap t = (Trap)getTrap(name);
+ if (t == null) return;
+ if (t.f == f) { putTrap(t.name, t.next); return; }
+ for(; t.next != null; t = t.next) if (t.next.f == f) { t.next = t.next.next; return; }
+ }
+ // Call Support //////////////////////////////////////////////////////////////////////////////
+ // return this from get() if the key was actually a method.
+ public static final Object METHOD = new Object();
+ public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) {
+ throw new JSExn("attempted to call the null value (method "+method+")");
+ }
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) {
+ throw new JSExn("you cannot call this object)");
+ }
+
+
+ // Typing Support //////////////////////////////////////////////////////////////////////////////
+
+ public Number coerceToNumber() { throw new JSExn("tried to coerce a JavaScript object to a Number"); }
+ public String coerceToString() { throw new JSExn("tried to coerce a JavaScript object to a String"); }
+ public boolean coerceToBoolean() { throw new JSExn("tried to coerce a JavaScript object to a Boolean"); }
+
+ public String typeName() { return "object"; }
+
+}
import java.io.*;
import java.util.*;
+// FIXME: review, use redblacktrees
/** A JavaScript JSArray */
-public class JSArray extends JSCallable {
+public class JSArray extends JS {
private Vec vec = new Vec();
public JSArray() { }
public JSArray(int size) { vec.setSize(size); }
return super.callMethod(method, a0, a1, a2, rest, nargs);
}
- public Object get(Object key) throws JS.Exn {
+ public Object get(Object key) throws JSExn {
if (key instanceof Number) {
int i = intVal(key);
if (i == Integer.MIN_VALUE) return super.get(key);
}
};
private Object sort(Object tmp) {
- if(tmp instanceof JSCallable) {
+ if(tmp instanceof JS) {
final JSArray funcArgs = new JSArray(2);
- final JSCallable jsFunc = (JSCallable) tmp;
+ final JS jsFunc = (JS) tmp;
vec.sort(new Vec.CompareFunc() {
public int compare(Object a, Object b) {
funcArgs.setElementAt(a,0);
+++ /dev/null
-package org.xwt.js;
-
-/** anything that is callable with the () operator and wasn't compiled from JS code */
-public abstract class JSCallable extends JS {
-
- // return this from get() if the key was actually a method.
- public static final Object METHOD = new Object();
-
- public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) {
- if (method == null) return call(a0, a1, a2, rest, nargs);
- Class c = this.getClass();
- String descrip = c.getName();
- if (c == Internal.JSCallableStub.class) {
- descrip = ((Internal.JSCallableStub)this).obj.getClass().getName();
- }
- throw new JS.Exn("attempted to call an undefined method ("+method+") on a " + descrip);
- }
-
- public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) {
- Class c = this.getClass();
- String descrip = c.getName();
- if (c == Internal.JSCallableStub.class) {
- descrip = ((Internal.JSCallableStub)this).obj.getClass().getName();
- }
- throw new JS.Exn("you cannot call this object (of type " + descrip + ")");
- }
-
-}
-
+++ /dev/null
-package org.xwt.js;
-import java.util.*;
-import org.xwt.util.*;
-
-/** encapsulates the state of a JavaScript "thread" (no relation to Java threads) */
-public class JSContext {
-
- // Statics //////////////////////////////////////////////////////////////////////
- private int getLine_() { return current().f == null || (pc < 0 || pc >= f.size) ? -1 : f.line[pc]; }
- public static int getLine() { return current().getLine_(); }
- public static String getSourceName() { return current().f == null ? null : current().f.sourceName; }
-
- /** fetches the currently-executing javascript function */
- private static JSContext current() { return (JSContext)threadToJSContext.get(Thread.currentThread()); }
- private static Hashtable threadToJSContext = new Hashtable();
-
- // Instance members and methods //////////////////////////////////////////////////////////////////////
-
- /**
- * the number of times this context has been paused; used by
- * Function.eval() to make sure it behaves properly even if the
- * pause Callback is invoked *before* control is returned to
- * eval()
- */
- int pausecount = 0;
- boolean pauseable;
-
- JSFunction f; ///< the currently-executing JSFunction
- JSScope scope;
- Vec stack = new Vec(); ///< the object stack
- int pc = 0; ///< the program counter
-
- /** can be paused */
- public static void invokePauseable(JSFunction function) {
- new JSContext(function, true).invoke(new JSArray());
- }
-
- /** cannot be paused */
- public static void invokeTrap(JSTrap.JSTrappable t, Object key, Object value) {
- JSFunction invoker = new JSFunction("trap invoker", 0, null);
- invoker.add(-1, ByteCodes.PUT, null);
- JSContext cx = new JSContext(invoker, false);
- cx.stack.push(t);
- cx.stack.push(key);
- cx.stack.push(value);
- cx.resume();
- }
-
- JSContext(JSFunction f, boolean pauseable) {
- this.pauseable = pauseable;
- this.f = f;
- scope = new JSScope(f.parentJSScope);
- }
-
- void invoke(JSArray args) {
- JSFunction tf = f;
- f = null;
- stack.push(new Interpreter.CallMarker(this));
- f = tf;
- stack.push(args);
- resume();
- }
-
- /** returns a callback which will restart the context, or null if this context is not pauseable */
- public static Callback pause() { return current().pause_(); }
- private Callback pause_() {
- if (!pauseable) return null;
- pausecount++;
- return new Callback() { public Object call(Object o) {
- stack.push(o);
- pc++;
- resume();
- return null;
- } };
- }
-
- private void resume() {
- Thread t = Thread.currentThread();
- JSContext old = (JSContext)threadToJSContext.get(t);
- threadToJSContext.put(t, this);
- try {
- Interpreter.eval(this);
- } finally {
- if (old == null) threadToJSContext.remove(t);
- else threadToJSContext.put(t, old);
- }
- }
-}
* @author Mike McCabe
* @author Adam Megacz (many modifications
*/
-public class JSDate extends JSCallable {
+public class JSDate extends JS {
public JSDate() {
if (thisTimeZone == null) {
private static double _toNumber(Object[] o, int index) { return JS.toDouble(o[index]); }
private static double toDouble(double d) { return d; }
+ // FIXME: switch to new calling convention here
public JSDate(JSArray args_) {
Object[] args = new Object[args_.length()];
for(int i=0; i<args.length; i++) args[i] = args_.elementAt(i);
import java.io.*;
/** A JavaScript function, compiled into bytecode */
-public class JSFunction extends JSCallable implements ByteCodes, Tokens {
+public class JSFunction extends JS implements ByteCodes, Tokens {
// Fields and Accessors ///////////////////////////////////////////////
int numFormalArgs = 0; ///< the number of formal arguments
+
String sourceName; ///< the source code file that this block was drawn from
- int[] line = new int[10]; ///< the line numbers
private int firstLine = -1; ///< the first line of this script
+
+ int[] line = new int[10]; ///< the line numbers
int[] op = new int[10]; ///< the instructions
Object[] arg = new Object[10]; ///< the arguments to the instructions
int size = 0; ///< the number of instruction/argument pairs
- JSScope parentJSScope; ///< the default scope to use as a parent scope when executing this
+ JSScope parentScope; ///< the default scope to use as a parent scope when executing this
+
+
+ // Public //////////////////////////////////////////////////////////////////////////////
- // Constructors ////////////////////////////////////////////////////////
+ /** parse and compile a function */
+ public static JSFunction fromReader(String sourceName, int firstLine, Reader sourceCode) throws IOException {
+ JSFunction ret = new JSFunction(sourceName, firstLine, null);
+ if (sourceCode == null) return ret;
+ Parser p = new Parser(sourceCode, sourceName, firstLine);
+ while(true) {
+ int s = ret.size;
+ p.parseStatement(ret, null);
+ if (s == ret.size) break;
+ }
+ ret.add(-1, LITERAL, null);
+ ret.add(-1, RETURN);
+ return ret;
+ }
- public JSFunction cloneWithNewParentJSScope(JSScope s) {
+ public JSFunction cloneWithNewParentScope(JSScope s) {
JSFunction ret = new JSFunction(sourceName, firstLine, s);
// Reuse the same op, arg, line, and size variables for the new "instance" of the function
// NOTE: Neither *this* function nor the new function should be modified after this call
return ret;
}
- JSFunction(String sourceName, int firstLine, JSScope parentJSScope) {
- this.sourceName = sourceName;
- this.firstLine = firstLine;
- this.parentJSScope = parentJSScope;
- }
-
- protected JSFunction(String sourceName, int firstLine, Reader sourceCode, JSScope parentJSScope) throws IOException {
- this(sourceName, firstLine, parentJSScope);
- if (sourceCode == null) return;
- Parser p = new Parser(sourceCode, sourceName, firstLine);
- while(true) {
- int s = size;
- p.parseStatement(this, null);
- if (s == size) break;
- }
- add(-1, LITERAL, null);
- add(-1, RETURN);
- }
-
/** Note: code gets run in an <i>unpauseable</i> context. */
public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) {
- JSContext cx = new JSContext(this, false);
JSArray args = new JSArray();
if (nargs > 0) args.addElement(a0);
if (nargs > 1) args.addElement(a1);
if (nargs > 2) args.addElement(a2);
for(int i=3; i<nargs; i++) args.addElement(rest[i-3]);
- cx.invoke(args);
- return cx.stack.pop();
+ Interpreter cx = new Interpreter(this, false, args);
+ return cx.resume();
}
// Adding and Altering Bytecodes ///////////////////////////////////////////////////
+ JSFunction(String sourceName, int firstLine, JSScope parentScope) {
+ this.sourceName = sourceName;
+ this.firstLine = firstLine;
+ this.parentScope = parentScope;
+ }
+
int get(int pos) { return op[pos]; }
Object getArg(int pos) { return arg[pos]; }
void set(int pos, int op_, Object arg_) { op[pos] = op_; arg[pos] = arg_; }
for (int i=0; i < size; i++) {
sb.append(i);
sb.append(": ");
- if (op[i] < 0)
- sb.append(bytecodeToString[-op[i]]);
- else
- sb.append(codeToString[op[i]]);
+ if (op[i] < 0) sb.append(bytecodeToString[-op[i]]);
+ else sb.append(codeToString[op[i]]);
sb.append(" ");
sb.append(arg[i] == null ? "(no arg)" : arg[i]);
if((op[i] == JF || op[i] == JT || op[i] == JMP) && arg[i] != null && arg[i] instanceof Number) {
import java.util.*;
/** The JavaScript Math object */
-public class JSMath extends JSCallable {
+public class JSMath extends JS {
public static JSMath singleton = new JSMath();
+// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
package org.xwt.js;
import gnu.regexp.*;
-public class JSRegexp extends JSCallable {
+/** A JavaScript regular expression object */
+public class JSRegexp extends JS {
private boolean global;
private RE re;
private int lastIndex;
- public JSRegexp(Object arg0, Object arg1) throws JS.Exn {
+ public JSRegexp(Object arg0, Object arg1) throws JSExn {
if(arg0 instanceof JSRegexp) {
JSRegexp r = (JSRegexp) arg0;
this.global = r.global;
this.re = r.re;
this.lastIndex = r.lastIndex;
} else {
- String pattern = arg0.toString();
+ String pattern = (String)arg0;
String sFlags = null;
int flags = 0;
if(arg1 != null) sFlags = (String)arg1;
case 'i': flags |= RE.REG_ICASE; break;
case 'm': flags |= RE.REG_MULTILINE; break;
case 'g': global = true; break;
- default: throw new JS.Exn("Invalid flag in regexp \"" + sFlags.charAt(i) + "\"");
+ default: throw new JSExn("Invalid flag in regexp \"" + sFlags.charAt(i) + "\"");
}
}
re = newRE(pattern,flags);
- put("source",pattern);
- put("global",wrapBool(global));
- put("ignoreCase",wrapBool(flags & RE.REG_ICASE));
- put("multiline",wrapBool(flags & RE.REG_MULTILINE));
+ put("source", pattern);
+ put("global", B(global));
+ put("ignoreCase", B(flags & RE.REG_ICASE));
+ put("multiline", B(flags & RE.REG_MULTILINE));
}
}
switch(nargs) {
case 1: {
//#switch(method)
- case "exec": return exec((String)a0);
- case "test": return test((String)a0);
+ case "exec": {
+ String s = (String)a0;
+ int start = global ? lastIndex : 0;
+ if(start < 0 || start >= s.length()) { lastIndex = 0; return null; }
+ REMatch match = re.getMatch(s,start);
+ if(global) lastIndex = match == null ? s.length() : match.getEndIndex();
+ return match == null ? null : matchToExecResult(match,re,s);
+ }
+ case "test": {
+ String s = (String)a0;
+ if (!global) return B(re.getMatch(s) != null);
+ int start = global ? lastIndex : 0;
+ if(start < 0 || start >= s.length()) { lastIndex = 0; return null; }
+ REMatch match = re.getMatch(s,start);
+ lastIndex = match != null ? s.length() : match.getEndIndex();
+ return B(match != null);
+ }
case "toString": return toString(a0);
+ case "stringMatch": return stringMatch(a0,a1);
+ case "stringSearch": return stringSearch(a0,a1);
+ //#end
+ break;
+ }
+ case 2: {
+ //#switch(method)
+ case "stringReplace": return stringReplace(a0, a1,a2);
//#end
break;
}
case "exec": return METHOD;
case "test": return METHOD;
case "toString": return METHOD;
- case "lastIndex": return new Integer(lastIndex);
+ case "lastIndex": return N(lastIndex);
//#end
return super.get(key);
}
if(key.equals("lastIndex")) lastIndex = JS.toNumber(value).intValue();
super.put(key,value);
}
-
- private Object exec(String s) throws JS.Exn {
- int start = global ? lastIndex : 0;
- if(start < 0 || start >= s.length()) {
- lastIndex = 0;
- return null;
- }
-
- REMatch match = re.getMatch(s,start);
- if(global)
- lastIndex = match == null ? s.length() : match.getEndIndex();
- if(match == null)
- return null;
- else
- return matchToExecResult(match,re,s);
- }
-
+
private static Object matchToExecResult(REMatch match, RE re, String s) {
JS ret = new JS();
- ret.put("index",new Integer(match.getStartIndex()));
+ ret.put("index", N(match.getStartIndex()));
ret.put("input",s);
int n = re.getNumSubs();
- ret.put("length",new Integer(n+1));
+ ret.put("length", N(n+1));
ret.put("0",match.toString());
- for(int i=1;i<=n;i++)
- ret.put(Integer.toString(i),match.toString(i));
+ for(int i=1;i<=n;i++) ret.put(Integer.toString(i),match.toString(i));
return ret;
}
-
- private Object exec(JSArray args) throws JS.Exn {
- if(args.length() < 1) throw new JS.Exn("Not enough args to exec");
- String s = args.elementAt(0).toString();
- return exec(s);
- }
-
- private Object test(String s) throws JS.Exn {
- if(global) {
- int start = global ? lastIndex : 0;
- if(start < 0 || start >= s.length()) {
- lastIndex = 0;
- return null;
- }
-
- REMatch match = re.getMatch(s,start);
- lastIndex = match != null ? s.length() : match.getEndIndex();
- return wrapBool(match != null);
- } else {
- return wrapBool(re.getMatch(s) != null);
- }
- }
-
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append('/');
return sb.toString();
}
- public static Object stringMatch(Object o, JSArray args) throws JS.Exn {
- if(args.length() < 1) throw new JS.Exn("not enough args to match");
- Object arg0 = args.elementAt(0);
+ public static Object stringMatch(Object o, Object arg0) throws JSExn {
String s = o.toString();
RE re;
JSRegexp regexp = null;
REMatch match = re.getMatch(s);
return matchToExecResult(match,re,s);
}
- if(!regexp.global)
- return regexp.exec(s);
+ if(!regexp.global) return regexp.callMethod("exec", s, null, null, null, 1);
JSArray ret = new JSArray();
REMatch[] matches = re.getAllMatches(s);
- for(int i=0;i<matches.length;i++)
- ret.addElement(matches[i].toString());
- if(matches.length > 0)
- regexp.lastIndex = matches[matches.length-1].getEndIndex();
- else
- regexp.lastIndex = s.length();
+ for(int i=0;i<matches.length;i++) ret.addElement(matches[i].toString());
+ regexp.lastIndex = matches.length > 0 ? matches[matches.length-1].getEndIndex() : s.length();
return ret;
}
- public static Object stringSearch(Object o, JSArray args) throws JS.Exn {
- if(args.length() < 1) throw new JS.Exn("not enough args to match");
- Object arg0 = args.elementAt(0);
+ public static Object stringSearch(Object o, Object arg0) throws JSExn {
String s = o.toString();
- RE re;
- if(arg0 instanceof JSRegexp)
- re = ((JSRegexp)arg0).re;
- else
- re = newRE(arg0.toString(),0);
+ RE re = arg0 instanceof JSRegexp ? ((JSRegexp)arg0).re : newRE(arg0.toString(),0);
REMatch match = re.getMatch(s);
- if(match == null) return new Integer(-1);
- return new Integer(match.getStartIndex());
+ return match == null ? N(-1) : N(match.getStartIndex());
}
- public static Object stringReplace(Object o, JSArray args) throws JS.Exn {
- if(args.length() < 2) throw new JS.Exn("not enough args to replace");
- Object arg0 = args.elementAt(0);
- Object arg1 = args.elementAt(1);
+ public static Object stringReplace(Object o, Object arg0, Object arg1) throws JSExn {
String s = o.toString();
RE re;
- JSCallable replaceFunc = null;
+ JS replaceFunc = null;
String replaceString = null;
JSRegexp regexp = null;
if(arg0 instanceof JSRegexp) {
} else {
re = newRE(arg0.toString(),0);
}
- if(arg1 instanceof JSCallable)
- replaceFunc = (JSCallable) arg1;
+ if(arg1 instanceof JS)
+ replaceFunc = (JS) arg1;
else
replaceString = arg1.toString();
REMatch[] matches;
sb.append(sa,pos,match.getStartIndex()-pos);
pos = match.getEndIndex();
if(replaceFunc != null) {
- // FEATURE: reintroduce
- throw new JS.Exn("stringReplace() with a replacement function is temporarily disabled");
- /*
- JSArray a = new JSArray();
- a.addElement(match.toString());
- if(regexp != null) {
- int n = re.getNumSubs();
- for(int j=1;j<=n;j++)
- a.addElement(match.toString(j));
+ int n = (regexp == null ? 0 : re.getNumSubs());
+ int numArgs = 3 + n;
+ Object[] rest = new Object[numArgs - 3];
+ Object a0 = match.toString();
+ Object a1 = null;
+ Object a2 = null;
+ for(int j=1;j<=n;j++)
+ switch(j) {
+ case 1: a1 = match.toString(j); break;
+ case 2: a2 = match.toString(j); break;
+ default: rest[j - 3] = match.toString(j); break;
+ }
+ switch(numArgs) {
+ case 3:
+ a1 = N(match.getStartIndex());
+ a2 = s;
+ break;
+ case 4:
+ a2 = N(match.getStartIndex());
+ rest[0] = s;
+ break;
+ default:
+ rest[rest.length - 2] = N(match.getStartIndex());
+ rest[rest.length - 1] = s;
}
- a.addElement(new Integer(match.getStartIndex()));
- a.addElement(s);
- Object ret = replaceFunc.call(a, null);
- sb.append(ret.toString());
- */
+
+ // note: can't perform pausing operations in here
+ sb.append((String)replaceFunc.call(a0, a1, a2, rest, numArgs));
+
} else {
sb.append(mySubstitute(match,replaceString,s));
}
}
- public static Object stringSplit(Object o,JSArray args) {
- String s = o.toString();
- if(args.length() < 1 || args.elementAt(0) == null || s.length() == 0) {
- JSArray ret = new JSArray();
- ret.addElement(s);
- return ret;
- }
- Object arg0 = args.elementAt(0);
-
- int limit = args.length() < 2 ? Integer.MAX_VALUE : JS.toInt(args.elementAt(1));
+ public static Object stringSplit(Object o, String s, Object arg0) {
+ // FIXME: reintroduce args.length() < 2 ? Integer.MAX_VALUE : JS.toInt(args.elementAt(1));
+ int limit = JS.toInt(arg0);
if(limit < 0) limit = Integer.MAX_VALUE;
if(limit == 0) return new JSArray();
return ret;
}
- public static RE newRE(String pattern, int flags) throws JS.Exn {
+ public static RE newRE(String pattern, int flags) throws JSExn {
try {
return new RE(pattern,flags,RESyntax.RE_SYNTAX_PERL5);
} catch(REException e) {
- throw new JS.Exn(e.toString());
+ throw new JSExn(e.toString());
}
}
- private static Boolean wrapBool(boolean b) {
- return b ? Boolean.TRUE : Boolean.FALSE;
- }
-
- private static Boolean wrapBool(int n) {
- return wrapBool(n != 0);
- }
-
public String typeName() { return "regexp"; }
}
import java.io.*;
import java.util.*;
-/** Implementation of a JavaScript JSScope */
-public class JSScope extends JSCallable {
- private JSScope parentJSScope;
+/** Implementation of a JavaScript Scope */
+public class JSScope extends JS {
+
+ private JSScope parentScope;
+
private static final Object NULL_PLACEHOLDER = new Object();
- public JSScope(JSScope parentJSScope) {
- if (parentJSScope == this) throw new Error("can't make a scope its own parent!");
- this.parentJSScope = parentJSScope;
- }
- public boolean isTransparent() { return false; }
+ public JSScope(JSScope parentScope) { this.parentScope = parentScope; }
public void declare(String s) { super.put(s, NULL_PLACEHOLDER); }
- public JSScope getParentJSScope() { return parentJSScope; }
- public boolean has(Object key) { return super.get(key) != null; }
+ public JSScope getParentScope() { return parentScope; }
public Object get(Object key) {
Object o = super.get(key);
if (o != null) return o == NULL_PLACEHOLDER ? null : o;
- else return parentJSScope == null ? null : parentJSScope.get(key);
+ else return parentScope == null ? null : parentScope.get(key);
}
+ public boolean has(Object key) { return super.get(key) != null; }
public void put(Object key, Object val) {
- if (parentJSScope != null && !has(key)) parentJSScope.put(key, val);
+ if (parentScope != null && !has(key)) parentScope.put(key, val);
else super.put(key, val == null ? NULL_PLACEHOLDER : val);
}
public static class Global extends JSScope {
private final static Double NaN = new Double(Double.NaN);
private final static Double POSITIVE_INFINITY = new Double(Double.POSITIVE_INFINITY);
-
- public Global(JSScope parent) { super(parent); }
+ public Global() { super(null); }
public Object get(Object key) {
//#switch(key)
case "NaN": return NaN;
case 0: {
//#switch(method)
case "stringFromCharCode":
- JSArray args = new JSArray();
- for(int i=0; i<nargs; i++) args.addElement(i==0?a0:i==1?a1:i==2?a2:rest[i-3]);
- return stringFromCharCode(args);
- default: break;
+ char buf[] = new char[nargs];
+ for(int i=0; i<nargs; i++) buf[i] = (char)(JS.toInt(i==0?a0:i==1?a1:i==2?a2:rest[i-3]) & 0xffff);
+ return new String(buf);
//#end
+ break;
}
case 1: {
//#switch(method)
case "parseInt": return parseInt(a0, N(0));
case "isNaN": { double d = toDouble(a0); return d == d ? F : T; }
case "isFinite": { double d = toDouble(a0); return (d == d && !Double.isInfinite(d)) ? T : F; }
- case "decodeURI": throw new JS.Exn("unimplemented");
- case "decodeURIComponent": throw new JS.Exn("unimplemented");
- case "encodeURI": throw new JS.Exn("unimplemented");
- case "encodeURIComponent": throw new JS.Exn("unimplemented");
- case "escape": throw new JS.Exn("unimplemented");
- case "unescape": throw new JS.Exn("unimplemented");
- default: break;
+ case "decodeURI": throw new JSExn("unimplemented");
+ case "decodeURIComponent": throw new JSExn("unimplemented");
+ case "encodeURI": throw new JSExn("unimplemented");
+ case "encodeURIComponent": throw new JSExn("unimplemented");
+ case "escape": throw new JSExn("unimplemented");
+ case "unescape": throw new JSExn("unimplemented");
//#end
+ break;
}
case 2: {
//#switch(method)
case "parseInt": return parseInt(a0, a1);
- default: break;
//#end
+ break;
}
}
return super.callMethod(method, a0, a1, a2, rest, nargs);
}
- private Object stringFromCharCode(JSArray args) {
- char buf[] = new char[args.length()];
- for(int i=0;i<args.length();i++) buf[i] = (char)(JS.toInt(args.elementAt(i)) & 0xffff);
- return new String(buf);
- }
-
private Object parseInt(Object arg, Object r) {
int radix = JS.toInt(r);
String s = (String)arg;
radix = 16;
} else {
radix = 8;
- if(length == start || Character.digit(s.charAt(start+1),8)==-1) return new Integer(0);
+ if(length == start || Character.digit(s.charAt(start+1),8)==-1) return JS.ZERO;
}
}
if(radix == 0) radix = 10;
// try the fast way first
try {
String s2 = start == 0 ? s : s.substring(start);
- return new Integer(sign*Integer.parseInt(s2,radix));
+ return JS.N(sign*Integer.parseInt(s2,radix));
} catch(NumberFormatException e) { }
// fall through to a slower but emca-compliant method
for(int i=start;i<length;i++) {
n = n*radix + digit;
if(n < 0) return NaN; // overflow;
}
- if(n <= Integer.MAX_VALUE) return new Integer(sign*(int)n);
- return new Long((long)sign*n);
+ if(n <= Integer.MAX_VALUE) return JS.N(sign*(int)n);
+ return JS.N((long)sign*n);
}
private Object parseFloat(Object arg) {
// trailing garbage
while(start < end) {
try {
- return new Double(s.substring(start,length));
+ return JS.N(s.substring(start,length));
} catch(NumberFormatException e) { }
end--;
}
+++ /dev/null
-// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
-package org.xwt.js;
-
-import java.util.*;
-import org.xwt.util.*;
-import java.io.*;
-
-/**
- * This class encapsulates a single trap placed on a given node. The
- * traps for a given property name on a given box are maintained as a
- * linked list stack, with the most recently placed trap at the head
- * of the list.
- */
-public class JSTrap {
-
- JSTrappable trapee = null; ///< the box on which this trap was placed
- JSFunction f = null; ///< the function for this trap
- JSTrap next = null; ///< the next trap down the trap stack
- Object name = null; ///< the property that the trap was placed on
-
- private JSTrap(JSTrappable b, String n, JSFunction f, JSTrap nx) { trapee = b; name = n; this.f = f; this.next = nx; }
-
- /** adds a trap, avoiding duplicates */
- public static void addTrap(JSTrappable trapee, Object name, JSFunction f) {
- for(JSTrap t = trapee.getTrap(name); t != null; t = t.next) if (t.f == f) return;
- trapee.putTrap(name, new JSTrap(trapee, name.toString(), f, (JSTrap)trapee.getTrap(name)));
- }
-
- /** deletes a trap, if present */
- public static void delTrap(JSTrappable trapee, Object name, JSFunction f) {
- JSTrap t = (JSTrap)trapee.getTrap(name);
- if (t == null) return;
- if (t.f == f) { trapee.putTrap(t.name, t.next); return; }
- for(; t.next != null; t = t.next) if (t.next.f == f) { t.next = t.next.next; return; }
- }
-
- /** objects onto which traps may be placed */
- public static interface JSTrappable {
- public abstract JSTrap getTrap(Object key);
- public abstract void putTrap(Object key, JSTrap trap);
- public abstract void putAndTriggerJSTraps(Object key, Object value);
- }
-
- static class JSTrapScope extends JSScope {
- JSTrap t;
- Object val = null;
- boolean cascadeHappened = false;
- public JSTrapScope(JSScope parent, JSTrap t, Object val) { super(parent); this.t = t; this.val = val; }
- public Object get(Object key) {
- if (key.equals("trapee")) return t.trapee;
- if (key.equals("trapname")) return t.name;
- return super.get(key);
- }
- }
-}
-
// Token Subtype Handlers /////////////////////////////////////////////////////////
+ // FIXME: convert to a string switch
private int getKeyword(String s) throws IOException {
char c;
switch (s.length()) {
}
}
- if (!isInteger) this.number = new Double(dval);
- else if (Byte.MIN_VALUE <= longval && longval <= Byte.MAX_VALUE) this.number = new Byte((byte)longval);
- else if (Short.MIN_VALUE <= longval && longval <= Short.MAX_VALUE) this.number = new Short((short)longval);
- else if (Integer.MIN_VALUE <= longval && longval <= Integer.MAX_VALUE) this.number = new Integer((int)longval);
- else this.number = new Double(longval);
+ if (!isInteger) this.number = JS.N(dval);
+ else this.number = JS.N(longval);
return NUMBER;
}
accumulator.append((char)lastread);
}
public String getString() throws IOException {
- String ret = accumulator.toString();
+ String ret = accumulator.toString().intern();
accumulator = null;
return ret;
}