private static Cache cache = new Cache();
private static GIF gif = new GIF();
- // FIXME: return a Picture that gets filled in later
/** turns a resource into a Picture.Source and passes it to the callback */
public static Picture fromRes(final Res r, final Callback callback) {
Picture ret = (Picture)cache.get(r);
if (ret != null) return ret;
- try {
- Platform.inputStreamToByteArray(r.getInputStream(), new Callback() { public Object call(Object o) {
+ if (callback != null)
+ new java.lang.Thread() { public void run() {
try {
- Picture ret = null;
- byte[] b = (byte[])o;
- InputStream pbis = new ByteArrayInputStream(b);
- if ((b[0] & 0xff) == 'G') ret = gif.fromInputStream(pbis, r.getDescriptiveName());
- else if ((b[0] & 0xff) == 137) ret = new PNG().fromInputStream(pbis, r.getDescriptiveName());
- else if ((b[0] & 0xff) == 0xff) ret = Platform.decodeJPEG(pbis, r.getDescriptiveName());
- else throw new JS.Exn("couldn't figure out image type from first byte");
- ret.res = r;
- cache.put(r, ret);
- callback.call(ret);
- } catch (Exception e) {
+ final byte[] b = InputStreamToByteArray.convert(r.getInputStream());
+ Scheduler.add(new Scheduler.Task() { public void perform() {
+ try {
+ Picture ret = null;
+ InputStream pbis = new ByteArrayInputStream(b);
+ if ((b[0] & 0xff) == 'G') ret = gif.fromInputStream(pbis, r.getDescriptiveName());
+ else if ((b[0] & 0xff) == 137) ret = new PNG().fromInputStream(pbis, r.getDescriptiveName());
+ else if ((b[0] & 0xff) == 0xff) ret = Platform.decodeJPEG(pbis, r.getDescriptiveName());
+ else throw new JS.Exn("couldn't figure out image type from first byte");
+ ret.res = r;
+ cache.put(r, ret);
+ callback.call(ret);
+ } catch (Exception e) {
+ Log.log(Picture.class, e);
+ } } });
+ } catch (IOException e) {
Log.log(Picture.class, e);
+ return;
}
- return null;
- }});
- } catch (Exception e) {
- Log.log(Picture.class, e);
- }
+ } }.start();
return null;
}
}
public abstract void drawPicture(Picture source, int dx1, int dy1, int cx1, int cy1, int cx2, int cy2);
/** fill a trapezoid whose top and bottom edges are horizontal */
- public abstract void fillTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, int color);
+ public abstract void fillJSTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, int color);
/**
* Same as drawPicture, but only uses the alpha channel of the Picture, and is allowed to destructively modify the RGB
*/
public abstract void drawPictureAlphaOnly(Picture source, int dx1, int dy1, int cx1, int cy1, int cx2, int cy2, int rgb);
- // FIXME: we want floats (inter-pixel spacing) for antialiasing, but this hoses the fastpath line drawing... argh!
+ // FEATURE: we want floats (inter-pixel spacing) for antialiasing, but this hoses the fastpath line drawing... argh!
/** draws a line of width <tt>w</tt>; note that the coordinates here are <i>post-transform</i> */
public void drawLine(int x1, int y1, int x2, int y2, int w, int color, boolean capped) {
if (y1 > y2) { int t = x1; x1 = x2; x2 = t; t = y1; y1 = y2; y2 = t; }
if (x1 == x2) {
- fillTrapezoid(x1 - w / 2, x2 + w / 2, y1 - (capped ? w / 2 : 0), x1 - w / 2, x2 + w / 2, y2 + (capped ? w / 2 : 0), color);
+ fillJSTrapezoid(x1 - w / 2, x2 + w / 2, y1 - (capped ? w / 2 : 0), x1 - w / 2, x2 + w / 2, y2 + (capped ? w / 2 : 0), color);
return;
}
int last_x = x1;
for(int y=y1; y<=y2; y++) {
int new_x = (int)((float)(y - y1) / slope) + x1;
- if (slope >= 0) fillTrapezoid(last_x + 1, y != y2 ? new_x + 1 : new_x, y,
+ if (slope >= 0) fillJSTrapezoid(last_x + 1, y != y2 ? new_x + 1 : new_x, y,
last_x + 1, y != y2 ? new_x + 1 : new_x, y + 1, color);
- else fillTrapezoid(y != y2 ? new_x : new_x + 1, last_x, y,
+ else fillJSTrapezoid(y != y2 ? new_x : new_x + 1, last_x, y,
y != y2 ? new_x : new_x + 1, last_x, y + 1, color);
last_x = new_x;
}
y2 += width * Math.sin(phi);
}
- fillTrapezoid(x1 + dx, x1 + dx, y1 - dy, x1 - dx, x1 - dx + slice, y1 + dy, color); // top corner
- fillTrapezoid(x2 + dx - slice, x2 + dx, y2 - dy, x2 - dx, x2 - dx, y2 + dy, color); // bottom corner
- fillTrapezoid(x1 - dx, x1 - dx + slice, y1 + dy, x2 + dx - slice, x2 + dx, y2 - dy, color); // middle
+ fillJSTrapezoid(x1 + dx, x1 + dx, y1 - dy, x1 - dx, x1 - dx + slice, y1 + dy, color); // top corner
+ fillJSTrapezoid(x2 + dx - slice, x2 + dx, y2 - dy, x2 - dx, x2 - dx, y2 + dy, color); // bottom corner
+ fillJSTrapezoid(x1 - dx, x1 - dx + slice, y1 + dy, x2 + dx - slice, x2 + dx, y2 - dy, color); // middle
}
}
/** displays a platform-specific "open file" dialog and returns the chosen filename, or null if the user hit cancel */
protected String _fileDialog(String suggestedFileName, boolean write) { return null; }
- public static String fileDialog(String suggestedFileName, boolean write) {
- // FIXME: put self in background
+ public static void fileDialog(String suggestedFileName, boolean write) throws JS.Exn {
return platform._fileDialog(suggestedFileName, write);
}
Surface ret = platform._createSurface(b, framed);
ret.setInvisible(false);
- Object titlebar = b.get("titlebar", true);
- if (titlebar != null) ret.setTitleBarText(titlebar.toString());
-
- Object icon = b.get("icon", true);
- if (icon != null && icon instanceof Res) {
- /*
- FIXME
- Picture pic = Picture.fromRes((Res)icon);
- if (pic != null) ret.setIcon(pic);
- else if (Log.on) Log.log(Platform.class, "unable to load icon " + icon);
- */
- }
-
ret.setLimits(b.minwidth, b.minheight, b.maxwidth, b.maxheight);
if (refreshable) {
protected Scheduler _getScheduler() { return new Scheduler(); }
public static Scheduler getScheduler() { return platform._getScheduler(); }
- /** read an input stream into a byte array and invoke callback when ready */
- protected void _inputStreamToByteArray(final InputStream is, final Callback c) {
- new java.lang.Thread() {
- public void run() {
- try {
- final byte[] b = InputStreamToByteArray.convert(is);
- Scheduler.add(new Scheduler.Task() { public void perform() { c.call(b); }});
- } catch (IOException e) {
- Log.log(Platform.class, e);
- }
- }
- }.start();
- }
- public static void inputStreamToByteArray(InputStream is, Callback c) { platform._inputStreamToByteArray(is, c); }
-
public static void running() { platform._running(); }
public void _running() { new Semaphore().block(); }
}
import org.xwt.util.*;
import org.bouncycastle.util.encoders.Base64;
-/** base class for XWT resources */
+
+/** Base class for XWT resources */
public abstract class Res extends JS {
- public abstract String getDescriptiveName();
+
+ // Public Static //////////////////////////////////////////////////////////////////////
+
+ // FIXME: move to XWT.load()?
+ public static Res fromString(String url) {
+ if (url.startsWith("http://")) return new HTTP(url);
+ else if (url.startsWith("https://")) return new HTTP(url);
+ else if (url.startsWith("data:")) return new ByteArray(Base64.decode(url.substring(5)));
+ else if (url.startsWith("utf8:")) return new ByteArray(url.substring(5).getBytes());
+ throw new JS.Exn("invalid resource specifier " + url);
+ }
+
+
+ // Base Class //////////////////////////////////////////////////////////////////////
+
public String typeName() { return "resource"; }
- /** cache of subresources so that the equality operator works on them */
+ /** so that we get the same subresource each time */
private Hash refCache = null;
+ /** FIXME: needed? good idea? */
public Template t = null;
- public Res getParent() { return null; }
-
- /** an InputStream that makes sure it is not in the MessageQueue when blocked on a read */
- // FIXME
- private static class BackgroundInputStream extends FilterInputStream {
- BackgroundInputStream(InputStream i) { super(i); }
- /*
- private void suspend() throws IOException {
- if (!ThreadMessage.suspendThread())
- throw new IOException("attempt to perform background-only operation in a foreground thread");
- }
- private void resume() {
- ThreadMessage.resumeThread();
- }
- public int read() throws IOException {
- suspend();
- try { return super.read(); }
- finally { resume(); }
- }
- public int read(byte[] b, int off, int len) throws IOException {
- suspend();
- try { return super.read(b, off, len); }
- finally { resume(); }
- }
- */
- }
-
- /** returns an InputStream containing the Resource's contents */
- public final InputStream getInputStream() throws IOException { return new BackgroundInputStream(getInputStream("")); }
+ public final InputStream getInputStream() throws IOException { return getInputStream(""); }
public abstract InputStream getInputStream(String path) throws IOException;
- /** graft newResource in place of this resource on its parent */
- public Res graft(Object newResource) { throw new JS.Exn("cannot graft onto this resource"); }
-
- /** if the path of this resource does not end with extension, return a new one wit it appended */
public Res addExtension(String extension) { return new Ref(this, extension); }
- public Object[] keys() { throw new JS.Exn("cannot enumerate a resource"); }
- public Object put(Object key, Object val) { throw new JS.Exn("cannot put to a resource"); }
public Object get(Object key) {
if ("".equals(key)) {
Template t = Template.getTemplate(addExtension(".xwt"));
return ret;
}
- public static Res stringToRes(String url) {
- if (url.indexOf('!') != -1) {
- Res ret = new Zip(stringToRes(url.substring(0, url.lastIndexOf('!'))));
- String subpath = url.substring(url.lastIndexOf('!') + 1);
- if (subpath.length() > 0) ret = (Res)ret.get(subpath);
- return ret;
- }
- if (url.startsWith("http://")) return new HTTP(url);
- if (url.startsWith("https://")) return new HTTP(url);
- if (url.startsWith("cab:")) return new Cab(stringToRes(url.substring(4)));
- if (url.startsWith("data:")) return new ByteArray(Base64.decode(url.substring(5)));
- if (url.startsWith("utf8:")) return new ByteArray(url.substring(5).getBytes());
- throw new JS.Exn("invalid resource specifier " + url);
- }
+
+ // Caching //////////////////////////////////////////////////////////////////////
+
+ private static class NotCacheableException extends Exception { }
+ private static NotCacheableException notCacheable = new NotCacheableException();
+
+ /** if it makes sense to cache a resource, the resource must return a unique key */
+ public String getCacheKey() throws NotCacheableException { throw notCacheable; }
+
+ // FIXME: general cleanup
/** subclass from this if you want a CachedInputStream for each path */
public static class CachedRes extends Res {
private Res parent;
private boolean disk = false;
-
- // FIXME: security concern here
- private String subdir = null;
-
- public String getDescriptiveName() { return parent.getDescriptiveName(); }
+ public String getCacheKey() throws NotCacheableException { return parent.getCacheKey(); }
private Hash cachedInputStreams = new Hash();
- public CachedRes(Res parent, String subdir, boolean disk) {
- this.parent = parent; this.disk = disk; this.subdir = subdir;
- }
+ public CachedRes(Res p, String s, boolean d) { this.parent = p; this.disk = d; }
public InputStream getInputStream(String path) throws IOException {
CachedInputStream cis = (CachedInputStream)cachedInputStreams.get(path);
if (cis == null) {
java.io.File f = null;
if (disk) {
- // FIXME ugly
- // FIXME need separate hash for disk/nondisk
f = new java.io.File(System.getProperty("user.home") +
java.io.File.separatorChar + ".xwt" +
java.io.File.separatorChar + "caches" +
- java.io.File.separatorChar + subdir +
java.io.File.separatorChar +
- new String(Base64.encode(parent.getDescriptiveName().getBytes())));
+ new String(Base64.encode(parent.getCacheKey().getBytes())));
Log.log(this, "caching resource in " + f);
new java.io.File(f.getParent()).mkdirs();
if (f.exists()) return new FileInputStream(f);
}
}
+
+ // Useful Subclasses //////////////////////////////////////////////////////////////////////
+
/** HTTP or HTTPS resource */
public static class HTTP extends Res {
private String url;
- HTTP(String url) { this.url = url; }
- public String getDescriptiveName() { return url; }
- public InputStream getInputStream(String path) throws IOException {
- return new org.xwt.HTTP(url + path).GET(); }
+ HTTP(String url) { while (url.endsWith('/')) url = url.substring(0, url.length() - 1); this.url = url; }
+ public String getCacheKey() throws NotCacheableException { return url; }
+ public InputStream getInputStream(String path) throws IOException { return new org.xwt.HTTP(url + path).GET(); }
}
/** byte arrays */
public static class ByteArray extends Res {
private byte[] bytes;
- ByteArray(byte[] bytes) { this.bytes = bytes; }
- public String getDescriptiveName() { return "byte[]"; }
+ private String cacheKey = null;
+ 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");
return new ByteArrayInputStream(bytes);
/** a file */
public static class File extends Res {
private String path;
- File(String path) { this.path = path; }
- public String getDescriptiveName() { return "file://" + path; }
+ File(String path) {
+ while (path.endsWith(java.io.File.separatorChar)) path = path.substring(0, path.length() - 1);
+ this.path = path;
+ }
+ public String getCacheKey() throws NotCacheableException { throw notCacheable; } // already on the disk!
public InputStream getInputStream(String rest) throws IOException {
return new FileInputStream((path + rest).replace('/', java.io.File.separatorChar)); }
}
public static class Zip extends Res {
private Res parent;
Zip(Res parent) { this.parent = parent; }
- public String getDescriptiveName() { return parent.getDescriptiveName() + "!"; }
+ public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "!zip:"; }
public InputStream getInputStream(String path) throws IOException {
if (path.startsWith("/")) path = path.substring(1);
InputStream pis = parent.getInputStream();
public static class Cab extends Res {
private Res parent;
Cab(Res parent) { this.parent = parent; }
- public String getDescriptiveName() { return "cab[" + parent.getDescriptiveName() + "]"; }
+ public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "!cab:"; }
public InputStream getInputStream(String path) throws IOException {
- // FIXME: knownlength
if (path.startsWith("/")) path = path.substring(1);
return new org.xwt.translators.MSPack(parent.getInputStream()).getInputStream(path);
}
/** the Builtin resource */
public static class Builtin extends Res {
public Builtin() { };
- public String getDescriptiveName() { return "[builtin]"; }
+ public String getCacheKey() throws NotCacheableException { throw notCacheable; } // not cacheable
public InputStream getInputStream(String path) throws IOException {
if (!path.equals("")) throw new IOException("the builtin resource has no subresources");
return Platform.getBuiltinInputStream();
Res parent;
Object key;
Ref(Res parent, Object key) { this.parent = parent; this.key = key; }
- public String getDescriptiveName() {
- String pdn = parent.getDescriptiveName();
- if (pdn.equals("")) return key.toString();
- if (!pdn.endsWith("!")) pdn += ".";
- return pdn + key.toString();
- }
+ public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "/" + key; }
public Res addExtension(String extension) {
- return (key instanceof String && ((String)key).endsWith(extension)) ? this : new Ref(parent, key + extension);
- }
- public InputStream getInputStream(String path) throws IOException {
- return parent.getInputStream("/" + key + path);
- }
- public Res getParent() { return parent; }
- public Res graft(Object newResource) { return new Graft(parent, key, newResource); }
- }
-
- // FEATURE: eliminate code duplication with JS.Graft
- /** shadow resource which replaces the graft */
- public static class Graft extends Res {
- Res graftee;
- Object replaced_key;
- Object replaced_val;
- Graft(Res graftee, Object key, Object val) { this.graftee = 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 InputStream getInputStream(String s) throws IOException { return graftee.getInputStream(s); }
- public Object get(Object key) { return replaced_key.equals(key) ? replaced_val : graftee.get(key); }
- public String getDescriptiveName() { return graftee.getDescriptiveName(); }
- public Res getParent() { return graftee.getParent(); }
- public Object callMethod(Object method, Array args, boolean checkOnly) throws JS.Exn {
- if (!replaced_key.equals(method)) return graftee.callMethod(method, args, checkOnly);
- if (replaced_val instanceof Callable) return checkOnly ? Boolean.TRUE : ((Callable)replaced_val).call(args);
- if (checkOnly) return Boolean.FALSE;
- throw new JS.Exn("attempt to call non-function");
- }
- public Number coerceToNumber() { return graftee.coerceToNumber(); }
- public String coerceToString() { return graftee.coerceToString(); }
- public boolean coerceToBoolean() { return graftee.coerceToBoolean(); }
- public String typeName() { return graftee.typeName(); }
+ return ((String)key).endsWith(extension) ? this : new Ref(parent, key + extension); }
+ public InputStream getInputStream(String path) throws IOException { return parent.getInputStream("/" + key + path); }
}
/** shadow resource which replaces the graft */
public static class ProgressWatcher extends Res {
final Res watchee;
- Function callback;
- ProgressWatcher(Res watchee, Function callback) { this.watchee = watchee; this.callback = callback; }
- public String getDescriptiveName() { return watchee.getDescriptiveName(); }
+ JSFunction callback;
+ ProgressWatcher(Res watchee, JSFunction callback) { this.watchee = watchee; this.callback = callback; }
+ public String getCacheKey() throws NotCacheableException { return watchee.getCacheKey(); }
public InputStream getInputStream(String s) throws IOException {
final InputStream is = watchee.getInputStream(s);
return new FilterInputStream(is) {
int ret = super.read(b, off, len);
if (ret != 1) bytesDownloaded += ret;
Scheduler.add(new Scheduler.Task() { public void perform() {
- JS.Array args = new JS.Array();
+ JSArray args = new JSArray();
args.addElement(new Integer(bytesDownloaded));
args.addElement(new Integer(is instanceof KnownLength ? ((KnownLength)is).getLength() : 0));
- // FIXME
- //new JS.Context(callback, null, args).resume();
+ callback.call(args);
} });
return ret;
}
};
}
}
-
- public Object callMethod(Object method, Array args, boolean checkOnly) throws JS.Exn {
- if (method.equals("getUTF")) {
- if (checkOnly) return Boolean.TRUE;
- if (args.length() != 0) return null;
- try {
- CharArrayWriter caw = new CharArrayWriter();
- InputStream is = getInputStream();
- BufferedReader r = new BufferedReader(new InputStreamReader(is));
- char[] buf = new char[1024];
- while(true) {
- int numread = r.read(buf, 0, 1024);
- if (numread == -1) break;
- caw.write(buf, 0, numread);
- }
- return caw.toString();
- } catch (IOException e) {
- if (Log.on) Log.log(Res.class, "IO Exception while reading from file");
- if (Log.on) Log.log(Res.class, e);
- throw new JS.Exn("error while reading from Resource");
- }
- } else if (method.equals("getDOM")) {
- if (checkOnly) return Boolean.TRUE;
- if (args.length() != 0) return null;
- return new XMLHelper().doParse();
- }
- if (checkOnly) return Boolean.FALSE;
- return null;
- }
-
- private class XMLHelper extends XML {
- Vector obStack = new Vector();
- public XMLHelper() { super(BUFFER_SIZE); }
- public void startElement(XML.Element c) throws XML.SchemaException {
- JS o = new JS.Obj();
- o.put("$name", c.localName);
- for(int i=0; i<c.len; i++) o.put(c.keys[i], c.vals[i]);
- o.put("$numchildren", new Integer(0));
- obStack.addElement(o);
- }
- public void endElement(XML.Element c) throws XML.SchemaException {
- if (obStack.size() == 1) return;
- JS me = (JS)obStack.lastElement();
- obStack.setSize(obStack.size() - 1);
- JS parent = (JS)obStack.lastElement();
- int numchildren = ((Integer)parent.get("$numchildren")).intValue();
- parent.put("$numchildren", new Integer(numchildren + 1));
- parent.put(new Integer(numchildren), me);
- }
- public void characters(char[] ch, int start, int length) throws XML.SchemaException {
- String s = new String(ch, start, length);
- JS parent = (JS)obStack.lastElement();
- int numchildren = ((Integer)parent.get("$numchildren")).intValue();
- Object lastChild = parent.get(new Integer(numchildren - 1));
- if (lastChild instanceof String) {
- parent.put(new Integer(numchildren - 1), lastChild + s);
- } else {
- parent.put("$numchildren", new Integer(numchildren + 1));
- parent.put(new Integer(numchildren), s);
- }
- }
- public void whitespace(char[] ch, int start, int length) {}
- public JS doParse() throws JS.Exn {
- try {
- InputStream is = getInputStream();
- BufferedReader r = new BufferedReader(new InputStreamReader(is));
- parse(r);
- } catch (XML.XMLException e) {
- throw new JS.Exn("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");
- }
- return obStack.size() >= 1 ? (JS)obStack.elementAt(0) : null;
- }
- }
-
- public void writeTo(OutputStream os) throws IOException {
- InputStream is = getInputStream();
- byte[] buf = new byte[1024];
- while(true) {
- int numread = is.read(buf, 0, 1024);
- if (numread == -1) break;
- if (Log.on) Log.log(this, "wrote " + numread + " bytes");
- os.write(buf, 0, numread);
- }
- os.flush();
-
- // we have to close this because flush() doesn't work on Win32-GCJ
- os.close();
- }
}
if (name.equals("SOAP-ENV:Fault")) fault = true;
// add a generic struct; we'll change this if our type is different
- objects.addElement(new JS.Obj());
+ objects.addElement(new JSObj());
for(int i=0; i<keys.length; i++) {
String key = keys[i];
} else if (value.endsWith("null")) {
objects.removeElementAt(objects.size() - 1);
objects.addElement(null);
- } else if (value.endsWith("arrayType") || value.endsWith("JS.Array") || key.endsWith("arrayType")) {
+ } else if (value.endsWith("arrayType") || value.endsWith("JSArray") || key.endsWith("arrayType")) {
objects.removeElementAt(objects.size() - 1);
- objects.addElement(new JS.Array());
+ objects.addElement(new JSArray());
}
}
}
if (objects.size() < 2) return;
// our parent "should" be an aggregate type -- add ourselves to it.
- if (parent != null && parent instanceof JS.Array) {
+ if (parent != null && parent instanceof JSArray) {
objects.removeElementAt(objects.size() - 1);
- ((JS.Array)parent).addElement(me);
+ ((JSArray)parent).addElement(me);
} else if (parent != null && parent instanceof JS) {
objects.removeElementAt(objects.size() - 1);
}
sb.append("</" + name + ">\r\n");
- } else if (o instanceof JS.Array) {
- JS.Array a = (JS.Array)o;
+ } else if (o instanceof JSArray) {
+ JSArray a = (JSArray)o;
sb.append(" <" + name + " SOAP-ENC:arrayType=\"xsd:ur-type[" + a.length() + "]\">");
for(int i=0; i<a.length(); i++) appendObject("item", a.elementAt(i), sb);
sb.append("</" + name + ">\r\n");
} else if (o instanceof JS) {
JS j = (JS)o;
sb.append(" <" + name + ">");
- Object[] ids = j.keys();
- for(int i=0; i<ids.length; i++) appendObject(ids[i].toString(), j.get(ids[i].toString()), sb);
+ Enumeration e = j.keys();
+ while(e.hasMoreElements()) {
+ Object key = e.nextElement();
+ appendObject(key, j.get(key), sb);
+ }
sb.append("</" + name + ">\r\n");
+
}
}
- protected String send(JS.Array args, HTTP http) throws JS.Exn, IOException {
+ protected String send(JSArray args, HTTP http) throws JS.Exn, IOException {
// build up the request
StringBuffer content = new StringBuffer();
content.append("SOAPAction: " + action + "\r\n\r\n");
content.append(nameSpace != null ? " xmlns=\"" + nameSpace + "\"" : "");
content.append(">\r\n");
if (args.length() > 0) {
- Object[] o = ((JS)args.elementAt(0)).keys();
- for(int i=0; i<o.length; i++)
- appendObject(o[i].toString(), ((JS)args.elementAt(0)).get(o[i].toString()), content);
+ Enumeration e = ((JS)args.elementAt(0)).keys();
+ while(e.hasMoreElements()) {
+ Object key = e.nextElement();
+ appendObject(key, ((JS)args.elementAt(0)).get(key), content);
+ }
}
content.append(" </" + methodname + "></SOAP-ENV:Body></SOAP-ENV:Envelope>\r\n");
return content.toString();
// Public API Exposed to org.xwt /////////////////////////////////////////////////
+ private static Scheduler singleton;
public static abstract class Task { public abstract void perform(); }
public static void add(Task t) { singleton.runnable.append(t); }
- public static void init() { singleton.run(); }
+ public static void init() {
+ if (singleton != null) return;
+ singleton = Platform.getScheduler();
+ singleton.run();
+ }
// API which must be supported by subclasses /////////////////////////////////////
- protected Scheduler() { }
-
/**
- * INVARIANT: all scheduler implementations MUST invoke
- * Surface.renderAll() after performing a Task if no tasks remain
- * in the queue. A scheduler may choose to invoke
- * Surface.renderAll() more often than that if it so chooses.
+ * INVARIANT: all scheduler implementations MUST invoke Surface.renderAll() after performing a Task if no tasks remain
+ * in the queue. A scheduler may choose to invoke Surface.renderAll() more often than that if it so chooses.
*/
public void run() { defaultRun(); }
+ protected Scheduler() { }
- // Default Implementation /////////////////////////////////////
+ // Default Implementation //////////////////////////////////////////////////////
protected static Queue runnable = new Queue(50);
- private static final Scheduler singleton = Platform.getScheduler();
public void defaultRun() {
while(true) {
Task t = (Task)runnable.remove(true);
try {
t.perform();
- // FIXME: be smarter about this
+ // FEATURE: be smarter about this
Surface.renderAll();
} catch (Exception e) {
Log.log(Scheduler.class, "Task threw an exception: " + e);
* Scheduler-time (the size/position/state at the time that the
* now-executing message was enqueued). This distinction is important.
*/
-// FIXME: put the scar box back in
public abstract class Surface extends PixelBuffer {
public int getWidth() { return root == null ? 0 : root.width; }
// Static Data ////////////////////////////////////////////////////////////////////////////////
- // FIXME
private abstract static class Message extends Scheduler.Task {
public abstract void perform();
public Object call(Object arg) { perform(); return null; }
final Box who = Box.whoIs(root, mousex, mousey);
Scheduler.add(new Message() { public void perform() {
Platform.clipboardReadEnabled = true;
- root.put("Press3", Boolean.TRUE);
+ root.putAndTriggerJSTraps("Press3", Boolean.TRUE);
Platform.clipboardReadEnabled = false;
}});
}
public KMessage(String k) { key = k; }
public void perform() {
if (key.equals("C-v") || key.equals("A-v")) Platform.clipboardReadEnabled = true;
- /* FIXME
outer: for(int i=0; i<keywatchers.size(); i++) {
Box b = (Box)keywatchers.elementAt(i);
- for(Box cur = b; cur != null; cur = cur.getParent())
- if ((cur.flags & cur.INVISIBLE_FLAG) != 0) continue outer;
- b.put("KeyPressed", key);
+ for(Box cur = b; cur != null; cur = cur.parent)
+ if (!cur.test(cur.VISIBLE)) continue outer;
+ b.putAndTriggerJSTraps("KeyPressed", key);
}
- */
Platform.clipboardReadEnabled = false;
}
}
+ Vec keywatchers = new Vec();
+
/** sends a KeyReleased message; subclasses should not add the C- or A- prefixes, nor should they capitalize alphabet characters */
protected final void KeyReleased(final String key) {
if (key == null) return;
else if (key.toLowerCase().equals("control")) control = false;
else if (key.toLowerCase().equals("shift")) shift = false;
Scheduler.add(new Message() { public void perform() {
- /* FIXME
outer: for(int i=0; i<keywatchers.size(); i++) {
Box b = (Box)keywatchers.elementAt(i);
- for(Box cur = b; cur != null; cur = cur.getParent())
- if ((cur.flags & cur.INVISIBLE_FLAG) != 0) continue outer;
- b.put("KeyReleased", key);
+ for(Box cur = b; cur != null; cur = cur.parent)
+ if (!cur.test(cur.VISIBLE)) continue outer;
+ b.putAndTriggerJSTraps("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.put("Move", Boolean.TRUE);
+ root.putAndTriggerJSTraps("Move", Boolean.TRUE);
root.Move(oldmousex, oldmousey, mousex, mousey);
if (!cursor.equals(oldcursor)) syncCursor();
protected final void SizeChange(final int width, final int height) {
Scheduler.add(new Message() { public void perform() {
if (width == root.width && height == root.height) return;
- root.needs_reflow = true;
+ root.set(REFLOW);
do { abort = false; root.reflow(width, height); } while(abort);
}});
abort = true;
Scheduler.add(new Message() { public void perform() {
root.x = x;
root.y = y;
- root.put("PosChange", Boolean.TRUE);
+ root.putAndTriggerJSTraps("PosChange", Boolean.TRUE);
}});
}
Refresh();
}
+ public static Surface fromBox(Box b) {
+ for(int i=0; i<allSurfaces.size(); i++) {
+ Surface s = (Surface)allSurfaces.elementAt(i);
+ if (s.root == b) return s;
+ }
+ return null;
+ }
+
public Surface(Box root) {
this.root = root;
- if (root.surface != null && root.surface.root == root) root.surface.dispose(false);
+ Surface old = fromBox(root);
+ if (old != null) old.dispose(false);
else root.remove();
- root.surface = this;
// make sure the root is properly sized
- do { abort = false; root.reflow(); } while(abort);
+ do { abort = false; root.reflow(root.width, root.height); } while(abort);
root.dirty();
Refresh();
// make sure the root is properly sized
do {
abort = false;
- root.reflow();
+ root.reflow(root.width, root.height);
setSize(root.width, root.height);
// update mouseinside and trigger Enter/Leave as a result of box size/position changes
String oldcursor = cursor;
if (!cursor.equals(oldcursor)) syncCursor();
} while(abort);
- Box.sizePosChangesSinceLastRender = 0;
+ //Box.sizePosChangesSinceLastRender = 0;
int[][] dirt = dirtyRegions.flush();
for(int i = 0; dirt != null && i < dirt.length; i++) {
if (dirt[i] == null) continue;
Scheduler.add(this);
}
- public void perform() { boxContainingMouse.put(name, value); }
+ public void perform() { boxContainingMouse.putAndTriggerJSTraps(name, value); }
public String toString() { return "SimpleMessage [name=" + name + ", value=" + value + "]"; }
}
backbuffer.drawPictureAlphaOnly(source, dx, dy, cx1, cy1, cx2, cy2, argb);
}
- public void fillTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, int color) {
+ public void fillJSTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, int color) {
screenDirtyRegions.dirty(Math.min(x1, x3), y1, Math.max(x2, x4) - Math.min(x1, x3), y2 - y1);
- backbuffer.fillTrapezoid(x1, x2, y1, x3, x4, y2, color); }
+ backbuffer.fillJSTrapezoid(x1, x2, y1, x3, x4, y2, color); }
public void render() {
super.render();
// Instance Members ///////////////////////////////////////////////////////
- String id = null; ///< the id of this box
- String redirect = null; ///< the id of the redirect target; only meaningful on a root node
- private String[] keys; ///< keys to be "put" to instances of this template; elements correspond to those of vals
- private Object[] vals; ///< values to be "put" to instances of this template; elements correspond to those of keys
- private Vec children = new Vec(); ///< during XML parsing, this holds the list of currently-parsed children; null otherwise
- private int numunits = -1; ///< see numUnits(); -1 means that this value has not yet been computed
+ String id = null; ///< the id of this box
+ String redirect = null; ///< the id of the redirect target; only meaningful on a root node
+ private String[] keys; ///< keys to be "put" to instances of this template; elements correspond to those of vals
+ private Object[] vals; ///< values to be "put" to instances of this template; elements correspond to those of keys
+ private Vec children = new Vec(); ///< during XML parsing, this holds the list of currently-parsed children; null otherwise
+ private int numunits = -1; ///< see numUnits(); -1 means that this value has not yet been computed
- private Function script = null; ///< the script on this node
- private String fileName = "unknown"; ///< the filename this node came from; used only for debugging
- private Vec preapply = new Vec(); ///< templates that should be preapplied (in the order of application)
+ private JSFunction script = null; ///< the script on this node
+ private String fileName = "unknown"; ///< the filename this node came from; used only for debugging
+ private Vec preapply = new Vec(); ///< templates that should be preapplied (in the order of application)
// Instance Members that are only meaningful on root Template //////////////////////////////////////
- private JS.Scope staticScope = null; ///< the scope in which the static block is executed
- private Function staticscript = null; ///< the script on the static node of this template, null already performed
+ private JSScope staticJSScope = null; ///< the scope in which the static block is executed
+ private JSFunction staticscript = null; ///< the script on the static node of this template, null already performed
// Only used during parsing /////////////////////////////////////////////////////////////////
- private StringBuffer content = null; ///< during XML parsing, this holds partially-read character data; null otherwise
- private int content_start = 0; ///< line number of the first line of <tt>content</tt>
- private int content_lines = 0; ///< number of lines in <tt>content</tt>
- private int startLine = -1; ///< the line number that this element starts on
- private final Res r; ///< the resource we came from
+ private StringBuffer content = null; ///< during XML parsing, this holds partially-read character data; null otherwise
+ private int content_start = 0; ///< line number of the first line of <tt>content</tt>
+ private int content_lines = 0; ///< number of lines in <tt>content</tt>
+ private int startLine = -1; ///< the line number that this element starts on
+ private final Res r; ///< the resource we came from
// Static data/methods ///////////////////////////////////////////////////////////////////
public static Res resolveStringToResource(String str, XWT xwt, boolean permitAbsolute) {
// URL
if (str.indexOf("://") != -1) {
- if (permitAbsolute) return Res.stringToRes(str);
+ if (permitAbsolute) return Res.fromString(str);
Log.log(Template.class, "absolute URL " + str + " not permitted here");
return null;
}
private Template(Res r) { this.r = r; }
/** called before this template is applied or its static object can be externally referenced */
- JS.Scope getStatic() {
- if (staticScope == null) staticScope = new JS.Scope(null);
- if (staticscript == null) return staticScope;
- Function temp = staticscript;
+ JSScope getStatic() {
+ if (staticJSScope == null) staticJSScope = new JSScope(null);
+ if (staticscript == null) return staticJSScope;
+ JSFunction temp = staticscript;
staticscript = null;
- new JS.Context(temp, staticScope).resume();
- return staticScope;
+ JSContext.invoke(temp.cloneWithNewParentJSSCope(staticJSScope));
+ return staticJSScope;
}
/** Applies the template to Box b
* @param pboxes a vector of all box parents on which to put $-references
* @param ptemplates a vector of the fileNames to recieve private references on the pboxes
*/
- void apply(Box b, JS.Callable c, XWT xwt) { apply(b, c, xwt, null); }
- void apply(Box b, JS.Callable callback, XWT xwt, PerInstantiationScope parentPis) {
+ void apply(Box b, XWT xwt) { apply(b, xwt, null); }
+ void apply(Box b, XWT xwt, PerInstantiationJSScope parentPis) {
getStatic();
for(int i=0; i<preapply.size(); i++) {
Template t = getTemplate(resolveStringToResource((String)preapply.elementAt(i), xwt, false));
if (t == null) throw new RuntimeException("unable to resolve resource " + preapply.elementAt(i));
- t.apply(b, callback, xwt);
+ t.apply(b, xwt);
}
- PerInstantiationScope pis = new PerInstantiationScope(b, xwt, parentPis, staticScope);
+ PerInstantiationJSScope pis = new PerInstantiationJSScope(b, xwt, parentPis, staticJSScope);
for (int i=0; children != null && i<children.size(); i++) {
- Box kid = new Box();
- ((Template)children.elementAt(i)).apply(kid, callback, xwt, pis);
-
- // FIXME: tailcall?
- b.put(b.numChildren(), kid);
+ Box kid = new BoxTree();
+ ((Template)children.elementAt(i)).apply(kid, xwt, pis);
+ b.putAndTriggerTraps(b.numChildren(), kid);
}
- if (script != null) new JS.Context(script, pis).resume();
+ if (script != null) JSContext.invoke(script.cloneWithNewParentJSScope(pis));
for(int i=0; keys != null && i<keys.length; i++)
- if (vals[i] instanceof String && ((String)vals[i]).charAt(0) == '$') b.put(keys[i], pis.get(vals[i]));
- else if ("image".equals(keys[i])) b.put("image", resolveStringToResource((String)vals[i], xwt, true));
- else if (keys[i] != null) b.put(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]);
}
if (nameOfHeaderNodeBeingProcessed != null) throw new XML.SchemaException("can't nest header nodes");
nameOfHeaderNodeBeingProcessed = c.localName;
if (c.localName.equals("doc")) {
- // FIXME: ignore
+ // FEATURE
} else if (c.localName.equals("static")) {
if (t.staticscript != null)
throw new XML.SchemaException("the <static> header node may not appear more than once");
}
}
- private Function parseScript(boolean isstatic) {
- Function thisscript = null;
+ private JSFunction parseScript(boolean isstatic) {
+ JSFunction thisscript = null;
try {
thisscript = JS.parse(t.fileName + (isstatic ? "._" : ""), t.content_start, new StringReader(t.content.toString()));
} catch (IOException ioe) {
public void whitespace(char[] ch, int start, int length) throws XML.SchemaException { }
}
- private static class PerInstantiationScope extends JS.Scope {
+ private static class PerInstantiationJSScope extends JSScope {
XWT xwt = null;
- PerInstantiationScope parentBoxPis = null;
- JS.Scope myStatic = null;
+ PerInstantiationJSScope parentBoxPis = null;
+ JSScope myStatic = null;
void putDollar(String key, Box target) {
if (parentBoxPis != null) parentBoxPis.putDollar(key, target);
declare("$" + key);
put("$" + key, target);
}
- public PerInstantiationScope(Scope parentScope, XWT xwt, PerInstantiationScope parentBoxPis, JS.Scope myStatic) {
- super(parentScope);
+ public PerInstantiationJSScope(JSScope parentJSScope, XWT xwt, PerInstantiationJSScope parentBoxPis, JSScope myStatic) {
+ super(parentJSScope);
this.parentBoxPis = parentBoxPis;
this.xwt = xwt;
this.myStatic = myStatic;
if (key.equals("static")) return myStatic;
return super.get(key);
}
- public Object put(Object key, Object val) {
- if (super.has(key)) return super.put(key, val);
- return super.put(key, val);
+ public void put(Object key, Object val) {
+ if (super.has(key)) super.put(key, val);
+ else super.put(key, val);
}
}
+++ /dev/null
-// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
-package org.xwt;
-
-import java.util.*;
-import org.xwt.js.*;
-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 Trap {
-
- // Static Data //////////////////////////////////////////////////////////////
-
- private static Function cascadeHelper = null;
- private static String cascadeHelperText =
- "return function(q) { var ret = arguments.doTrap;" +
- "if (ret != false && !arguments.didCascade) arguments.cascade = q; };";
- static {
- try {
- cascadeHelper = JS.parse("cascadeHelper", 1, new StringReader(cascadeHelperText));
- cascadeHelper = (Function)new JS.Context(cascadeHelper, null).resume();
- } catch (Exception e) {
- Log.log(Trap.class, e);
- }
- }
-
- /** List of properties that cannot be trapped */
- private static final Hash PROHIBITED = new Hash(120, 3);
-
- static {
- String[] p = new String[] {
- "shrink", "hshrink", "vshrink", "x", "y",
- "width", "height", "flex", "colspan", "rowspan", "cols",
- "rows", "align", "invisible", "absolute", "globalx",
- "globaly", "minwidth", "minheight", "height", "width",
- "maxwidth", "maxheight", "numchildren", "hpad", "vpad",
- "buffered", "cursor", "mousex", "mousey",
- "mouseinside", "thisbox", "indexof", "path", "font", "fontsize"
- };
- for(int i=0; i<p.length; i++) PROHIBITED.put(p[i], Boolean.TRUE);
- };
-
-
- // Instance Members ////////////////////////////////////////////////////////
-
- /** the box on which this trap was placed */
- private Box trapee = null;
-
- /** the function for this trap */
- Function f = null;
-
- /** the next trap down the trap stack */
- private Trap next = null;
-
- /** the property that the trap was placed on */
- private Object name = null;
-
-
- // Static Methods //////////////////////////////////////////////////////////////////////////
-
- /**
- * adds a trap.
- * @param trapee the box to place the trap on
- * @param name the name of the property to trap on
- * @param f the function to place as a trap
- */
- static void addTrap(Box trapee, Object name, Function f) {
-
- // check if this script has already placed a trap on this property
- for(Trap t = (Trap)trapee.get(name, Trap.class); t != null; t = t.next)
- if (t.f == f) return;
-
- // actually place the trap
- trapee.put2(name, Trap.class, new Trap(trapee, name.toString(), f, (Trap)trapee.get(name, Trap.class)));
- }
-
-
- /**
- * deletes a trap.
- * @param trapee the box to remove the trap from
- * @param name the name of the property to trap on
- * @param f the function to remove
- */
- static void delTrap(Box trapee, Object name, Function f) {
- Trap t = (Trap)trapee.get(name, Trap.class);
- if (t.f == f) { trapee.put2(name, Trap.class, t.next); return; }
- for(; t.next != null; t = t.next)
- if (t.next.f == f) { t.next = t.next.next; return; }
- Log.logJS("warning: tried to remove a trap that had not been placed");
- }
-
-
- // Instance Methods //////////////////////////////////////////////////////////////////////////
-
- private Trap(Box b, String n, Function f, Trap nx)
- { trapee = b; name = n; this.f = f; this.next = nx; }
-
- // Read Traps //////////////////////////////////////////////////////////////////////
-
- public Object perform() {
- if (f.getNumFormalArgs() > 0) return cascade();
- else return new JS.TailCall().set(f, new TrapArgs(this));
- }
-
- public Object cascade() {
- if (next != null) return next.perform();
- else return trapee.get(name, true);
- }
-
- // Write Traps //////////////////////////////////////////////////////////////////////
-
- public Object perform(Object val) {
- if (f.getNumFormalArgs() == 0) return cascade(val);
- else return new JS.TailCall().set(cascadeHelper, new TrapArgs(this, val));
- }
-
- public Object cascade(Object val) {
- if (next != null) return next.perform(val);
- else return trapee.put(name, val, true);
- }
-
- // Args ///////////////////////////////////////////////////////////////////////////
-
- private static class TrapArgs extends JS.Array {
- private Trap t;
- public boolean cascadeHappened = false;
- public TrapArgs(Trap t) { this.t = t; }
- public TrapArgs(Trap t, Object value) { this.t = t; addElement(value); }
-
- public Object put(Object key, Object val) {
- if (key.equals("cascade")) { cascadeHappened = true; return t.cascade(val); }
- else return super.put(key, val);
- }
-
- public Object get(Object key) {
- // common case
- if(!(key instanceof String)) return super.get(key);
- if (key.equals("trapee")) return t.trapee;
- if (key.equals("doTrap")) return new JS.TailCall().set(t.f, this);
- if (key.equals("didCascade")) return cascadeHappened ? Boolean.TRUE : Boolean.FALSE;
- if (key.equals("trapname")) return t.name;
- if (key.equals("cascade")) return t.cascade();
- if (key.equals("callee")) return t.f;
- return super.get(key);
- }
- }
-}
-
// Public entry points /////////////////////////////////////////////////////////////////
public static VectorPath parseVectorPath(String s) {
+ if (s == null) return null;
PathTokenizer t = new PathTokenizer(s);
VectorPath ret = new VectorPath();
char last_command = 'M';
// PathTokenizer //////////////////////////////////////////////////////////////////////////////
+ public static Affine parseTransform(String t) {
+ if (t == null) return null;
+ t = t.trim();
+ Affine ret = VectorGraphics.Affine.identity();
+ while (t.length() > 0) {
+ if (t.startsWith("skewX(")) {
+ // FIXME
+
+ } else if (t.startsWith("shear(")) {
+ // FIXME: nonstandard; remove this
+ ret.multiply(VectorGraphics.Affine.shear(Float.parseFloat(t.substring(t.indexOf('(') + 1, t.indexOf(')')))));
+
+ } else if (t.startsWith("skewY(")) {
+ // FIXME
+
+ } else if (t.startsWith("rotate(")) {
+ String sub = t.substring(t.indexOf('(') + 1, t.indexOf(')'));
+ if (sub.indexOf(',') != -1) {
+ float angle = Float.parseFloat(sub.substring(0, sub.indexOf(',')));
+ sub = sub.substring(sub.indexOf(',') + 1);
+ float cx = Float.parseFloat(sub.substring(0, sub.indexOf(',')));
+ sub = sub.substring(sub.indexOf(',') + 1);
+ float cy = Float.parseFloat(sub);
+ ret.multiply(VectorGraphics.Affine.translate(cx, cy));
+ ret.multiply(VectorGraphics.Affine.rotate(angle));
+ ret.multiply(VectorGraphics.Affine.translate(-1 * cx, -1 * cy));
+ } else {
+ ret.multiply(VectorGraphics.Affine.rotate(Float.parseFloat(t.substring(t.indexOf('(') + 1, t.indexOf(')')))));
+ }
+
+ } else if (t.startsWith("translate(")) {
+ String sub = t.substring(t.indexOf('(') + 1, t.indexOf(')'));
+ if (sub.indexOf(',') > -1) {
+ ret.multiply(VectorGraphics.Affine.translate(Float.parseFloat(t.substring(t.indexOf('(') + 1, t.indexOf(','))),
+ Float.parseFloat(t.substring(t.indexOf(',') + 1, t.indexOf(')')))));
+ } else {
+ ret.multiply(VectorGraphics.Affine.translate(Float.parseFloat(t.substring(t.indexOf('(') + 1, t.indexOf(','))), 0));
+ }
+
+ } else if (t.startsWith("flip(")) {
+ String which = t.substring(t.indexOf('(') + 1, t.indexOf(')'));
+ ret.multiply(VectorGraphics.Affine.flip(which.equals("horizontal"), which.equals("vertical")));
+
+ } else if (t.startsWith("scale(")) {
+ String sub = t.substring(t.indexOf('(') + 1, t.indexOf(')'));
+ if (sub.indexOf(',') > -1) {
+ ret.multiply(VectorGraphics.Affine.scale(Float.parseFloat(t.substring(t.indexOf('(') + 1, t.indexOf(','))),
+ Float.parseFloat(t.substring(t.indexOf(',') + 1, t.indexOf(')')))));
+ } else {
+ ret.multiply(VectorGraphics.Affine.scale(Float.parseFloat(t.substring(t.indexOf('(') + 1, t.indexOf(','))),
+ Float.parseFloat(t.substring(t.indexOf('(') + 1, t.indexOf(',')))));
+ }
+
+ } else if (t.startsWith("matrix(")) {
+ // FIXME: is this mapped right?
+ float d[] = new float[6];
+ StringTokenizer st = new StringTokenizer(t, ",", false);
+ for(int i=0; i<6; i++)
+ d[i] = Float.parseFloat(st.nextToken());
+ ret.multiply(new VectorGraphics.Affine(d[0], d[1], d[2], d[3], d[4], d[5]));
+ }
+ t = t.substring(t.indexOf(')') + 1).trim();
+ }
+ return ret;
+ }
+
public static final float PX_PER_INCH = 72;
public static final float INCHES_PER_CM = (float)0.3937;
public static final float INCHES_PER_MM = INCHES_PER_CM / 10;
if (leftSegment == rightSegment || rightSegment == Integer.MAX_VALUE) break;
if (leftSegment != -1)
if ((useEvenOdd && count % 2 != 0) || (!useEvenOdd && count != 0))
- paint.fillTrapezoid(intercept(edges[leftSegment], y0, true, true),
+ paint.fillJSTrapezoid(intercept(edges[leftSegment], y0, true, true),
intercept(edges[rightSegment], y0, true, true), y0,
intercept(edges[leftSegment], y1, true, true),
intercept(edges[rightSegment], y1, true, true), y1,
public static interface Paint {
public abstract void
- fillTrapezoid(int tx1, int tx2, int ty1, int tx3, int tx4, int ty2, PixelBuffer buf);
+ fillJSTrapezoid(int tx1, int tx2, int ty1, int tx3, int tx4, int ty2, PixelBuffer buf);
}
public static class SingleColorPaint implements Paint {
int color;
public SingleColorPaint(int color) { this.color = color; }
- public void fillTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, PixelBuffer buf) {
- buf.fillTrapezoid(x1, x2, y1, x3, x4, y2, color);
+ public void fillJSTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, PixelBuffer buf) {
+ buf.fillJSTrapezoid(x1, x2, y1, x3, x4, y2, color);
}
}
int[] stop_colors;
float[] stop_offsets;
- public void fillTrapezoid(float tx1, float tx2, float ty1, float tx3, float tx4, float ty2, PixelBuffer buf) {
+ public void fillJSTrapezoid(float tx1, float tx2, float ty1, float tx3, float tx4, float ty2, PixelBuffer buf) {
Affine a = buf.a;
Affine inverse = a.copy().invert();
float slope1 = (tx3 - tx1) / (ty2 - ty1);
* convert.
* </ol>
*/
-class XMLRPC extends JS.Callable {
-
- public Object[] keys() { throw new Error("not implemented"); }
+class XMLRPC extends JSCallable {
/** the url to connect to */
protected String url = null;
*
* If an <array> tag is encountered, a null is pushed onto the
* stack. When a </data> is encountered, we search back on the
- * stack to the last null, replace it with a NativeJS.Array, and
+ * stack to the last null, replace it with a NativeJSArray, and
* insert into it all elements above it on the stack.
*
* If a <struct> tag is encountered, a JSObject is pushed
public void startElement(XML.Element c) {
content.reset();
if (c.localName.equals("fault")) fault = true;
- else if (c.localName.equals("struct")) objects.setElementAt(new JS.Obj(), objects.size() - 1);
+ else if (c.localName.equals("struct")) objects.setElementAt(new JSObj(), objects.size() - 1);
else if (c.localName.equals("array")) objects.setElementAt(null, objects.size() - 1);
else if (c.localName.equals("value")) objects.addElement("");
}
if (i > 0) s = s.substring(i);
try {
- org.xwt.js.Date nd = new org.xwt.js.Date();
- double date = org.xwt.js.Date.date_msecFromDate(Double.valueOf(s.substring(0, 4)).doubleValue(),
+ JSDate nd = new JSDate();
+ double date = JSDate.date_msecFromDate(Double.valueOf(s.substring(0, 4)).doubleValue(),
Double.valueOf(s.substring(4, 6)).doubleValue() - 1,
Double.valueOf(s.substring(6, 8)).doubleValue(),
Double.valueOf(s.substring(9, 11)).doubleValue(),
Double.valueOf(s.substring(15, 17)).doubleValue(),
(double)0
);
- nd.jsFunction_setTime(org.xwt.js.Date.internalUTC(date));
+ nd.jsJSFunction_setTime(JSDate.internalUTC(date));
objects.setElementAt(nd, objects.size() - 1);
} catch (Exception e) {
} else if (c.localName.equals("data")) {
int i;
for(i=objects.size() - 1; objects.elementAt(i) != null; i--);
- JS.Array arr = new JS.Array();
+ JSArray arr = new JSArray();
for(int j = i + 1; j<objects.size(); j++) arr.put(new Integer(j - i - 1), objects.elementAt(j));
objects.setElementAt(arr, i);
objects.setSize(i + 1);
}
sb.append("</string></value>\n");
- } else if (o instanceof org.xwt.js.Date) {
+ } else if (o instanceof JSDate) {
sb.append(" <value><dateTime.iso8601>");
- java.util.Date d = new java.util.Date(((org.xwt.js.Date)o).getRawTime());
+ java.util.Date d = new java.util.Date(((JSDate)o).getRawTime());
sb.append(d.getYear() + 1900);
if (d.getMonth() + 1 < 10) sb.append('0');
sb.append(d.getMonth() + 1);
sb.append(d.getSeconds());
sb.append("</dateTime.iso8601></value>\n");
- } else if (o instanceof JS.Array) {
+ } else if (o instanceof JSArray) {
if (tracker.get(o) != null) throw new JS.Exn("attempted to send multi-ref data structure via XML-RPC");
tracker.put(o, Boolean.TRUE);
sb.append(" <value><array><data>\n");
- JS.Array a = (JS.Array)o;
+ JSArray a = (JSArray)o;
for(int i=0; i<a.length(); i++) appendObject(a.elementAt(i), sb);
sb.append(" </data></array></value>\n");
tracker.put(o, Boolean.TRUE);
JS j = (JS)o;
sb.append(" <value><struct>\n");
- Object[] ids = j.keys();
- for(int i=0; i<ids.length; i++) {
- sb.append(" <member><name>" + ids[i] + "</name>\n");
- appendObject(j.get(ids[i].toString()), sb);
+ Enumeration e = j.keys();
+ while(e.hasMoreElements()) {
+ Object key = e.nextElement();
+ sb.append(" <member><name>" + key + "</name>\n");
+ appendObject(j.get(key), sb);
sb.append(" </member>\n");
}
sb.append(" </struct></value>\n");
}
}
- // this is synchronized in case multiple threads try to make a call on the same object... in the future, change this
- // behavior to use pipelining.
- public synchronized Object call2(JS.Array args) throws JS.Exn, IOException {
+ public Object call_(JSArray args) throws JS.Exn, IOException {
if (Log.verbose) Log.log(this, "call to " + url + " : " + methodname);
if (tracker == null) tracker = new Hash();
if (objects == null) objects = new Vec();
else objects.setSize(0);
- String content = send(args, http);
+ final String content = send(args, http);
if (Log.verbose) {
String s;
BufferedReader br2 = new BufferedReader(new StringReader(content));
return ret;
}
});
- return recieve(br);
+ return null;
} finally {
is.close();
}
}
- protected String send(JS.Array args, HTTP http) throws JS.Exn, IOException {
+ protected String send(JSArray args, HTTP http) throws JS.Exn, IOException {
StringBuffer content = new StringBuffer();
content.append("\r\n");
content.append("<?xml version=\"1.0\"?>\n");
return objects.elementAt(0);
}
- public final Object call(final JS.Array args) throws JS.Exn {
- final JS.Context cx = JS.Context.current();
- Scheduler.add(new Scheduler.Task() { public void perform() {
- Object ret;
- try {
- ret = call2(args);
- } catch (IOException se) {
- if (Log.on) Log.log(this, se);
- throw new JS.Exn("socket exception: " + se);
- }
- cx.resume(ret);
- } });
- return JS.Context.pause;
+ 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;
}
/** 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 JS.Obj {
+public final class XWT extends JSCallable {
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 {
+ String key;
+ Sub(String key) { this.key = key; }
+ public void put(Object key, Object val) { XWT.this.put(this.key + "." + key, val); }
+ public Object get(Object key) { return XWT.this.get(this.key + "." + key); }
+ public Object call(Object method, JSArray args) { return XWT.call(method == null ? key : this.key + "." + method, args); }
+ }
+
public Object get(Object name) {
//#switch(name)
case "math": return xwtMath;
case "string": return xwtString;
- case "date": return new org.xwt.js.Date();
+ case "date": return new JSDate();
case "origin": return Main.origin;
- case "box": return new Box();
+ case "box": return new BoxTree();
+ case "ui": return sub("ui");
+ case "ui.key": return sub("ui.key");
case "ui.key.alt": return Surface.alt ? Boolean.TRUE : Boolean.FALSE;
case "ui.key.control": return Surface.control ? Boolean.TRUE : Boolean.FALSE;
case "ui.key.shift": return Surface.shift ? Boolean.TRUE : Boolean.FALSE;
case "ui.clipboard": return Platform.getClipBoard();
case "ui.maxdim": return new Integer(Short.MAX_VALUE);
- case "ui.key.alt.name": return Platform.altKeyName();
+ case "ui.key.name": return sub("ui.key.name");
+ case "ui.key.name.alt": return Platform.altKeyName();
+ case "ui.screen": return sub("ui.screen");
case "ui.screen.width": return new Integer(Platform.getScreenWidth());
case "ui.screen.height": return new Integer(Platform.getScreenHeight());
case "fs.home": return System.getProperty("user.home");
case "fs.temp": return System.getProperty("java.io.tempdir");
+ case "ui.mouse": return sub("ui.mouse");
case "ui.mouse.button":
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);
+ case "undocumented": return sub("undocumented");
+ case "undocumented.internal": return sub("undocumented.internal");
//#end
return rr.get(name);
}
- public Object put(Object name, final Object value) {
+ public void put(Object name, final Object value) {
//#switch(name)
case "thread":
- Scheduler.add(new Scheduler.Task() { public void perform() { new JS.Context((Function)value, null).resume(); } });
- return null;
- case "ui.clipboard": Platform.setClipBoard((String)value); return null;
- case "ui.frame": Platform.createSurface((Box)value, true, true); return null;
- case "ui.window": Platform.createSurface((Box)value, false, true); return null;
+ Scheduler.add(new Scheduler.Task() { public void perform() { JSContext.newJSContext((JSFunction)value, null); } });
+ 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);
case "undocumented.internal.proxyAuthorization":
HTTP.Proxy.Authorization.authorization = value.toString();
HTTP.Proxy.Authorization.waitingForUser.release();
- return null;
//#end
- return super.put(name, value);
}
- public Object callMethod(Object name, boolean checkOnly, JS.Array args) throws JS.Exn {
+ public Object call(Object name, JSArray args) throws JS.Exn {
Object a = args.elementAt(0);
Object b = args.elementAt(1);
Object c = args.elementAt(2);
- if (name.equals("date")) return new org.xwt.js.Date(args);
- else if (args.length() == 0 && name.equals("thread.yield")) return checkOnly ? T : sleep(0);
+ if (name.equals("date")) return new org.xwt.js.JSDate(args);
+ else if (args.length() == 0 && name.equals("thread.yield")) { sleep(0); return null; }
else if (args.length() == 2) {
//#switch(name)
- case "res.watch": return checkOnly ? T : new Res.ProgressWatcher((Res)a, (Function)b);
- case "soap": return checkOnly ? T : new SOAP((String)args.elementAt(0), "", (String)args.elementAt(1), null);
- case "apply": if (checkOnly) return T;
- Template.getTemplate((Res)args.elementAt(1)).apply((Box)args.elementAt(0), null, XWT.this);
+ case "res.watch": return new Res.ProgressWatcher((Res)a, (JSFunction)b);
+ case "soap": return new SOAP((String)args.elementAt(0), "", (String)args.elementAt(1), null);
+ case "apply":
+ Template.getTemplate((Res)args.elementAt(1)).apply((Box)args.elementAt(0), XWT.this);
return args.elementAt(0);
//#end
} else if (args.length() == 3 && name.equals("soap")) {
if (name.equals("soap")) {
- if (checkOnly) return T;
return new SOAP((String)args.elementAt(0), "", (String)args.elementAt(1), (String)args.elementAt(2));
} else if (name.equals("graft")) {
- if (checkOnly) return T;
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)");
- // FIXME if (a instanceof Res) return new Res.Graft((Res)a, b, c);
+ if (a instanceof Res) return new Res.Graft((Res)a, b, c);
return new JS.Graft((JS)a, b, c);
}
} else if (args.length() == 1) {
//#switch(name)
- case "ui.browser": if (checkOnly) return T; Platform.newBrowserWindow((String)a); return null;
- case "clone": return checkOnly ? T : new XWT((Res)a);
- case "res.unzip": return checkOnly ? T : new Res.Zip((Res)a);
- case "res.uncab": return checkOnly ? T : new Res.Cab((Res)a);
- case "res.cache": return checkOnly ? T : new Res.CachedRes((Res)a, "resources", true);
- case "res.fromURL": return checkOnly ? T : Res.stringToRes((String)a);
- case "thread.sleep": return checkOnly ? T : sleep(JS.toInt(a));
- case "log.println": if (checkOnly) return T; Log.logJS(this, a== null ? "**null**" : a.toString()); return null;
- case "log.dump": if (checkOnly) return T; JS.recurse("", "", a); return null;
- case "regexp": return checkOnly ? T : new Regexp(a, null);
- case "rpc.xml": return checkOnly ? T : new XMLRPC((String)a, "");
- case "rpc.soap": return checkOnly ? T : new SOAP((String)a, "", null, null);
- case "crypto.rsa": /* FIXME */ break;
- case "crypto.md5": /* FIXME */ break;
- case "crypto.sha1": /* FIXME */ break;
- case "crypto.rc4": /* FIXME */ break;
- case "stream.parse.html": /* FIXME */ break;
- case "stream.parse.xml": /* FIXME */ break;
- case "stream.parse.utf8": /* FIXME */ break;
+ case "ui.browser": Platform.newBrowserWindow((String)a); return null;
+ case "clone": return new XWT((Res)a);
+ case "res.unzip": return new Res.Zip((Res)a);
+ case "res.uncab": return new Res.Cab((Res)a);
+ case "res.cache": return new Res.CachedRes((Res)a, "resources", true);
+ case "res.fromURL": return Res.fromString((String)a);
+ 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": JS.recurse("", "", a); return null;
+ case "regexp": return new JSRegexp(a, null);
+ case "rpc.xml": return new XMLRPC((String)a, "");
+ case "rpc.soap": return new SOAP((String)a, "", null, null);
+ case "crypto.rsa": /* FEATURE */ break;
+ case "crypto.md5": /* FEATURE */ break;
+ case "crypto.sha1": /* FEATURE */ break;
+ case "crypto.rc4": /* FEATURE */ break;
+ case "stream.parse.html": /* FEATURE */ break;
+ case "stream.parse.xml": /* FEATURE */ break;
+ case "stream.parse.utf8": /* FEATURE */ break;
//#end
}
- return super.callMethod(name, args, checkOnly);
+ return null;
}
- // FIXME
- public static Object sleep(final int i) {
- final JS.Context jsthread = JS.Context.current();
+ 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() {
- jsthread.resume();
- } }); } }.start();
- return JS.Context.pause;
+ new Thread() {
+ public void run() {
+ try { Thread.sleep(i); } catch (InterruptedException e) { }
+ Scheduler.add(new Scheduler.Task() { public void perform() { callback.call(null); } });
+ }
+ }.start();
}
- public static final org.xwt.js.Math xwtMath = new org.xwt.js.Math() {
- private JS gs = new JS.GlobalScope();
+ public static final JSMath xwtMath = new JSMath() {
+ private JS gs = new JSScope.Global(null);
public Object get(Object key) {
//#switch(key)
case "isNaN": return gs.get("isNaN");
}
};
- public static final JS.Obj xwtString = new JS.Obj(true) {
- private JS gs = new JS.GlobalScope();
+ public static final JSObj xwtString = new JSObj() {
+ private JS gs = new JSScope.Global(null);
+ public void put(Object key, Object val) { }
public Object get(Object key) {
//#switch(key)
case "parseInt": return gs.get("parseInt");
return super.get(key);
}
};
+
+ private class XMLHelper extends XML {
+ Vector obStack = new Vector();
+ public XMLHelper() { super(BUFFER_SIZE); }
+ public void startElement(XML.Element c) throws XML.SchemaException {
+ JS o = new JSObj();
+ o.put("$name", c.localName, null);
+ for(int i=0; i<c.len; i++) o.put(c.keys[i], c.vals[i]);
+ o.put("$numchildren", new Integer(0), null);
+ obStack.addElement(o);
+ }
+ public void endElement(XML.Element c) throws XML.SchemaException {
+ if (obStack.size() == 1) return;
+ JS me = (JS)obStack.lastElement();
+ obStack.setSize(obStack.size() - 1);
+ JS parent = (JS)obStack.lastElement();
+ int numchildren = ((Integer)parent.get("$numchildren")).intValue();
+ parent.put("$numchildren", new Integer(numchildren + 1), null);
+ parent.put(new Integer(numchildren), me, null);
+ }
+ public void characters(char[] ch, int start, int length) throws XML.SchemaException {
+ String s = new String(ch, start, length);
+ JS parent = (JS)obStack.lastElement();
+ int numchildren = ((Integer)parent.get("$numchildren")).intValue();
+ Object lastChild = parent.get(new Integer(numchildren - 1));
+ if (lastChild instanceof String) {
+ parent.put(new Integer(numchildren - 1), lastChild + s, null);
+ } else {
+ parent.put("$numchildren", new Integer(numchildren + 1), null);
+ parent.put(new Integer(numchildren), s, null);
+ }
+ }
+ public void whitespace(char[] ch, int start, int length) {}
+ public JS doParse() throws JS.Exn {
+ try {
+ InputStream is = getInputStream();
+ BufferedReader r = new BufferedReader(new InputStreamReader(is));
+ parse(r);
+ } catch (XML.XMLException e) {
+ throw new JS.Exn("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");
+ }
+ return obStack.size() >= 1 ? (JS)obStack.elementAt(0) : null;
+ }
+ }
}
/** pop two elements; call stack[-n](stack[-n+1], stack[-n+2]...) where n is the number of args to the function */
public static final byte CALL = -15;
- /** pop an element; push a JS.JS.Array containing the keys of the popped element */
+ /** pop an element; push a JS.JSArray containing the keys of the popped element */
public static final byte PUSHKEYS = -16;
/** swap the top two elements on the stack */
public static final byte SWAP = -17;
- /** execute the ForthBlock pointed to by the literal in a fresh scope with parentScope==THIS */
+ /** execute the ForthBlock pointed to by the literal in a fresh scope with parentJSScope==THIS */
public static final byte NEWSCOPE = -18;
- /** execute the ForthBlock pointed to by the literal in a fresh scope with parentScope==THIS */
+ /** execute the ForthBlock pointed to by the literal in a fresh scope with parentJSScope==THIS */
public static final byte OLDSCOPE = -19;
/** push a copy of the top stack element */
/** finish a finally block and carry out whatever instruction initiated the finally block */
public static final byte FINALLY_DONE = -24;
- /** same as CALL, except that the function is on top of the arguments instead of beneath them */
- public static final byte CALL_REVERSED = -25;
-
public static final String[] bytecodeToString = new String[] {
"", "", "LITERAL", "ARRAY", "OBJECT", "NEWFUNCTION", "DECLARE", "TOPSCOPE",
"GET", "GET_PRESERVE", "PUT", "JT", "JF", "JMP", "POP", "CALL", "PUSHKEYS",
"SWAP", "NEWSCOPE", "OLDSCOPE", "DUP", "LABEL", "LOOP", "CALLMETHOD",
- "FINALLY_DONE", "CALL_REVERSED"
+ "FINALLY_DONE"
};
}
+++ /dev/null
-package org.xwt.js;
-
-class GlobalScopeImpl extends JS.Scope {
- private final static Double NaN = new Double(Double.NaN);
- private final static Double POSITIVE_INFINITY = new Double(Double.POSITIVE_INFINITY);
-
- public GlobalScopeImpl(JS.Scope parent) {
- super(parent);
- }
- public Object get(Object key) {
- if(key.equals("NaN")) return NaN;
- if(key.equals("Infinity")) return POSITIVE_INFINITY;
- if(key.equals("undefined")) return null;
- return super.get(key);
- }
- public Object callMethod(Object method, JS.Array args, boolean checkOnly) {
- if(method.equals("parseInt")) {
- if(checkOnly) return Boolean.TRUE;
- return parseInt(args);
- } else if(method.equals("parseFloat")) {
- if(checkOnly) return Boolean.TRUE;
- return parseFloat(args);
- } else if(method.equals("isNaN")) {
- if(checkOnly) return Boolean.TRUE;
- return isNaN(args);
- } else if(method.equals("isFinite")) {
- if(checkOnly) return Boolean.TRUE;
- return isFinite(args);
- } else if(method.equals("decodeURI")) {
- if(checkOnly) return Boolean.TRUE;
- return decodeURI(args);
- } else if(method.equals("decodeURIComponent")) {
- if(checkOnly) return Boolean.TRUE;
- return decodeURIComponent(args);
- } else if(method.equals("encodeURI")) {
- if(checkOnly) return Boolean.TRUE;
- return encodeURI(args);
- } else if(method.equals("encodeURIComponent")) {
- if(checkOnly) return Boolean.TRUE;
- return encodeURIComponent(args);
- } else if(method.equals("escape")) {
- if(checkOnly) return Boolean.TRUE;
- return escape(args);
- } else if(method.equals("unescape")) {
- if(checkOnly) return Boolean.TRUE;
- return unescape(args);
- } else if(method.equals("stringFromCharCode")) {
- if(checkOnly) return Boolean.TRUE;
- return stringFromCharCode(args);
- }
- return super.callMethod(method,args,checkOnly);
- }
- private Object stringFromCharCode(JS.Array 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(JS.Array args) {
- String s = args.length() > 0 ? args.elementAt(0).toString() : "";
- int radix = args.length() > 1 ? toInt(args.elementAt(1)) : 0;
- int start = 0;
- int length = s.length();
- int sign = 1;
- long n = 0;
- if(radix != 0 && (radix < 2 || radix > 36)) return NaN;
- while(start < length && Character.isWhitespace(s.charAt(start))) start++;
- if((length >= start+1) && (s.charAt(start) == '+' || s.charAt(start) == '-')) {
- sign = s.charAt(start) == '+' ? 1 : -1;
- start++;
- }
- if(radix == 0 && length >= start+1 && s.charAt(start) == '0') {
- start++;
- if(length >= start+1 && (s.charAt(start) == 'x' || s.charAt(start) == 'X')) {
- start++;
- radix = 16;
- } else {
- radix = 8;
- if(length == start || Character.digit(s.charAt(start+1),8)==-1) return new Integer(0);
- }
- }
- if(radix == 0) radix = 10;
- if(length == start || Character.digit(s.charAt(start),radix) == -1) return NaN;
- // try the fast way first
- try {
- String s2 = start == 0 ? s : s.substring(start);
- return new Integer(sign*Integer.parseInt(s2,radix));
- } catch(NumberFormatException e) { }
- // fall through to a slower but emca-compliant method
- for(int i=start;i<length;i++) {
- int digit = Character.digit(s.charAt(i),radix);
- if(digit < 0) break;
- 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);
- }
- private Object parseFloat(JS.Array args) {
- String s = args.length() > 0 ? args.elementAt(0).toString() : "";
- int start = 0;
- int length = s.length();
- while(start < length && Character.isWhitespace(s.charAt(0))) start++;
- int end = length;
- // as long as the string has no trailing garbage,this is fast, its slow with
- // trailing garbage
- while(start < end) {
- try {
- return new Double(s.substring(start,length));
- } catch(NumberFormatException e) { }
- end--;
- }
- return NaN;
- }
- private Object isNaN(JS.Array args) {
- double d = args.length() > 0 ? toDouble(args.elementAt(0)) : Double.NaN;
- return d == d ? Boolean.FALSE : Boolean.TRUE;
- }
- private Object isFinite(JS.Array args) {
- double d = args.length() > 0 ? toDouble(args.elementAt(0)) : Double.NaN;
- return (d == d && !Double.isInfinite(d)) ? Boolean.TRUE : Boolean.FALSE;
- }
- private Object decodeURI(JS.Array args) {
- throw new JS.Exn("decodeURI is unimplemented");
- }
- private Object decodeURIComponent(JS.Array args) {
- throw new JS.Exn("decodeURIComponent is unimplemented");
- }
- private Object encodeURI(JS.Array args) {
- throw new JS.Exn("encodeURI is unimplemented");
- }
- private Object encodeURIComponent(JS.Array args) {
- throw new JS.Exn("encodeURIComponent is unimplemented");
- }
- private Object escape(JS.Array args) {
- throw new JS.Exn("escape is unimplemented");
- }
- private Object unescape(JS.Array args) {
- throw new JS.Exn("unescape is unimplemented");
- }
-}
// Package Helper Methods //////////////////////////////////////////////////////////////
private static Object wrapInt(int i) { return new Integer(i); }
- static Object callMethodOnPrimitive(Object o, Object method, JS.Array args) {
+ static Object callMethodOnPrimitive(Object o, Object method, JSArray args) {
int alength = args.length();
String s;
if(o instanceof Number) {
return wrapInt(s.lastIndexOf(search,start));
}
if(method.equals("match")) {
- return Regexp.stringMatch(s,args);
+ return JSRegexp.stringMatch(s,args);
}
if(method.equals("replace")) {
- return Regexp.stringReplace(s,args);
+ return JSRegexp.stringReplace(s,args);
}
if(method.equals("search")) {
- return Regexp.stringSearch(s,args);
+ return JSRegexp.stringSearch(s,args);
}
if(method.equals("slice")) {
int a = alength >= 1 ? JS.toInt(args.elementAt(0)) : 0;
return s.substring(a,b);
}
if(method.equals("split")){
- return Regexp.stringSplit(s,args);
+ return JSRegexp.stringSplit(s,args);
}
if(method.equals("toLowerCase")) return s.toLowerCase();
if(method.equals("toUpperCase")) return s.toUpperCase();
}
static Object getFromPrimitive(Object o, Object key) {
- boolean returnCallable = false;
+ 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"))
- returnCallable = true;
+ returnJSCallable = true;
}
- if(!returnCallable) {
+ if(!returnJSCallable) {
// the string stuff applies to everything
String s = o.toString();
key.equals("seatch") || key.equals("slice") || key.equals("split") || key.equals("toLowerCase") ||
key.equals("toUpperCase") || key.equals("toString") || key.equals("substr")
)
- returnCallable = true;
+ returnJSCallable = true;
}
- if(returnCallable) {
+ if(returnJSCallable) {
final Object target = o;
final String method = key.toString();
- return new JS.Callable() {
- public Object call(JS.Array args) { return callMethodOnPrimitive(target,method,args); }
+ return new JSCallable() {
+ public Object call(JSArray args) { return callMethodOnPrimitive(target,method,args); }
};
}
return null;
}
- static class CallableStub extends JS.Callable {
+ static class JSCallableStub extends JSCallable {
private Object method;
- private JS.Obj obj;
- public CallableStub(JS.Obj obj, Object method) { this.obj = obj; this.method = method; }
- public Object call(JS.Array args) { return obj.callMethod(method,args,false); }
+ private JSObj obj;
+ public JSCallableStub(JSObj obj, Object method) { this.obj = obj; this.method = method; }
+ public Object call(JSArray args) { return ((JSCallable)obj).call(method,args); }
}
}
/**
* 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. The static
- * methods, fields, and inner classes of JS define the publicly
- * visible API for the XWT JavaScript engine; code outside this
- * package should never depend on anything not defined in this file.
+ * Object which can be manipulated by JavaScript code.
*/
public abstract class JS {
// Public Helper Methods //////////////////////////////////////////////////////////////////////
/** parse and compile a function */
- public static Function parse(String sourceName, int firstLine, Reader sourceCode) throws IOException {
- return new Function(sourceName, firstLine, sourceCode, null);
+ public static JSFunction parse(String sourceName, int firstLine, Reader sourceCode) throws IOException {
+ return new JSFunction(sourceName, firstLine, sourceCode, null);
}
/** coerce an object to a Boolean */
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");
}
-
+
/** coerce an object to a String */
public static String toString(Object o) {
if(o == null) return "null";
// Instance Methods ////////////////////////////////////////////////////////////////////
public abstract Object get(Object key) throws JS.Exn;
- public abstract Object put(Object key, Object val) throws JS.Exn;
- public abstract Object[] keys();
+ public void put(Object key, Object val) throws JS.Exn { throw new JS.Exn("you cannot put to this object"); }
+
+ public Enumeration keys() { throw new Error("you cannot apply for..in to a " + this.getClass().getName()); }
- public Object callMethod(Object method, Array args, boolean checkOnly) throws JS.Exn {
- if(checkOnly) return Boolean.FALSE;
- Object o = get(method);
- if(o instanceof JS.Callable) {
- return ((JS.Callable)o).call(args);
- } else if(o == null) {
- throw new JS.Exn("Attempted to call non-existent method: " + method);
- } else {
- throw new JS.Exn("Attempted to call a non-method: " +method);
- }
- }
-
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"); }
+
public String typeName() { return "object"; }
// Inner Classes /////////////////////////////////////////////////////////////////////////
- /** A sensible implementation of the abstract methods in the JS class */
- public static class Obj extends JS {
-
- // FIXME: move these to an interface so they're optional
- // 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;
-
- // FIXME: be smart here; perhaps intern
- 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 Boolean B(boolean b) { return b ? Boolean.TRUE : Boolean.FALSE; }
-
- private Hash entries = null;
- private boolean sealed = false;
- public Obj() { this(false); }
- public Obj(boolean sealed) { this.sealed = sealed; }
- public void setSeal(boolean sealed) { this.sealed = sealed; } ///< a sealed object cannot have its properties modified
- public Object put(Object key, Object val) { put2(key, null, val); return null; }
- protected void put2(Object key, Object key2, Object val) {
- if (sealed) return;
- if (entries == null) entries = new Hash();
- entries.put(key, key2, val); }
- public Object[] keys() { return entries == null ? new Object[0] : entries.keys(); }
- public Object get(Object key) { return get(key, null); }
- protected Object get(Object key, Object key2) {
- if (entries == null) return null;
- if(key2 == null && callMethod((String)key, null, true) == Boolean.TRUE)
- return new Internal.CallableStub(this, key);
- return entries.get(key, key2);
- }
- }
-
/** An exception which can be thrown and caught by JavaScript code */
public static class Exn extends RuntimeException {
private Object js = null;
public Object getObject() { return js; }
}
- /** The publicly-visible face of JavaScript Array objects */
- public static class Array extends ArrayImpl {
- public Array() { }
- public Array(int size) { super(size); }
- public void setSize(int i) { super.setSize(i); }
- public int length() { return super.length(); }
- public Object elementAt(int i) { return super.elementAt(i); }
- public void addElement(Object o) { super.addElement(o); }
- public void setElementAt(Object o, int i) { super.setElementAt(o, i); }
- public Object get(Object key) { return super._get(key); }
- public Object put(Object key, Object val) { super._put(key, val); return null; }
- }
-
- /** Any object which becomes part of the scope chain must support this interface */
- public static class Scope extends ScopeImpl {
- public Scope(Scope parentScope) { this(parentScope, false); }
- public Scope(Scope parentScope, boolean sealed) { super(parentScope, sealed); }
- public boolean has(Object key) { return super.has(key); }
- public Object get(Object key) { return super._get(key); }
- public Object put(Object key, Object val) { super._put(key, val); return null; }
- public void declare(String s) { super.declare(s); }
- }
-
/** the result of a graft */
- public static class Graft extends JS {
+ public static class Graft extends JSCallable {
private JS graftee;
private Object replaced_key;
private Object replaced_val;
- public Graft(JS graftee, Object key, Object val) {
- if (graftee instanceof Array) throw new JS.Exn("can't graft onto Arrays (yet)");
- if (graftee instanceof Callable) throw new JS.Exn("can't graft onto Callables (yet)");
- if (graftee instanceof Scope) throw new JS.Exn("can't graft onto Scopes (yet)");
- this.graftee = graftee;
+ 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 Object put(Object key, Object val) { graftee.put(key, val); return null; }
- public Object callMethod(Object method, Array args, boolean checkOnly) throws JS.Exn {
- if (!replaced_key.equals(method)) return graftee.callMethod(method, args, checkOnly);
- if (replaced_val instanceof Callable) return checkOnly ? Boolean.TRUE : ((Callable)replaced_val).call(args);
- if (checkOnly) return Boolean.FALSE;
- throw new JS.Exn("attempt to call non-function");
+ public void put(Object key, Object val) { graftee.put(key, val); }
+ public void call(Object method, JSArray args) {
+ if (replaced_key.equals(method)) return ((JSCallable)replaced_val).call(null, args);
+ else if (graftee instanceof JSCallable) return 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 Object[] keys() {
- Object[] ret = graftee.keys();
- for(int i=0; i<ret.length; i++) if (replaced_key.equals(ret[i])) return ret;
- Object[] ret2 = new Object[ret.length + 1];
- System.arraycopy(ret, 0, ret2, 0, ret.length);
- ret2[ret2.length - 1] = replaced_key;
- return ret2;
- }
- }
-
- /** anything that is callable with the () operator and wasn't compiled from JS code */
- public static abstract class Callable extends JS.Obj {
- public abstract Object call(JS.Array args) throws JS.Exn;
- }
-
- /** a scope that is populated with js objects and functions normally found in the global scope */
- public static class GlobalScope extends GlobalScopeImpl {
- public GlobalScope() { this(null); }
- public GlobalScope(JS.Scope parent) { super(parent); }
- }
-
- public static class TailCall {
- Function func = null;
- JS.Array args = null;
- public TailCall() { }
- public TailCall set(Function func) { this.func = func; this.args = new JS.Array(); return this; }
- public TailCall set(Function func, JS.Array args) { this.func = func; this.args = args; return this; }
- }
-
- /** encapsulates the state of a JavaScript "thread" (ie stack) */
- public static class Context {
-
-
- // Statics //////////////////////////////////////////////////////////////////////
-
- /**
- * Return this from call/get/put in order to make the interpreter pause. The interpreter will expect a value
- * (the return from the call or the get) to be pushed onto the stack when it is resumed.
- */
- public static Object pause = new Object();
-
- private int getLine_() { return current().f == null ? -1 : (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 */
- public static JS.Context current() { return (JS.Context)threadToContext.get(Thread.currentThread()); }
- private static Hashtable threadToContext = new Hashtable();
-
-
- // Instance members and methods //////////////////////////////////////////////////////////////////////
-
- /** the currently-executing Function */
- Function f = null;
-
- /** the currently-executing scope */
- public Scope scope = null; // FIXME: do we really need this? the function should contain this info
-
- /** the object stack */
- Vec stack = new Vec();
-
- /** the program counter */
- int pc = 0;
-
- public Context(Function function, Scope scope) {
- if (scope == null) scope = new Scope(function.parentScope);
- stack.push(new Function.CallMarker(this));
- stack.push(new JS.Array());
- this.f = function;
- this.scope = scope;
- }
-
- public Object resume() { return resume(null); }
- public Object resume(Object o) {
- Thread t = Thread.currentThread();
- Context old = (Context)threadToContext.get(t);
- try {
- threadToContext.put(t, this);
- stack.push(o);
- return Function.eval(this);
- } finally {
- if (old == null) threadToContext.remove(t);
- else threadToContext.put(t, old);
- }
- }
-
- }
-
- public static void recurse(String indent, String name, Object o) {
- if (!name.equals("")) name += " : ";
-
- if (o == null) {
- Log.logJS(indent + name + "<null>");
-
- } else if (o instanceof JS.Array) {
- Log.logJS(indent + name + "<array>");
- JS.Array na = (JS.Array)o;
- for(int i=0; i<na.length(); i++)
- recurse(indent + " ", i + "", na.elementAt(i));
-
- } else if (o instanceof JS) {
- Log.logJS(indent + name + "<object>");
- JS s = (JS)o;
- Object[] keys = s.keys();
- for(int i=0; i<keys.length; i++)
- if (keys[i] != null)
- recurse(indent + " ", keys[i].toString(),
- (keys[i] instanceof Integer) ?
- s.get(((Integer)keys[i])) : s.get(keys[i].toString()));
-
- } else {
- Log.logJS(indent + name + o);
-
+ 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;
+ }
+ }
+ };
}
}
-
}
// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
-
package org.xwt.js;
+
import org.xwt.util.*;
import java.io.*;
import java.util.*;
-/** A JavaScript Array */
-class ArrayImpl extends JS.Obj {
+/** A JavaScript JSArray */
+public class JSArray extends JSCallable {
private Vec vec = new Vec();
- public ArrayImpl() { }
- public ArrayImpl(int size) { vec.setSize(size); }
+ public JSArray() { }
+ public JSArray(int size) { vec.setSize(size); }
private static int intVal(Object o) {
if (o instanceof Number) {
int intVal = ((Number)o).intValue();
return Integer.parseInt(s);
}
- public Object callMethod(Object method, JS.Array args,boolean justChecking) {
+ public Object call(Object method, JSArray args) {
if(method.equals("push")) {
- if(justChecking) return Boolean.TRUE;
for(int i=0;i<args.length();i++)
vec.push(args.elementAt(i));
return new Integer(vec.size());
}
if(method.equals("pop")) {
- if(justChecking) return Boolean.TRUE;
return vec.pop(); // this'll return null on size()==0
}
if(method.equals("shift")) {
- if(justChecking) return Boolean.TRUE;
if(length() > 0) {
Object o = vec.elementAt(0);
vec.removeElementAt(0);
}
}
if(method.equals("unshift")) {
- if(justChecking) return Boolean.TRUE;
// FEATURE: could be optimized a bit with some help from Vec
for(int i=0;i<args.length();i++)
vec.insertElementAt(args.elementAt(i),i);
return new Integer(vec.size());
}
- if(method.equals("slice")) return justChecking ? Boolean.TRUE : slice(args);
- if(method.equals("join")) return justChecking ? Boolean.TRUE : join(args);
- if(method.equals("reverse")) return justChecking ? Boolean.TRUE : reverse(args);
- if(method.equals("toString")) return justChecking ? Boolean.TRUE : join(",");
- if(method.equals("sort")) return justChecking ? Boolean.TRUE : sort(args);
- if(method.equals("splice")) return justChecking ? Boolean.TRUE : splice(args);
- return super.callMethod(method,args,justChecking);
+ if(method.equals("slice")) return slice(args);
+ if(method.equals("join")) return join(args);
+ if(method.equals("reverse")) return reverse(args);
+ if(method.equals("toString")) return join(",");
+ if(method.equals("sort")) return sort(args);
+ if(method.equals("splice")) return splice(args);
+ throw new JS.Exn("unknown method " + method);
}
-
- // we use _get instead of get solely to work around a GCJ bug
- public Object _get(Object key) throws JS.Exn {
+ public Object get(Object key) throws JS.Exn {
if (key.equals("length")) return new Long(vec.size());
int i = intVal(key);
return null;
}
}
- // we use _put instead of put solely to work around a GCJ bug
- public void _put(Object key, Object val) {
+
+ public void put(Object key, Object val) {
if (key.equals("length")) vec.setSize(toNumber(val).intValue());
int i = intVal(key);
if (i == Integer.MIN_VALUE) super.put(key, val);
vec.setElementAt(val, i);
}
}
- public Object[] keys() {
- Object[] sup = super.keys();
- Object[] ret = new Object[vec.size() + 1 + sup.length];
- System.arraycopy(sup, 0, ret, vec.size(), sup.length);
- for(int i=0; i<vec.size(); i++) ret[i] = new Integer(i);
- ret[vec.size()] = "length";
- return ret;
+
+ public Enumeration keys() {
+ return new Enumeration() {
+ int cur = 0;
+ public boolean hasMoreElements() { return cur >= vec.size(); }
+ public Object nextElement() {
+ if (cur >= vec.size()) throw new NoSuchElementException();
+ return new Integer(cur++);
+ }
+ };
}
+
public void setSize(int i) { vec.setSize(i); }
public int length() { return vec.size(); }
public Object elementAt(int i) { return vec.elementAt(i); }
public void addElement(Object o) { vec.addElement(o); }
public void setElementAt(Object o, int i) { vec.setElementAt(o, i); }
-
+ public int size() { return vec.size(); }
public String typeName() { return "array"; }
- private Object join(JS.Array args) {
+ private Object join(JSArray args) {
return join(args.length() == 0 ? "," : JS.toString(args.elementAt(0)));
}
return sb.toString();
}
- private Object reverse(JS.Array args) {
+ private Object reverse(JSArray args) {
Vec oldVec = vec;
int size = oldVec.size();
if(size < 2) return this;
return this;
}
- private Object slice(JS.Array args) {
+ private Object slice(JSArray args) {
int length = length();
int start = JS.toInt(args.length() < 1 ? null : args.elementAt(0));
int end = args.length() < 2 ? length : JS.toInt(args.elementAt(1));
if(end < 0) end = 0;
if(start > length) start = length;
if(end > length) end = length;
- JS.Array a = new JS.Array(end-start);
+ JSArray a = new JSArray(end-start);
for(int i=0;i<end-start;i++)
a.setElementAt(elementAt(start+i),i);
return a;
return JS.toString(a).compareTo(JS.toString(b));
}
};
- private Object sort(JS.Array args) {
+ private Object sort(JSArray args) {
Object tmp = args.length() < 1 ? null : args.elementAt(0);
- if(tmp instanceof JS.Callable) {
- final JS.Array funcArgs = new JS.Array(2);
- final JS.Callable jsFunc = (JS.Callable) tmp;
+ if(tmp instanceof JSCallable) {
+ final JSArray funcArgs = new JSArray(2);
+ final JSCallable jsFunc = (JSCallable) tmp;
vec.sort(new Vec.CompareFunc() {
public int compare(Object a, Object b) {
funcArgs.setElementAt(a,0);
funcArgs.setElementAt(b,1);
- return JS.toInt(jsFunc.call(funcArgs));
+ return JS.toInt(jsFunc.call(null, funcArgs));
}
});
} else {
return this;
}
- private Object splice(JS.Array args) {
+ private Object splice(JSArray args) {
int oldLength = length();
int start = JS.toInt(args.length() < 1 ? null : args.elementAt(0));
int deleteCount = JS.toInt(args.length() < 2 ? null : args.elementAt(1));
if(deleteCount > oldLength-start) deleteCount = oldLength-start;
int newLength = oldLength - deleteCount + newCount;
int lengthChange = newLength - oldLength;
- JS.Array ret = new JS.Array(deleteCount);
+ JSArray ret = new JSArray(deleteCount);
for(int i=0;i<deleteCount;i++)
ret.setElementAt(elementAt(start+i),i);
if(lengthChange > 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 JSObj {
+ public Object call0(Object method) throws JS.Exn {
+ JSArray args = new JSArray();
+ return call(method, args);
+ }
+ public Object call1(Object method, Object arg1) throws JS.Exn {
+ JSArray args = new JSArray();
+ args.addElement(arg1);
+ return call(method, args);
+ }
+ public Object call2(Object method, Object arg1, Object arg2) throws JS.Exn {
+ JSArray args = new JSArray();
+ args.addElement(arg1);
+ args.addElement(arg2);
+ return call(method, args);
+ }
+ public Object call(Object method, JSArray args) throws JS.Exn {
+ throw new JS.Exn("cannot invoke this method with " + args.size() + " arguments");
+ }
+}
+
--- /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;
+
+ JSFunction f = null; ///< the currently-executing JSFunction
+ JSScope scope = null;
+ Vec stack = new Vec(); ///< the object stack
+ int pc = 0; ///< the program counter
+ boolean pauseable;
+
+ public static void invokePauseable(JSFunction function) { new JSContext(f, true).invoke(new JSArray()); }
+
+ JSContext(JSFunction f, boolean pauseable) {
+ this.pauseable = pauseable;
+ this.f = f;
+ scope = new JSScope(function.parentJSScope);
+ }
+
+ void invoke(JSArray args) {
+ stack.push(new JSFunction.CallMarker(cx));
+ 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) {
+ paused = false;
+ stack.push(o);
+ resume();
+ return null;
+ } };
+ }
+
+ private void resume() {
+ Thread t = Thread.currentThread();
+ JSContext old = (JSContext)threadToJSContext.get(t);
+ threadToJSContext.put(t, this);
+ try {
+ JSFunction.eval(this);
+ } finally {
+ if (old == null) threadToJSContext.remove(t);
+ else threadToJSContext.put(t, old);
+ }
+ }
+}
* See ECMA 15.9.
* @author Mike McCabe
*/
-public class Date extends JS.Obj {
+public class JSDate extends JSCallable {
- public Date() {
+ public JSDate() {
if (thisTimeZone == null) {
// j.u.TimeZone is synchronized, so setting class statics from it
// should be OK.
public String coerceToString() { return date_format(date, FORMATSPEC_FULL); }
- public Object callMethod(Object name, JS.Array args_, boolean checkOnly) {
+ public Object call(Object name, JSArray args_) {
Object[] args = new Object[args_.length()];
for(int i=0; i<args.length; i++) args[i] = args_.elementAt(i);
if (name.equals(getIdName(ConstructorId_UTC))) {
- if (checkOnly) return Boolean.TRUE;
- return new Double(jsStaticFunction_UTC(args));
+ return new Double(jsStaticJSFunction_UTC(args));
} else if (name.equals(getIdName(ConstructorId_parse))) {
- if (checkOnly) return Boolean.TRUE;
- return new Double(jsStaticFunction_parse(args[0].toString()));
+ return new Double(jsStaticJSFunction_parse(args[0].toString()));
} else if (name.equals(getIdName(Id_constructor))) {
- if (checkOnly) return Boolean.TRUE;
- return new Date(args_);
+ return new JSDate(args_);
} else if (name.equals(getIdName(Id_toString))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
return date_format(t, FORMATSPEC_FULL);
} else if (name.equals(getIdName(Id_toTimeString))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
return date_format(t, FORMATSPEC_TIME);
} else if (name.equals(getIdName(Id_toDateString))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
return date_format(t, FORMATSPEC_DATE);
} else if (name.equals(getIdName(Id_toLocaleString))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
- return jsFunction_toLocaleString(t);
+ return jsJSFunction_toLocaleString(t);
} else if (name.equals(getIdName(Id_toLocaleTimeString))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
- return jsFunction_toLocaleTimeString(t);
+ return jsJSFunction_toLocaleTimeString(t);
} else if (name.equals(getIdName(Id_toLocaleDateString))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
- return jsFunction_toLocaleDateString(t);
+ return jsJSFunction_toLocaleDateString(t);
} else if (name.equals(getIdName(Id_toUTCString))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
- return jsFunction_toUTCString(t);
+ return jsJSFunction_toUTCString(t);
} else if (name.equals(getIdName(Id_valueOf))) {
- if (checkOnly) return Boolean.TRUE;
return new Double(this.date);
} else if (name.equals(getIdName(Id_getTime))) {
- if (checkOnly) return Boolean.TRUE;
return new Double(this.date);
} else if (name.equals(getIdName(Id_getYear))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
- t = jsFunction_getYear(t);
+ t = jsJSFunction_getYear(t);
return new Double(t);
} else if (name.equals(getIdName(Id_getFullYear))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
t = YearFromTime(LocalTime(t));
return new Double(t);
} else if (name.equals(getIdName(Id_getUTCFullYear))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
t = YearFromTime(t);
return new Double(t);
} else if (name.equals(getIdName(Id_getMonth))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
t = MonthFromTime(LocalTime(t));
return new Double(t);
} else if (name.equals(getIdName(Id_getUTCMonth))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
t = MonthFromTime(t);
return new Double(t);
} else if (name.equals(getIdName(Id_getDate))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
t = DateFromTime(LocalTime(t));
return new Double(t);
} else if (name.equals(getIdName(Id_getUTCDate))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
t = DateFromTime(t);
return new Double(t);
} else if (name.equals(getIdName(Id_getDay))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
t = WeekDay(LocalTime(t));
return new Double(t);
} else if (name.equals(getIdName(Id_getUTCDay))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
t = WeekDay(t);
return new Double(t);
} else if (name.equals(getIdName(Id_getHours))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
t = HourFromTime(LocalTime(t));
return new Double(t);
} else if (name.equals(getIdName(Id_getUTCHours))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
t = HourFromTime(t);
return new Double(t);
} else if (name.equals(getIdName(Id_getMinutes))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
t = MinFromTime(LocalTime(t));
return new Double(t);
} else if (name.equals(getIdName(Id_getUTCMinutes))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
t = MinFromTime(t);
return new Double(t);
} else if (name.equals(getIdName(Id_getSeconds))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
t = SecFromTime(LocalTime(t));
return new Double(t);
} else if (name.equals(getIdName(Id_getUTCSeconds))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
t = SecFromTime(t);
return new Double(t);
} else if (name.equals(getIdName(Id_getMilliseconds))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
t = msFromTime(LocalTime(t));
return new Double(t);
} else if (name.equals(getIdName(Id_getUTCMilliseconds))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
t = msFromTime(t);
return new Double(t);
} else if (name.equals(getIdName(Id_getTimezoneOffset))) {
- if (checkOnly) return Boolean.TRUE;
double t = date;
- t = jsFunction_getTimezoneOffset(t);
+ t = jsJSFunction_getTimezoneOffset(t);
return new Double(t);
} else if (name.equals(getIdName(Id_setTime))) {
- if (checkOnly) return Boolean.TRUE;
- return new Double(this.jsFunction_setTime(_toNumber(args, 0)));
+ return new Double(this.jsJSFunction_setTime(_toNumber(args, 0)));
} else if (name.equals(getIdName(Id_setMilliseconds))) {
- if (checkOnly) return Boolean.TRUE;
return new Double(this.
makeTime(args, 1, true));
} else if (name.equals(getIdName(Id_setUTCMilliseconds))) {
- if (checkOnly) return Boolean.TRUE;
return new Double(this.
makeTime(args, 1, false));
} else if (name.equals(getIdName(Id_setSeconds))) {
- if (checkOnly) return Boolean.TRUE;
return new Double(this.
makeTime(args, 2, true));
} else if (name.equals(getIdName(Id_setUTCSeconds))) {
- if (checkOnly) return Boolean.TRUE;
return new Double(this.
makeTime(args, 2, false));
} else if (name.equals(getIdName(Id_setMinutes))) {
- if (checkOnly) return Boolean.TRUE;
return new Double(this.
makeTime(args, 3, true));
} else if (name.equals(getIdName(Id_setUTCMinutes))) {
- if (checkOnly) return Boolean.TRUE;
return new Double(this.
makeTime(args, 3, false));
} else if (name.equals(getIdName(Id_setHours))) {
- if (checkOnly) return Boolean.TRUE;
return new Double(this.
makeTime(args, 4, true));
} else if (name.equals(getIdName(Id_setUTCHours))) {
- if (checkOnly) return Boolean.TRUE;
return new Double(this.
makeTime(args, 4, false));
} else if (name.equals(getIdName(Id_setDate))) {
- if (checkOnly) return Boolean.TRUE;
return new Double(this.
makeDate(args, 1, true));
} else if (name.equals(getIdName(Id_setUTCDate))) {
- if (checkOnly) return Boolean.TRUE;
return new Double(this.
makeDate(args, 1, false));
} else if (name.equals(getIdName(Id_setMonth))) {
- if (checkOnly) return Boolean.TRUE;
return new Double(this.
makeDate(args, 2, true));
} else if (name.equals(getIdName(Id_setUTCMonth))) {
- if (checkOnly) return Boolean.TRUE;
return new Double(this.
makeDate(args, 2, false));
} else if (name.equals(getIdName(Id_setFullYear))) {
- if (checkOnly) return Boolean.TRUE;
return new Double(this.
makeDate(args, 3, true));
} else if (name.equals(getIdName(Id_setUTCFullYear))) {
- if (checkOnly) return Boolean.TRUE;
return new Double(this.
makeDate(args, 3, false));
} else if (name.equals(getIdName(Id_setYear))) {
- if (checkOnly) return Boolean.TRUE;
return new Double(this.
- jsFunction_setYear(_toNumber(args, 0)));
+ jsJSFunction_setYear(_toNumber(args, 0)));
}
- if (checkOnly) return Boolean.FALSE;
return null;
}
private static final int MAXARGS = 7;
- private static double jsStaticFunction_UTC(Object[] args) {
+ private static double jsStaticJSFunction_UTC(Object[] args) {
double array[] = new double[MAXARGS];
int loop;
double d;
return msec;
}
- private static double jsStaticFunction_parse(String s) {
+ private static double jsStaticJSFunction_parse(String s) {
return date_parseString(s);
}
private static String date_format(double t, int format) {
if (t != t)
- return jsFunction_NaN_date_str;
+ return jsJSFunction_NaN_date_str;
StringBuffer result = new StringBuffer(60);
double local = LocalTime(t);
private static double _toNumber(Object[] o, int index) { return JS.toDouble(o[index]); }
private static double toDouble(double d) { return d; }
- public Date(JS.Array args_) {
+ public JSDate(JSArray args_) {
Object[] args = new Object[args_.length()];
for(int i=0; i<args.length; i++) args[i] = args_.elementAt(i);
- Date obj = this;
+ JSDate obj = this;
// if called as a constructor with no args,
// return a new Date with the current time.
}
/* constants for toString, toUTCString */
- private static String jsFunction_NaN_date_str = "Invalid Date";
+ private static String jsJSFunction_NaN_date_str = "Invalid Date";
private static String[] days = {
"Sun","Mon","Tue","Wed","Thu","Fri","Sat"
java.text.DateFormat formatter)
{
if (t != t)
- return jsFunction_NaN_date_str;
+ return jsJSFunction_NaN_date_str;
java.util.Date tempdate = new java.util.Date((long) t);
return formatter.format(tempdate);
}
- private static String jsFunction_toLocaleString(double date) {
+ private static String jsJSFunction_toLocaleString(double date) {
if (localeDateTimeFormatter == null)
localeDateTimeFormatter =
DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
return toLocale_helper(date, localeDateTimeFormatter);
}
- private static String jsFunction_toLocaleTimeString(double date) {
+ private static String jsJSFunction_toLocaleTimeString(double date) {
if (localeTimeFormatter == null)
localeTimeFormatter = DateFormat.getTimeInstance(DateFormat.LONG);
return toLocale_helper(date, localeTimeFormatter);
}
- private static String jsFunction_toLocaleDateString(double date) {
+ private static String jsJSFunction_toLocaleDateString(double date) {
if (localeDateFormatter == null)
localeDateFormatter = DateFormat.getDateInstance(DateFormat.LONG);
return toLocale_helper(date, localeDateFormatter);
}
- private static String jsFunction_toUTCString(double date) {
+ private static String jsJSFunction_toUTCString(double date) {
StringBuffer result = new StringBuffer(60);
String dateStr = Integer.toString(DateFromTime(date));
return result.toString();
}
- private static double jsFunction_getYear(double date) {
+ private static double jsJSFunction_getYear(double date) {
int result = YearFromTime(LocalTime(date));
result -= 1900;
return result;
}
- private static double jsFunction_getTimezoneOffset(double date) {
+ private static double jsJSFunction_getTimezoneOffset(double date) {
return (date - LocalTime(date)) / msPerMinute;
}
- public double jsFunction_setTime(double time) {
+ public double jsJSFunction_setTime(double time) {
this.date = TimeClip(time);
return this.date;
}
return date;
}
- private double jsFunction_setHours(Object[] args) {
+ private double jsJSFunction_setHours(Object[] args) {
return makeTime(args, 4, true);
}
- private double jsFunction_setUTCHours(Object[] args) {
+ private double jsJSFunction_setUTCHours(Object[] args) {
return makeTime(args, 4, false);
}
return date;
}
- private double jsFunction_setYear(double year) {
+ private double jsJSFunction_setYear(double year) {
double day, result;
if (year != year || Double.isInfinite(year)) {
this.date = Double.NaN;
import org.xwt.util.*;
import java.io.*;
-/** a JavaScript function, compiled into bytecode */
-public class Function extends JS.Obj implements ByteCodes, Tokens {
-
- public int getNumFormalArgs() { return numFormalArgs; }
+/** A JavaScript function, compiled into bytecode */
+public class JSFunction extends JSCallable implements ByteCodes, Tokens {
+
+ /** Note: code gets run in an <i>unpauseable</i> context. */
+ public Object call(JSArray args) {
+ Context cx = new JSContext(this, false);
+ cx.invoke(args);
+ return cx.stack.pop();
+ }
// Fields and Accessors ///////////////////////////////////////////////
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
- JS.Scope parentScope; ///< the default scope to use as a parent scope when executing this
+ JSScope parentJSScope; ///< the default scope to use as a parent scope when executing this
// Constructors ////////////////////////////////////////////////////////
- private Function cloneWithNewParentScope(JS.Scope s) {
- Function ret = new Function(sourceName, firstLine, s);
+ public JSFunction cloneWithNewParentJSScope(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
ret.op = this.op;
return ret;
}
- private Function(String sourceName, int firstLine, JS.Scope parentScope) {
+ private JSFunction(String sourceName, int firstLine, JSScope parentJSScope) {
this.sourceName = sourceName;
this.firstLine = firstLine;
- this.parentScope = parentScope;
+ this.parentJSScope = parentJSScope;
}
- protected Function(String sourceName, int firstLine, Reader sourceCode, JS.Scope parentScope) throws IOException {
- this(sourceName, firstLine, parentScope);
+ 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) {
void set(int pos, int op_, Object arg_) { op[pos] = op_; arg[pos] = arg_; }
void set(int pos, Object arg_) { arg[pos] = arg_; }
int pop() { size--; arg[size] = null; return op[size]; }
- void paste(Function other) { for(int i=0; i<other.size; i++) add(other.line[i], other.op[i], other.arg[i]); }
- Function add(int line, int op_) { return add(line, op_, null); }
- Function add(int line, int op_, Object arg_) {
+ void paste(JSFunction other) { for(int i=0; i<other.size; i++) add(other.line[i], other.op[i], other.arg[i]); }
+ JSFunction add(int line, int op_) { return add(line, op_, null); }
+ JSFunction add(int line, int op_, Object arg_) {
if (size == op.length - 1) {
int[] line2 = new int[op.length * 2]; System.arraycopy(this.line, 0, line2, 0, op.length); this.line = line2;
Object[] arg2 = new Object[op.length * 2]; System.arraycopy(arg, 0, arg2, 0, arg.length); arg = arg2;
// Invoking the Bytecode ///////////////////////////////////////////////////////
/** returns false if the thread has been paused */
- static Object eval(final Context cx) throws JS.Exn {
+ static Object eval(final JSContext cx) throws JS.Exn {
+ final initialPauseCount = cx.pausecount;
OUTER: for(;; cx.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];
- Object returnedFromJava = null;
- boolean checkReturnedFromJava = false;
if(op == FINALLY_DONE) {
FinallyData fd = (FinallyData) cx.stack.pop();
if(fd == null) continue OUTER; // NOP
}
switch(op) {
case LITERAL: cx.stack.push(arg); break;
- case OBJECT: cx.stack.push(new JS.Obj()); break;
- case ARRAY: cx.stack.push(new JS.Array(JS.toNumber(arg).intValue())); break;
+ case OBJECT: cx.stack.push(new JSObj()); 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 POP: cx.stack.pop(); break;
case SWAP: { Object o1 = cx.stack.pop(); Object o2 = cx.stack.pop(); cx.stack.push(o1); cx.stack.push(o2); break; }
case DUP: cx.stack.push(cx.stack.peek()); break;
- case NEWSCOPE: cx.scope = new JS.Scope(cx.scope); break;
- case OLDSCOPE: cx.scope = cx.scope.getParentScope(); 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(((Function)arg).cloneWithNewParentScope(cx.scope)); break;
+ case NEWFUNCTION: cx.stack.push(((JSFunction)arg).cloneWithNewParentJSScope(cx.scope)); break;
case LABEL: break;
case TYPEOF: {
case PUSHKEYS: {
Object o = cx.stack.peek();
- Object[] keys = ((JS)o).keys();
- JS.Array a = new JS.Array();
- a.setSize(keys.length);
- for(int j=0; j<keys.length; j++) a.setElementAt(keys[j], j);
+ Enumeration e = ((JS)o).keys();
+ JSArray a = new JSArray();
+ while(e.hasMoreElements()) a.addElement(e.nextElement());
cx.stack.push(a);
break;
}
cx.pc = ((TryMarker)o).finallyLoc - 1;
continue OUTER;
} else if (o instanceof CallMarker) {
+ if (cx.scope instanceof JSTrapScope) {
+ JSTrapScope ts = (JSTrapScope)cx.scope;
+ if (!ts.cascadeHappened) {
+ ts.cascadeHappened = true;
+ JSTrap t = ts.t.next;
+ while (t != null && t.f.numFormalArgs == 0) t = t.next;
+ if (t == null) {
+ ts.trappee.put(key, val);
+ if (cx.pausecount > initialPauseCount) return; // we were paused
+ } else {
+ cx.stack.push(o);
+ JSArray args = new JSArray();
+ args.addElement(ts.val);
+ cx.stack.push(ta);
+ cx.f = t.f;
+ cx.scope = new JSTrap.JSTrapScope(cx.f.parentJSScope, ts.val);
+ cx.pc = -1;
+ break;
+ }
+ }
+ }
cx.scope = ((CallMarker)o).scope;
cx.pc = ((CallMarker)o).pc;
- cx.f = (Function)((CallMarker)o).f;
+ cx.f = (JSFunction)((CallMarker)o).f;
cx.stack.push(retval);
continue OUTER;
}
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");
- returnedFromJava = ((JS)target).put(key, val);
- if (returnedFromJava != null) checkReturnedFromJava = true;
- else cx.stack.push(val);
+ JSTrap t = null;
+ if (o instanceof JSTrap.JSTrappable) {
+ t = ((JSTrap.JSTrappable)o).getTrap(v);
+ while (t != null && t.f.numFormalArgs == 0) t = t.next;
+ } else if (o instanceof JSTrap.JSTrapScope && key.equals("cascade")) {
+ JSTrap.JSTrapScope ts = (JSTrap.JSTrapScope)o;
+ t = ts.t.next;
+ ts.cascadeHappened = true;
+ while (t != null && t.f.numFormalArgs == 0) t = t.next;
+ if (t == null) o = ts.t.trappee;
+ }
+ if (t != null) {
+ cx.stack.push(new CallMarker(cx));
+ JSArray args = new JSArray();
+ args.addElement(val);
+ cx.stack.push(ta);
+ cx.f = t.f;
+ cx.scope = new JSTrap.JSTrapScope(cx.f.parentJSScope, val);
+ cx.pc = -1;
+ break;
+ }
+ ((JS)target).put(key, val);
+ if (cx.pausecount > initialPauseCount) return; // we were paused
+ cx.stack.push(val);
break;
}
cx.stack.push(ret);
break;
} else if (o instanceof JS) {
- returnedFromJava = ((JS)o).get(v);
- checkReturnedFromJava = true;
+ JSTrap t = null;
+ if (o instanceof JSTrap.JSTrappable) {
+ t = ((JSTrap.JSTrappable)o).getTrap(v);
+ while (t != null && t.f.numFormalArgs != 0) t = t.next;
+ } else if (o instanceof JSTrap.JSTrapScope && key.equals("cascade")) {
+ t = ((JSTrap.JSTrapScope)o).t.next;
+ while (t != null && t.f.numFormalArgs != 0) t = t.next;
+ if (t == null) o = ((JSTrap.JSTrapScope)o).t.trappee;
+ }
+ if (t != null) {
+ cx.stack.push(new CallMarker(cx));
+ JSArray args = new JSArray();
+ cx.stack.push(ta);
+ cx.f = t.f;
+ cx.scope = new JSTrap.JSTrapScope(cx.f.parentJSScope, null);
+ ((JSTrap.JSTrapScope)cx.scope).cascadeHappened = true;
+ cx.pc = -1;
+ break;
+ }
+ ret = ((JS)o).get(v);
+ if (cx.pausecount > initialPauseCount) return; // we were paused
+ cx.stack.push(ret);
break;
}
throw je("tried to get property " + v + " from a " + o.getClass().getName());
}
- case CALL: case CALLMETHOD: case CALL_REVERSED: {
- JS.Array arguments = new JS.Array();
+ case CALL: case CALLMETHOD: {
int numArgs = JS.toNumber(arg).intValue();
- arguments.setSize(numArgs);
- Object o = null;
- if (op == CALL_REVERSED) o = cx.stack.pop();
- for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j);
- if (op != CALL_REVERSED) o = cx.stack.pop();
+ Object o = cx.stack.pop();
if(o == null) throw je("attempted to call null");
Object ret;
-
+ Object method = null;
if(op == CALLMETHOD) {
- Object method = o;
+ method = o;
+ if (method == null) throw new JS.Exn("cannot call the null method");
o = cx.stack.pop();
if(o instanceof String || o instanceof Number || o instanceof Boolean) {
+ JSArray arguments = new JSArray();
+ for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j);
ret = Internal.callMethodOnPrimitive(o,method,arguments);
cx.stack.push(ret);
cx.pc += 2; // skip the GET and CALL
- } else if (o instanceof JS && ((JS)o).callMethod(method, arguments, true) == Boolean.TRUE) {
- returnedFromJava = ((JS)o).callMethod(method, arguments, false);
- checkReturnedFromJava = true;
- cx.pc += 2; // skip the GET and CALL
+ break;
+ } else if (o instanceof JSCallable) {
+ // fall through
} else {
// put the args back on the stack and let the GET followed by CALL happen
- for(int j=0; j<numArgs; j++) cx.stack.push(arguments.elementAt(j));
cx.stack.push(o);
cx.stack.push(method);
+ break;
}
- } else if (o instanceof Function) {
+ } else if (o 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 = (Function)o;
- cx.scope = new Scope(cx.f.parentScope);
+ cx.f = (JSFunction)o;
+ cx.scope = new JSScope(cx.f.parentJSScope);
cx.pc = -1;
-
- } else {
- returnedFromJava = ((JS.Callable)o).call(arguments);
- checkReturnedFromJava = true;
+ break;
+ }
+
+ JSCallable c = ((JSCallable)o);
+ switch(numArgs) {
+ case 0: ret = c.call0(method); break;
+ case 1: ret = c.call1(method, cx.stack.pop()); break;
+ case 2: ret = c.call2(method, cx.stack.pop(), cx.stack.pop()); break;
+ default: {
+ JSArray arguments = new JSArray();
+ for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j);
+ ret = c.call(method, arguments);
+ if (cx.pausecount > initialPauseCount) return; // we were paused
+ break;
+ }
}
+ cx.stack.push(ret);
+ if (method != null) cx.pc += 2; // skip the GET and CALL if this was a GETCALL
+
break;
}
throw new JS.Exn(o);
}
- case INC: case DEC: {
- boolean isPrefix = JS.toBoolean(arg);
- Object key = cx.stack.pop();
- JS obj = (JS)cx.stack.pop();
- Number num = JS.toNumber(obj.get(key));
- Number val = new Double(op == INC ? num.doubleValue() + 1.0 : num.doubleValue() - 1.0);
- obj.put(key, val);
- cx.stack.push(isPrefix ? val : num);
- break;
- }
-
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();
- if (val instanceof Function && obj instanceof JS.Scope) {
- JS.Scope parent = (JS.Scope)obj;
- while(parent.getParentScope() != null) parent = parent.getParentScope();
- if (parent instanceof org.xwt.Box) {
- org.xwt.Box b = (org.xwt.Box)parent;
- if (op == ASSIGN_ADD) b.addTrap(key, val); else b.delTrap(key, val);
+ 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);
// skip over the "normal" implementation of +=/-=
cx.pc += ((Integer)arg).intValue() - 1;
break;
} }
}
- // handle special directions returned from Java callouts
- // ideally we would do this with exceptions, but they're *very* slow in gcj
- if (checkReturnedFromJava) {
- checkReturnedFromJava = false;
- if (returnedFromJava == Context.pause) {
- cx.pc++;
- return Context.pause;
- } else if (returnedFromJava instanceof TailCall) {
- cx.stack.push(new CallMarker(cx));
- cx.stack.push(((JS.TailCall)returnedFromJava).args);
- cx.f = ((JS.TailCall)returnedFromJava).func;
- cx.scope = new Scope(cx.f.parentScope);
- cx.pc = -1;
- } else {
- cx.stack.push(returnedFromJava);
- }
- continue OUTER;
- }
-
-
} catch(JS.Exn e) {
while(cx.stack.size() > 0) {
Object o = cx.stack.pop();
// Debugging //////////////////////////////////////////////////////////////////////
- public String toString() { return "Function [" + sourceName + ":" + firstLine + "]"; }
+ public String toString() { return "JSFunction [" + sourceName + ":" + firstLine + "]"; }
public String dump() {
StringBuffer sb = new StringBuffer(1024);
sb.append("\n" + sourceName + ": " + firstLine + "\n");
static class EvaluatorException extends RuntimeException { public EvaluatorException(String s) { super(s); } }
static EvaluatorException ee(String s) {
- throw new EvaluatorException(Context.getSourceName() + ":" + Context.getLine() + " " + s);
+ throw new EvaluatorException(JSContext.getSourceName() + ":" + JSContext.getLine() + " " + s);
}
static JS.Exn je(String s) {
- throw new JS.Exn(Context.getSourceName() + ":" + Context.getLine() + " " + s);
+ throw new JS.Exn(JSContext.getSourceName() + ":" + JSContext.getLine() + " " + s);
}
public static class CallMarker {
int pc;
- Scope scope;
- Function f;
- public CallMarker(Context cx) { pc = cx.pc + 1; scope = cx.scope; f = cx.f; }
+ JSScope scope;
+ JSFunction f;
+ public CallMarker(JSContext cx) { pc = cx.pc + 1; scope = cx.scope; f = cx.f; }
}
public static class CatchMarker { public CatchMarker() { } }
public static class LoopMarker {
public int location;
public String label;
- public JS.Scope scope;
- public LoopMarker(int location, String label, JS.Scope scope) {
+ public JSScope scope;
+ public LoopMarker(int location, String label, JSScope scope) {
this.location = location;
this.label = label;
this.scope = scope;
public static class TryMarker {
public int catchLoc;
public int finallyLoc;
- public JS.Scope scope;
- public TryMarker(int catchLoc, int finallyLoc, JS.Scope scope) {
+ public JSScope scope;
+ public TryMarker(int catchLoc, int finallyLoc, JSScope scope) {
this.catchLoc = catchLoc;
this.finallyLoc = finallyLoc;
this.scope = scope;
--- /dev/null
+// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
+
+package org.xwt.js;
+import org.xwt.util.*;
+import java.io.*;
+import java.util.*;
+
+/** The JavaScript Math object */
+public class JSMath extends JSObj {
+
+ public static JSMath singleton = new JSMath();
+
+ public Object call(Object method, JSArray args) {
+ if (method == null) throw new JS.Exn("you cannot call this object");
+ //#switch(method)
+ case "ceil": return new Long((long)java.lang.Math.ceil(JS.toNumber(args.elementAt(0)).doubleValue()));
+ case "floor": return new Long((long)java.lang.Math.floor(JS.toNumber(args.elementAt(0)).doubleValue()));
+ case "round": return new Long((long)java.lang.Math.round(JS.toNumber(args.elementAt(0)).doubleValue()));
+ case "min": return new Double(java.lang.Math.min(JS.toNumber(args.elementAt(0)).doubleValue(),
+ JS.toNumber(args.elementAt(1)).doubleValue()));
+ case "max": return new Double(java.lang.Math.max(JS.toNumber(args.elementAt(0)).doubleValue(),
+ JS.toNumber(args.elementAt(1)).doubleValue()));
+ case "pow": return new Double(java.lang.Math.pow(JS.toNumber(args.elementAt(0)).doubleValue(),
+ JS.toNumber(args.elementAt(1)).doubleValue()));
+ case "atan2": return new Double(java.lang.Math.atan2(JS.toNumber(args.elementAt(0)).doubleValue(),
+ JS.toNumber(args.elementAt(1)).doubleValue()));
+ case "abs": return new Double(java.lang.Math.abs(JS.toNumber(args.elementAt(0)).doubleValue()));
+ case "sin": return new Double(java.lang.Math.sin(JS.toNumber(args.elementAt(0)).doubleValue()));
+ case "cos": return new Double(java.lang.Math.cos(JS.toNumber(args.elementAt(0)).doubleValue()));
+ case "tan": return new Double(java.lang.Math.tan(JS.toNumber(args.elementAt(0)).doubleValue()));
+ case "asin": return new Double(java.lang.Math.asin(JS.toNumber(args.elementAt(0)).doubleValue()));
+ case "acos": return new Double(java.lang.Math.acos(JS.toNumber(args.elementAt(0)).doubleValue()));
+ case "atan": return new Double(java.lang.Math.atan(JS.toNumber(args.elementAt(0)).doubleValue()));
+ case "sqrt": return new Double(java.lang.Math.sqrt(JS.toNumber(args.elementAt(0)).doubleValue()));
+ case "exp": return new Double(java.lang.Math.exp(JS.toNumber(args.elementAt(0)).doubleValue()));
+ case "log": return new Double(java.lang.Math.log(JS.toNumber(args.elementAt(0)).doubleValue()));
+ case "random": return new Double(java.lang.Math.random());
+ //#end
+ return null;
+ }
+
+ private static final Double E = new Double(java.lang.Math.E);
+ private static final Double PI = new Double(java.lang.Math.PI);
+ private static final Double LN10 = new Double(java.lang.Math.log(10));
+ private static final Double LN2 = new Double(java.lang.Math.log(2));
+ private static final Double LOG10E = new Double(1/java.lang.Math.log(10));
+ private static final Double LOG2E = new Double(1/java.lang.Math.log(2));
+ private static final Double SQRT1_2 = new Double(1/java.lang.Math.sqrt(2));
+ private static final Double SQRT2 = new Double(java.lang.Math.sqrt(2));
+
+ public void put(Object key, Object val) { return; }
+
+ public Object get(Object key, Object val) {
+ //#switch(key)
+ case "E": return E;
+ case "LN10": return LN10;
+ case "LN2": return LN2;
+ case "LOG10E": return LOG10E;
+ case "LOG2E": return LOG2E;
+ case "PI": return PI;
+ case "SQRT1_2": return SQRT1_2;
+ case "SQRT2": return SQRT2;
+ //#end
+ return null;
+ }
+}
--- /dev/null
+package org.xwt.js;
+import java.util.*;
+import org.xwt.util.*;
+
+// FEATURE: static slots for four objects?
+/** A sensible implementation of the abstract methods in the JS class */
+public class JSObj extends JS {
+
+ // 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
+ 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 Boolean B(boolean b) { return b ? Boolean.TRUE : Boolean.FALSE; }
+
+ private static Enumeration emptyEnumeration = new Enumeration() {
+ public boolean hasMoreElements() { return false; }
+ public Object nextElement() { throw new NoSuchElementException(); }
+ };
+
+ private Hash entries = null;
+ public JSObj() { }
+ public Enumeration keys() { return entries == null ? emptyEnumeration : entries.keys(); }
+ 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); }
+}
--- /dev/null
+package org.xwt.js;
+
+import gnu.regexp.*;
+
+public class JSRegexp extends JSCallable {
+ private boolean global;
+ private RE re;
+ private int lastIndex;
+
+ public JSRegexp(Object arg0, Object arg1) throws JS.Exn {
+ 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 sFlags = null;
+ int flags = 0;
+ if(arg1 != null) sFlags = (String)arg1;
+ if(sFlags == null) sFlags = "";
+ for(int i=0;i<sFlags.length();i++) {
+ switch(sFlags.charAt(i)) {
+ 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) + "\"");
+ }
+ }
+ 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));
+ }
+ }
+
+ public Object call(Object method, JSArray args) throws JS.Exn {
+ if (method.equals("exec")) {
+ return exec(args);
+ } else if (method.equals("test")) {
+ return test(args);
+ } else if (method.equals("toString")) {
+ return toString();
+ }
+ return null;
+ }
+
+ // gcj bug...
+ public Object get(Object key) { return _get(key); }
+ public void put(Object key,Object value) { _put(key,value); }
+
+ public Object _get(Object key) {
+ if(key.equals("lastIndex")) return new Integer(lastIndex);
+ return super.get(key);
+ }
+
+ public void _put(Object key, Object value) {
+ 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) {
+ JSObj ret = new JSObj();
+ ret.put("index",new Integer(match.getStartIndex()));
+ ret.put("input",s);
+ int n = re.getNumSubs();
+ ret.put("length",new Integer(n+1));
+ ret.put("0",match.toString());
+ 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(JSArray args) throws JS.Exn {
+ if(args.length() < 1) throw new JS.Exn("Not enough args to match");
+ String s = args.elementAt(0).toString();
+
+ 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('/');
+ sb.append(_get("source"));
+ sb.append('/');
+ if(global) sb.append('g');
+ if(Boolean.TRUE.equals(_get("ignoreCase"))) sb.append('i');
+ if(Boolean.TRUE.equals(_get("multiline"))) sb.append('m');
+ 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);
+ String s = o.toString();
+ RE re;
+ JSRegexp regexp = null;
+ if(arg0 instanceof JSRegexp) {
+ regexp = (JSRegexp) arg0;
+ re = regexp.re;
+ } else {
+ re = newRE(arg0.toString(),0);
+ }
+
+ if(regexp == null) {
+ REMatch match = re.getMatch(s);
+ return matchToExecResult(match,re,s);
+ }
+ if(!regexp.global)
+ return regexp.exec(s);
+
+ 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();
+ 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);
+ String s = o.toString();
+ RE re;
+ if(arg0 instanceof JSRegexp)
+ re = ((JSRegexp)arg0).re;
+ else
+ re = newRE(arg0.toString(),0);
+ REMatch match = re.getMatch(s);
+ if(match == null) return new Integer(-1);
+ return new Integer(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);
+ String s = o.toString();
+ RE re;
+ JSCallable replaceFunc = null;
+ String replaceString = null;
+ JSRegexp regexp = null;
+ if(arg0 instanceof JSRegexp) {
+ regexp = (JSRegexp) arg0;
+ re = regexp.re;
+ } else {
+ re = newRE(arg0.toString(),0);
+ }
+ if(arg1 instanceof JSCallable)
+ replaceFunc = (JSCallable) arg1;
+ else
+ replaceString = arg1.toString();
+ REMatch[] matches;
+ if(regexp != null && regexp.global) {
+ matches = re.getAllMatches(s);
+ if(regexp != null) {
+ if(matches.length > 0)
+ regexp.lastIndex = matches[matches.length-1].getEndIndex();
+ else
+ regexp.lastIndex = s.length();
+ }
+ } else {
+ REMatch match = re.getMatch(s);
+ if(match != null)
+ matches = new REMatch[]{ match };
+ else
+ matches = new REMatch[0];
+ }
+
+ StringBuffer sb = new StringBuffer(s.length());
+ int pos = 0;
+ char[] sa = s.toCharArray();
+ for(int i=0;i<matches.length;i++) {
+ REMatch match = matches[i];
+ 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));
+ }
+ a.addElement(new Integer(match.getStartIndex()));
+ a.addElement(s);
+ Object ret = replaceFunc.call(a, null);
+ sb.append(ret.toString());
+ */
+ } else {
+ sb.append(mySubstitute(match,replaceString,s));
+ }
+ }
+ int end = matches.length == 0 ? 0 : matches[matches.length-1].getEndIndex();
+ sb.append(sa,end,sa.length-end);
+ return sb.toString();
+ }
+
+ private static String mySubstitute(REMatch match, String s, String source) {
+ StringBuffer sb = new StringBuffer();
+ int i,n;
+ char c,c2;
+ for(i=0;i<s.length()-1;i++) {
+ c = s.charAt(i);
+ if(c != '$') {
+ sb.append(c);
+ continue;
+ }
+ i++;
+ c = s.charAt(i);
+ switch(c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if(i < s.length()-1 && (c2 = s.charAt(i+1)) >= '0' && c2 <= '9') {
+ n = (c - '0') * 10 + (c2 - '0');
+ i++;
+ } else {
+ n = c - '0';
+ }
+ if(n > 0)
+ sb.append(match.toString(n));
+ break;
+ case '$':
+ sb.append('$'); break;
+ case '&':
+ sb.append(match.toString()); break;
+ case '`':
+ sb.append(source.substring(0,match.getStartIndex())); break;
+ case '\'':
+ sb.append(source.substring(match.getEndIndex())); break;
+ default:
+ sb.append('$');
+ sb.append(c);
+ }
+ }
+ if(i < s.length()) sb.append(s.charAt(i));
+ return sb.toString();
+ }
+
+
+ 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));
+ if(limit < 0) limit = Integer.MAX_VALUE;
+ if(limit == 0) return new JSArray();
+
+ RE re = null;
+ JSRegexp regexp = null;
+ String sep = null;
+ JSArray ret = new JSArray();
+ int p = 0;
+
+ if(arg0 instanceof JSRegexp) {
+ regexp = (JSRegexp) arg0;
+ re = regexp.re;
+ } else {
+ sep = arg0.toString();
+ }
+
+ // special case this for speed. additionally, the code below doesn't properly handle
+ // zero length strings
+ if(sep != null && sep.length()==0) {
+ int len = s.length();
+ for(int i=0;i<len;i++)
+ ret.addElement(s.substring(i,i+1));
+ return ret;
+ }
+
+ OUTER: while(p < s.length()) {
+ if(re != null) {
+ REMatch m = re.getMatch(s,p);
+ if(m == null) break OUTER;
+ boolean zeroLength = m.getStartIndex() == m.getEndIndex();
+ ret.addElement(s.substring(p,zeroLength ? m.getStartIndex()+1 : m.getStartIndex()));
+ p = zeroLength ? p + 1 : m.getEndIndex();
+ if(!zeroLength) {
+ for(int i=1;i<=re.getNumSubs();i++) {
+ ret.addElement(m.toString(i));
+ if(ret.length() == limit) break OUTER;
+ }
+ }
+ } else {
+ int x = s.indexOf(sep,p);
+ if(x == -1) break OUTER;
+ ret.addElement(s.substring(p,x));
+ p = x + sep.length();
+ }
+ if(ret.length() == limit) break;
+ }
+ if(p < s.length() && ret.length() != limit)
+ ret.addElement(s.substring(p));
+ return ret;
+ }
+
+ public static RE newRE(String pattern, int flags) throws JS.Exn {
+ try {
+ return new RE(pattern,flags,RESyntax.RE_SYNTAX_PERL5);
+ } catch(REException e) {
+ throw new JS.Exn(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"; }
+}
--- /dev/null
+// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt.js;
+
+import org.xwt.util.*;
+import java.io.*;
+import java.util.*;
+
+/** Implementation of a JavaScript JSScope */
+public class JSScope extends JSCallable {
+ private JSScope parentJSScope;
+ 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 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 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);
+ }
+
+ public void put(Object key, Object val) {
+ if (parentJSScope != null && !has(key)) parentJSScope.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 Object get(Object key) {
+ if(key.equals("NaN")) return NaN;
+ if(key.equals("Infinity")) return POSITIVE_INFINITY;
+ if(key.equals("undefined")) return null;
+ return super.get(key);
+ }
+
+ public Object call1(Object method, Object arg0) {
+ //#switch(method)
+ case "parseInt": return parseInt(arg0, 0);
+ case "isNaN": { double d = toDouble(arg0); return d == d ? F : T; }
+ case "isFinite": { double d = toDouble(arg0); return (d == d && !Double.isFinite(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");
+ case "stringFromCharCode": return stringFromCharCode(arg0);
+ //#end
+ return null;
+ }
+
+ public Object call2(Object method, Object arg1, Object arg2) {
+ if (method.equals("parseInt")) return parseInt(arg1, arg2);
+ return null;
+ }
+
+ private Object stringFromCharCode(Object arg) {
+ char buf[] = new char[args.length()];
+ for(int i=0;i<args.length();i++) buf[i] = (char)(JS.toInt(arg) & 0xffff);
+ return new String(buf);
+ }
+
+ private Object parseInt(Object arg, int radix) {
+ String s = (String)arg;
+ int start = 0;
+ int length = s.length();
+ int sign = 1;
+ long n = 0;
+ if(radix != 0 && (radix < 2 || radix > 36)) return NaN;
+ while(start < length && Character.isWhitespace(s.charAt(start))) start++;
+ if((length >= start+1) && (s.charAt(start) == '+' || s.charAt(start) == '-')) {
+ sign = s.charAt(start) == '+' ? 1 : -1;
+ start++;
+ }
+ if(radix == 0 && length >= start+1 && s.charAt(start) == '0') {
+ start++;
+ if(length >= start+1 && (s.charAt(start) == 'x' || s.charAt(start) == 'X')) {
+ start++;
+ radix = 16;
+ } else {
+ radix = 8;
+ if(length == start || Character.digit(s.charAt(start+1),8)==-1) return new Integer(0);
+ }
+ }
+ if(radix == 0) radix = 10;
+ if(length == start || Character.digit(s.charAt(start),radix) == -1) return NaN;
+ // try the fast way first
+ try {
+ String s2 = start == 0 ? s : s.substring(start);
+ return new Integer(sign*Integer.parseInt(s2,radix));
+ } catch(NumberFormatException e) { }
+ // fall through to a slower but emca-compliant method
+ for(int i=start;i<length;i++) {
+ int digit = Character.digit(s.charAt(i),radix);
+ if(digit < 0) break;
+ 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);
+ }
+
+ private Object parseFloat(Object arg) {
+ String s = (String)arg;
+ int start = 0;
+ int length = s.length();
+ while(start < length && Character.isWhitespace(s.charAt(0))) start++;
+ int end = length;
+ // as long as the string has no trailing garbage,this is fast, its slow with
+ // trailing garbage
+ while(start < end) {
+ try {
+ return new Double(s.substring(start,length));
+ } catch(NumberFormatException e) { }
+ end--;
+ }
+ return NaN;
+ }
+ }
+}
+
--- /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 {
+
+ private JSTrappable trapee = null; ///< the box on which this trap was placed
+ JSFunction f = null; ///< the function for this trap
+ private JSTrap next = null; ///< the next trap down the trap stack
+ private 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);
+
+ /** puts to this value using an unpauseable context, triggering any traps */
+ public abstract void putAndTriggerTraps(Object key, Object value);
+ }
+
+ // FIXME: cascadeHappened gets set, but autocascade does not happen
+ static class JSTrapScope extends JSScope {
+ JSTrap t;
+ Object val = null;
+ boolean cascadeHappened = false;
+ public JSTrapScope(JSTrap t, Object val) { 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);
+ }
+ }
+}
+
+++ /dev/null
-// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
-
-package org.xwt.js;
-import org.xwt.util.*;
-import java.io.*;
-import java.util.*;
-
-/** The JavaScript Math object */
-public class Math extends JS.Obj {
- public static Math singleton = new Math();
-
- private static final JS.Callable ceil = new JS.Callable() { public Object call(JS.Array args) {
- return new Long((long)java.lang.Math.ceil(JS.toNumber(args.elementAt(0)).doubleValue())); } };
-
- private static final JS.Callable floor = new JS.Callable() { public Object call(JS.Array args) {
- return new Long((long)java.lang.Math.floor(JS.toNumber(args.elementAt(0)).doubleValue())); } };
-
- private static final JS.Callable round = new JS.Callable() { public Object call(JS.Array args) {
- return new Long((long)java.lang.Math.round(JS.toNumber(args.elementAt(0)).doubleValue())); } };
-
- private static final JS.Callable min = new JS.Callable() { public Object call(JS.Array args) {
- return new Double(java.lang.Math.min(JS.toNumber(args.elementAt(0)).doubleValue(),
- JS.toNumber(args.elementAt(1)).doubleValue())); } };
-
- private static final JS.Callable max = new JS.Callable() { public Object call(JS.Array args) {
- return new Double(java.lang.Math.max(JS.toNumber(args.elementAt(0)).doubleValue(),
- JS.toNumber(args.elementAt(1)).doubleValue())); } };
-
- private static final JS.Callable pow = new JS.Callable() { public Object call(JS.Array args) {
- return new Double(java.lang.Math.pow(JS.toNumber(args.elementAt(0)).doubleValue(),
- JS.toNumber(args.elementAt(1)).doubleValue())); } };
-
- private static final JS.Callable atan2 = new JS.Callable() { public Object call(JS.Array args) {
- return new Double(java.lang.Math.atan2(JS.toNumber(args.elementAt(0)).doubleValue(),
- JS.toNumber(args.elementAt(1)).doubleValue())); } };
-
- private static final JS.Callable abs = new JS.Callable() { public Object call(JS.Array args) {
- return new Double(java.lang.Math.abs(JS.toNumber(args.elementAt(0)).doubleValue())); } };
-
- private static final JS.Callable sin = new JS.Callable() { public Object call(JS.Array args) {
- return new Double(java.lang.Math.sin(JS.toNumber(args.elementAt(0)).doubleValue())); } };
-
- private static final JS.Callable cos = new JS.Callable() { public Object call(JS.Array args) {
- return new Double(java.lang.Math.cos(JS.toNumber(args.elementAt(0)).doubleValue())); } };
-
- private static final JS.Callable tan = new JS.Callable() { public Object call(JS.Array args) {
- return new Double(java.lang.Math.tan(JS.toNumber(args.elementAt(0)).doubleValue())); } };
-
- private static final JS.Callable asin = new JS.Callable() { public Object call(JS.Array args) {
- return new Double(java.lang.Math.asin(JS.toNumber(args.elementAt(0)).doubleValue())); } };
-
- private static final JS.Callable acos = new JS.Callable() { public Object call(JS.Array args) {
- return new Double(java.lang.Math.acos(JS.toNumber(args.elementAt(0)).doubleValue())); } };
-
- private static final JS.Callable atan = new JS.Callable() { public Object call(JS.Array args) {
- return new Double(java.lang.Math.atan(JS.toNumber(args.elementAt(0)).doubleValue())); } };
-
- private static final JS.Callable sqrt = new JS.Callable() { public Object call(JS.Array args) {
- return new Double(java.lang.Math.sqrt(JS.toNumber(args.elementAt(0)).doubleValue())); } };
-
- private static final JS.Callable exp = new JS.Callable() { public Object call(JS.Array args) {
- return new Double(java.lang.Math.exp(JS.toNumber(args.elementAt(0)).doubleValue())); } };
-
- private static final JS.Callable log = new JS.Callable() { public Object call(JS.Array args) {
- return new Double(java.lang.Math.log(JS.toNumber(args.elementAt(0)).doubleValue())); } };
-
- private static final JS.Callable random = new JS.Callable() { public Object call(JS.Array args) {
- return new Double(java.lang.Math.random()); } };
-
- private static final Double E = new Double(java.lang.Math.E);
- private static final Double PI = new Double(java.lang.Math.PI);
- private static final Double LN10 = new Double(java.lang.Math.log(10));
- private static final Double LN2 = new Double(java.lang.Math.log(2));
- private static final Double LOG10E = new Double(1/java.lang.Math.log(10));
- private static final Double LOG2E = new Double(1/java.lang.Math.log(2));
- private static final Double SQRT1_2 = new Double(1/java.lang.Math.sqrt(2));
- private static final Double SQRT2 = new Double(java.lang.Math.sqrt(2));
-
- protected Math() {
- put("abs", abs);
- put("acos", acos);
- put("asin", asin);
- put("atan", atan);
- put("atan2", atan2);
- put("ceil", ceil);
- put("cos", cos);
- put("exp", exp);
- put("floor", floor);
- put("log", log);
- put("max", max);
- put("min", min);
- put("pow", pow);
- put("random", random);
- put("round",round);
- put("sin", sin);
- put("sqrt", sqrt);
- put("tan", tan);
-
- put("E", E);
- put("LN10", LN10);
- put("LN2", LN2);
- put("LOG10E", LOG10E);
- put("LOG2E", LOG2E);
- put("PI", PI);
- put("SQRT1_2", SQRT1_2);
- put("SQRT2", SQRT2);
-
- setSeal(true);
- }
-}
import org.xwt.util.*;
import java.io.*;
+// FEATURE intern Integers/Numbers
+
/**
- * Parses a stream of lexed tokens into a tree of Function's.
+ * Parses a stream of lexed tokens into a tree of JSFunction's.
*
* There are three kinds of things we parse: blocks, statements, and
* expressions.
/** for debugging */
public static void main(String[] s) throws Exception {
- Function block = new Function("stdin", 0, new InputStreamReader(System.in), null);
+ JSFunction block = new JSFunction("stdin", 0, new InputStreamReader(System.in), null);
if (block == null) return;
System.out.println(block);
}
* bytecodes for that expression to <tt>appendTo</tt>; the
* appended bytecodes MUST grow the stack by exactly one element.
*/
- private void startExpr(Function appendTo, int minPrecedence) throws IOException {
+ private void startExpr(JSFunction appendTo, int minPrecedence) throws IOException {
int saveParserLine = parserLine;
_startExpr(appendTo, minPrecedence);
parserLine = saveParserLine;
}
- private void _startExpr(Function appendTo, int minPrecedence) throws IOException {
+ private void _startExpr(JSFunction appendTo, int minPrecedence) throws IOException {
int tok = getToken();
- Function b = appendTo;
+ JSFunction b = appendTo;
switch (tok) {
case -1: throw pe("expected expression");
b.pop();
else
throw pe("prefixed increment/decrement can only be performed on a valid assignment target");
- b.add(parserLine, tok, Boolean.TRUE);
+ b.add(parserLine, GET_PRESERVE, Boolean.TRUE);
+ b.add(parserLine, LITERAL, new Integer(1));
+ b.add(parserLine, tok == INC ? ADD : SUB, null);
+ b.add(parserLine, PUT, null);
+ b.add(parserLine, SWAP, null);
+ b.add(parserLine, POP, null);
break;
}
case BANG: case BITNOT: case TYPEOF: {
case FUNCTION: {
consume(LP);
int numArgs = 0;
- Function b2 = new Function(sourceName, parserLine, null, null);
+ JSFunction b2 = new JSFunction(sourceName, parserLine, null, null);
b.add(parserLine, NEWFUNCTION, b2);
// function prelude; arguments array is already on the stack
b2.add(parserLine, POP); // pop off TOPSCOPE
if(peekToken() != LC)
- throw pe("Functions must have a block surrounded by curly brackets");
+ throw pe("JSFunctions must have a block surrounded by curly brackets");
parseBlock(b2, null); // the function body
* expression that modifies the assignable. This method always
* decreases the stack depth by exactly one element.
*/
- private void continueExprAfterAssignable(Function b,int minPrecedence) throws IOException {
+ private void continueExprAfterAssignable(JSFunction b,int minPrecedence) throws IOException {
int saveParserLine = parserLine;
_continueExprAfterAssignable(b,minPrecedence);
parserLine = saveParserLine;
}
- private void _continueExprAfterAssignable(Function b,int minPrecedence) throws IOException {
+ private void _continueExprAfterAssignable(JSFunction b,int minPrecedence) throws IOException {
if (b == null) throw new Error("got null b; this should never happen");
int tok = getToken();
if (minPrecedence != -1 && (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok])))
break;
}
case INC: case DEC: { // postfix
- b.add(parserLine, tok, Boolean.FALSE);
+ b.add(parserLine, GET_PRESERVE, Boolean.TRUE);
+ b.add(parserLine, LITERAL, new Integer(1));
+ b.add(parserLine, tok == INC ? ADD : SUB, null);
+ b.add(parserLine, PUT, null);
+ b.add(parserLine, SWAP, null);
+ b.add(parserLine, POP, null);
+ b.add(parserLine, LITERAL, new Integer(1));
+ b.add(parserLine, tok == INC ? SUB : ADD, null); // undo what we just did, since this is postfix
break;
}
case ASSIGN: {
break;
}
case LP: {
- int n = parseArgs(b);
+ int n = parseArgs(b, false);
// if the object supports GETCALL, we use this, and jump over the following two instructions
b.add(parserLine,CALLMETHOD,new Integer(n));
b.add(parserLine,GET);
- b.add(parserLine,CALL_REVERSED,new Integer(n));
+ b.add(parserLine,CALL,new Integer(n));
break;
}
default: {
* If any bytecodes are appended, they will not alter the stack
* depth.
*/
- private void continueExpr(Function b, int minPrecedence) throws IOException {
+ private void continueExpr(JSFunction b, int minPrecedence) throws IOException {
int saveParserLine = parserLine;
_continueExpr(b, minPrecedence);
parserLine = saveParserLine;
}
- private void _continueExpr(Function b, int minPrecedence) throws IOException {
+ private void _continueExpr(JSFunction b, int minPrecedence) throws IOException {
if (b == null) throw new Error("got null b; this should never happen");
int tok = getToken();
if (tok == -1) return;
switch (tok) {
case LP: { // invocation (not grouping)
- int n = parseArgs(b);
+ int n = parseArgs(b, true);
b.add(parserLine, CALL, new Integer(n));
break;
}
}
// parse a set of comma separated function arguments, assume LP has already been consumed
- private int parseArgs(Function b) throws IOException {
+ // if swap is true, (because the function is already on the stack) we will SWAP after each argument to keep it on top
+ private int parseArgs(JSFunction b, boolean swap) throws IOException {
int i = 0;
while(peekToken() != RP) {
i++;
if (peekToken() != COMMA) {
startExpr(b, NO_COMMA);
+ if (swap) b.add(parserLine, SWAP);
if (peekToken() == RP) break;
}
consume(COMMA);
}
/** Parse a block of statements which must be surrounded by LC..RC. */
- void parseBlock(Function b) throws IOException { parseBlock(b, null); }
- void parseBlock(Function b, String label) throws IOException {
+ void parseBlock(JSFunction b) throws IOException { parseBlock(b, null); }
+ void parseBlock(JSFunction b, String label) throws IOException {
int saveParserLine = parserLine;
_parseBlock(b, label);
parserLine = saveParserLine;
}
- void _parseBlock(Function b, String label) throws IOException {
+ void _parseBlock(JSFunction b, String label) throws IOException {
if (peekToken() == -1) return;
else if (peekToken() != LC) parseStatement(b, null);
else {
}
/** Parse a single statement, consuming the RC or SEMI which terminates it. */
- void parseStatement(Function b, String label) throws IOException {
+ void parseStatement(JSFunction b, String label) throws IOException {
int saveParserLine = parserLine;
_parseStatement(b, label);
parserLine = saveParserLine;
}
- void _parseStatement(Function b, String label) throws IOException {
+ void _parseStatement(JSFunction b, String label) throws IOException {
int tok = peekToken();
if (tok == -1) return;
switch(tok = getToken()) {
b.add(parserLine, NEWSCOPE); // grab a fresh scope
parseStatement(b, null); // initializer
- Function e2 = // we need to put the incrementor before the test
- new Function(sourceName, parserLine, null, null); // so we save the test here
+ JSFunction e2 = // we need to put the incrementor before the test
+ new JSFunction(sourceName, parserLine, null, null); // so we save the test here
if (peekToken() != SEMI)
startExpr(e2, -1);
else