if ((m != null && m == MessageQueue.currentlyPerforming) || MessageQueue.working) {
String what, where;
if (m != null && m instanceof ThreadMessage) {
- where = org.xwt.js.JS.getCurrentFunctionSourceName((ThreadMessage)m);
+ where = org.xwt.js.Context.getSourceNameAndLineForThread((ThreadMessage)m);
what = "background thread";
} else if (m != null) {
- where = org.xwt.js.JS.getCurrentFunctionSourceName(MessageQueue.singleton);
+ where = org.xwt.js.Context.getSourceNameAndLineForThread(MessageQueue.singleton);
what = "event trap";
} else {
- where = org.xwt.js.JS.getCurrentFunctionSourceName(MessageQueue.singleton);
+ where = org.xwt.js.Context.getSourceNameAndLineForThread(MessageQueue.singleton);
what = "script";
}
- if (Log.on) Log.log(this, "note: executing same " + what +
- " for " + (System.currentTimeMillis() - t) / 1000 + "s" +
- " at " + where);
+ long howlong = (System.currentTimeMillis() - t) / 1000;
+ if (howlong >= 5)
+ if (Log.on) Log.log(this, "note: executing same " + what + " for " + howlong + "s" + " at " + where);
} else {
m = MessageQueue.currentlyPerforming;
t = System.currentTimeMillis();
public String[] excluded = null;
/** the PAC script */
- public JS.Function proxyAutoConfigFunction = null;
+ public JS.Callable proxyAutoConfigFunction = null;
public static Proxy detectProxyViaManual() {
Proxy ret = new Proxy();
return ret;
}
- //public static JS proxyAutoConfigRootScope = new ProxyAutoConfigRootScope();
- public static JS.Function getProxyAutoConfigFunction(String url) {
- throw new Error("not implemented");
- // FIXME
- /*
+ public static JS.Scope proxyAutoConfigRootScope = new ProxyAutoConfigRootScope();
+ public static JS.Callable getProxyAutoConfigFunction(String url) {
try {
- Context cx = Context.enter();
- cx.setOptimizationLevel(-1);
BufferedReader br = new BufferedReader(new InputStreamReader(new HTTP(url, true).GET()));
String s = null;
String script = "";
if (Log.on) Log.log(Proxy.class, "DeCARPed PAC script:");
if (Log.on) Log.log(Proxy.class, script);
}
-
- Script scr = cx.compileReader(proxyAutoConfigRootScope, new StringReader(script), "PAC script at " + url, 0, null);
- scr.exec(cx, proxyAutoConfigRootScope);
- return (JS.Function)proxyAutoConfigRootScope.get("FindProxyForURL", null);
+
+ JS.Callable scr = JS.parse("PAC script at " + url, 0, new StringReader(script), proxyAutoConfigRootScope);
+ scr.call(new org.xwt.js.Array());
+ return (JS.Callable)proxyAutoConfigRootScope.get("FindProxyForURL");
} catch (Exception e) {
if (Log.on) {
Log.log(Platform.class, "WPAD detection failed due to:");
- if (e instanceof EcmaError) Log.log(HTTP.class, ((EcmaError)e).getMessage() + " at " +
- ((EcmaError)e).getSourceName() + ":" + ((EcmaError)e).getLineNumber());
- else if (e instanceof JS.Exn) {
- try {
- XWT.recursivePrintObject.call(Context.enter(), null, null, new Object[] {
- ((JS.Exn)e).getValue() });
- } catch (Exception e2) {
- Log.log(Platform.class, e);
- }
- }
+ if (e instanceof JS.Exn) {
+ try {
+ org.xwt.js.Array arr = new org.xwt.js.Array();
+ arr.addElement(((JS.Exn)e).getObject());
+ // FIXME
+ //XWT.recursivePrintObject.call();
+ } catch (Exception e2) {
+ Log.log(Platform.class, e);
+ }
+ }
else Log.log(Platform.class, e);
}
return null;
}
- */
}
// ProxyAutoConfigRootScope ////////////////////////////////////////////////////////////////////
- /*
- public static class ProxyAutoConfigRootScope extends JSObject {
-
- public String getClassName() { return "ProxyAutoConfigRootScope"; }
- ProxyAutoConfigRootScope() { Context.enter().initStandardObjects(this); }
+
+ public static class ProxyAutoConfigRootScope extends JS.Scope {
+
+ public ProxyAutoConfigRootScope() { super(null); }
- public Object get(String name, JS start) {
+ // FIXME: needs "standard objects"
+
+ public Object get(Object name) {
if (name.equals("isPlainHostName")) return isPlainHostName;
else if (name.equals("dnsDomainIs")) return dnsDomainIs;
else if (name.equals("localHostOrDomainIs")) return localHostOrDomainIs;
else if (name.equals("dateRange")) return dateRange;
else if (name.equals("timeRange")) return timeRange;
else if (name.equals("ProxyConfig")) return ProxyConfig;
- else return super.get(name, start);
+ else return super.get(name);
}
- private static final JS.Object proxyConfigBindings = new JS.Object();
- private static final JS.Object ProxyConfig = new JS.Object() {
- public Object get(String name, JS start) {
+ private static final JS.Obj proxyConfigBindings = new JS.Obj();
+ private static final JS.Obj ProxyConfig = new JS.Obj() {
+ public Object get(Object name) {
if (name.equals("bindings")) return proxyConfigBindings;
return null;
}
};
- private static final JS.Function isPlainHostName = new JS.Function() {
- public Object call(Context cx, JS thisObj, JS ctorObj, Object[] args) throws JS.Exn {
- return (args[0].toString().indexOf('.') == -1) ? Boolean.TRUE : Boolean.FALSE;
+ private static final JS.Callable isPlainHostName = new JS.Callable() {
+ public Object call(org.xwt.js.Array args) throws JS.Exn {
+ return (args.elementAt(0).toString().indexOf('.') == -1) ? Boolean.TRUE : Boolean.FALSE;
}
};
- private static final JS.Function dnsDomainIs = new JS.Function() {
- public Object call(Context cx, JS thisObj, JS ctorObj, Object[] args) throws JS.Exn {
- return (args[0].toString().endsWith(args[1].toString())) ? Boolean.TRUE : Boolean.FALSE;
+ private static final JS.Callable dnsDomainIs = new JS.Callable() {
+ public Object call(org.xwt.js.Array args) throws JS.Exn {
+ return (args.elementAt(0).toString().endsWith(args.elementAt(1).toString())) ? Boolean.TRUE : Boolean.FALSE;
}
};
- private static final JS.Function localHostOrDomainIs = new JS.Function() {
- public Object call(Context cx, JS thisObj, JS ctorObj, Object[] args) throws JS.Exn {
- return (args[0].toString().equals(args[1].toString()) ||
- (args[0].toString().indexOf('.') == -1 && args[1].toString().startsWith(args[0].toString()))) ?
+ private static final JS.Callable localHostOrDomainIs = new JS.Callable() {
+ public Object call(org.xwt.js.Array args) throws JS.Exn {
+ return (args.elementAt(0).toString().equals(args.elementAt(1).toString()) ||
+ (args.elementAt(0).toString().indexOf('.') == -1 && args.elementAt(1).toString().startsWith(args.elementAt(0).toString()))) ?
Boolean.TRUE : Boolean.FALSE;
}
};
- private static final JS.Function isResolvable = new JS.Function() {
- public Object call(Context cx, JS thisObj, JS ctorObj, Object[] args) throws JS.Exn {
+ private static final JS.Callable isResolvable = new JS.Callable() {
+ public Object call(org.xwt.js.Array args) throws JS.Exn {
try {
- return (InetAddress.getByName(args[0].toString()) != null) ? Boolean.TRUE : Boolean.FALSE;
+ return (InetAddress.getByName(args.elementAt(0).toString()) != null) ? Boolean.TRUE : Boolean.FALSE;
} catch (UnknownHostException e) {
return Boolean.FALSE;
}
}
};
- private static final JS.Function isInNet = new JS.Function() {
- public Object call(Context cx, JS thisObj, JS ctorObj, Object[] args) throws JS.Exn {
- if (args.length != 3) return Boolean.FALSE;
+ private static final JS.Callable isInNet = new JS.Callable() {
+ public Object call(org.xwt.js.Array args) throws JS.Exn {
+ if (args.length() != 3) return Boolean.FALSE;
try {
- byte[] host = InetAddress.getByName(args[0].toString()).getAddress();
- byte[] net = InetAddress.getByName(args[1].toString()).getAddress();
- byte[] mask = InetAddress.getByName(args[2].toString()).getAddress();
+ byte[] host = InetAddress.getByName(args.elementAt(0).toString()).getAddress();
+ byte[] net = InetAddress.getByName(args.elementAt(1).toString()).getAddress();
+ byte[] mask = InetAddress.getByName(args.elementAt(2).toString()).getAddress();
return ((host[0] & mask[0]) == net[0] &&
(host[1] & mask[1]) == net[1] &&
(host[2] & mask[2]) == net[2] &&
}
};
- private static final JS.Function dnsResolve = new JS.Function() {
- public Object call(Context cx, JS thisObj, JS ctorObj, Object[] args) throws JS.Exn {
+ private static final JS.Callable dnsResolve = new JS.Callable() {
+ public Object call(org.xwt.js.Array args) throws JS.Exn {
try {
- return InetAddress.getByName(args[0].toString()).getHostAddress();
+ return InetAddress.getByName(args.elementAt(0).toString()).getHostAddress();
} catch (UnknownHostException e) {
return null;
}
}
};
- private static final JS.Function myIpAddress = new JS.Function() {
- public Object call(Context cx, JS thisObj, JS ctorObj, Object[] args) throws JS.Exn {
+ private static final JS.Callable myIpAddress = new JS.Callable() {
+ public Object call(org.xwt.js.Array args) throws JS.Exn {
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
}
};
- private static final JS.Function dnsDomainLevels = new JS.Function() {
- public Object call(Context cx, JS thisObj, JS ctorObj, Object[] args) throws JS.Exn {
- String s = args[0].toString();
+ private static final JS.Callable dnsDomainLevels = new JS.Callable() {
+ public Object call(org.xwt.js.Array args) throws JS.Exn {
+ String s = args.elementAt(0).toString();
int i = 0;
while((i = s.indexOf('.', i)) != -1) i++;
return new Integer(i);
return false;
}
- private static final JS.Function shExpMatch = new JS.Function() {
- public Object call(Context cx, JS thisObj, JS ctorObj, Object[] args) throws JS.Exn {
- StringTokenizer st = new StringTokenizer(args[1].toString(), "*", false);
+ private static final JS.Callable shExpMatch = new JS.Callable() {
+ public Object call(org.xwt.js.Array args) throws JS.Exn {
+ StringTokenizer st = new StringTokenizer(args.elementAt(1).toString(), "*", false);
String[] arr = new String[st.countTokens()];
- String s = args[0].toString();
+ String s = args.elementAt(0).toString();
for (int i=0; st.hasMoreTokens(); i++) arr[i] = st.nextToken();
return match(arr, s, 0) ? Boolean.TRUE : Boolean.FALSE;
}
public static String[] days = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" };
- private static final JS.Function weekdayRange = new JS.Function() {
- public Object call(Context cx, JS thisObj, JS ctorObj, Object[] args) throws JS.Exn {
- TimeZone tz = (args.length < 3 || args[2] == null || !args[2].equals("GMT")) ? TimeZone.getTimeZone("UTC") : TimeZone.getDefault();
+ private static final JS.Callable weekdayRange = new JS.Callable() {
+ public Object call(org.xwt.js.Array args) throws JS.Exn {
+ TimeZone tz = (args.length() < 3 || args.elementAt(2) == null || !args.elementAt(2).equals("GMT")) ? TimeZone.getTimeZone("UTC") : TimeZone.getDefault();
Calendar c = new GregorianCalendar();
c.setTimeZone(tz);
- c.setTime(new Date());
- Date d = c.getTime();
+ c.setTime(new java.util.Date());
+ java.util.Date d = c.getTime();
int day = d.getDay();
- String d1s = args[0].toString().toUpperCase();
+ String d1s = args.elementAt(0).toString().toUpperCase();
int d1 = 0, d2 = 0;
for(int i=0; i<days.length; i++) if (days[i].equals(d1s)) d1 = i;
- if (args.length == 1)
+ if (args.length() == 1)
return d1 == day ? Boolean.TRUE : Boolean.FALSE;
- String d2s = args[1].toString().toUpperCase();
+ String d2s = args.elementAt(1).toString().toUpperCase();
for(int i=0; i<days.length; i++) if (days[i].equals(d2s)) d2 = i;
return
}
};
- private static final JS.Function dateRange = new JS.Function() {
- public Object call(Context cx, JS thisObj, JS ctorObj, Object[] args) throws JS.Exn {
+ private static final JS.Callable dateRange = new JS.Callable() {
+ public Object call(org.xwt.js.Array args) throws JS.Exn {
throw new JS.Exn("XWT does not support dateRange() in PAC scripts");
}
};
- private static final JS.Function timeRange = new JS.Function() {
- public Object call(Context cx, JS thisObj, JS ctorObj, Object[] args) throws JS.Exn {
+ private static final JS.Callable timeRange = new JS.Callable() {
+ public Object call(org.xwt.js.Array args) throws JS.Exn {
throw new JS.Exn("XWT does not support timeRange() in PAC scripts");
}
};
}
- */
+
}
/** Load an archive from an inputstream. */
public static synchronized void loadArchive(InputStream is) throws IOException { loadArchive(is, 0, null); }
- public static synchronized void loadArchive(InputStream is, final int length, final JS.Function callback) throws IOException {
+ public static synchronized void loadArchive(InputStream is, final int length, final JS.Callable callback) throws IOException {
// random placeholder
Object thisArchive = new Object();
int ret = super.read(b, off, len);
if (clear && callback != null) {
clear = false;
- ThreadMessage.newthread(new JS.Function(-1, "java", null, null) {
- public Object _call(JS.Array args_) throws JS.Exn {
+ ThreadMessage.newthread(new JS.Callable() {
+ public Object call(Array args_) throws JS.Exn {
try {
- JS.Array args = new JS.Array();
+ Array args = new Array();
args.addElement(new Double(bytesDownloaded));
args.addElement(new Double(length));
callback.call(args);
objects.addElement(null);
} else if (value.endsWith("arrayType") || value.endsWith("Array") || key.endsWith("arrayType")) {
objects.removeElementAt(objects.size() - 1);
- objects.addElement(new JS.Array());
+ objects.addElement(new Array());
}
}
}
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 Array) {
objects.removeElementAt(objects.size() - 1);
- ((JS.Array)parent).addElement(me);
+ ((Array)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 Array) {
+ Array a = (Array)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");
}
}
- protected String send(JS.Array args, HTTP http) throws JS.Exn, IOException {
+ protected String send(Array args, HTTP http) throws JS.Exn, IOException {
// build up the request
StringBuffer content = new StringBuffer();
content.append("SOAPAction: " + action + "\r\n\r\n");
else if (s.equals("pink")) newcolor = pink;
else if (s.equals("yellow")) newcolor = yellow;
else if (s.equals("white")) newcolor = white;
- else if (Log.on) Log.log(this, "invalid color \"" + s + "\" at " + JS.getCurrentFunctionSourceName());
+ else if (Log.on) Log.log(this, "invalid color \"" + s + "\" at " + Context.getCurrentSourceNameAndLine());
if (newcolor == b.color) return;
b.color = newcolor;
b.dirty();
else if (s.equals("pink")) newtextcolor = pink;
else if (s.equals("yellow")) newtextcolor = yellow;
else if (s.equals("white")) newtextcolor = white;
- else if (Log.on) Log.log(this, "invalid color \"" + s + "\" at " + JS.getCurrentFunctionSourceName());
+ else if (Log.on) Log.log(this, "invalid color \"" + s + "\" at " + Context.getCurrentSourceNameAndLine());
if (newtextcolor == b.textcolor) return;
b.textcolor = newtextcolor;
b.dirty();
specialBoxProperties.put("static", new SpecialBoxProperty() {
public Object get(Box b) {
- JS.Function cf = JS.getCurrentFunction();
- String cfsn = cf.getSourceName();
+ String cfsn = Context.getContextForThread(Thread.currentThread()).getCurrentFunction().getSourceName();
for(int i=0; i<cfsn.length() - 1; i++)
if (cfsn.charAt(i) == '.' && (cfsn.charAt(i+1) == '_' || Character.isDigit(cfsn.charAt(i+1)))) {
cfsn = cfsn.substring(0, i);
}
/** helper that converts a String to a int according to JavaScript coercion rules */
- public static class Apply extends JS.Function {
+ public static class Apply extends JS.Callable {
Box b;
- public Apply(Box b) { super(-1, "java", null, null); this.b = b; }
+ public Apply(Box b) { super(); this.b = b; }
- public Object _call(JS.Array args) throws JS.Exn {
+ public Object call(Array args) throws JS.Exn {
// apply a template
if (args.elementAt(0) instanceof String) {
String templatename = (String)args.elementAt(0);
Template t = Template.getTemplate(templatename, null);
if (t == null) {
- if (Log.on) Log.log(this, "template " + templatename + " not found at " + JS.getFileAndLine());
+ if (Log.on) Log.log(this, "template " + templatename + " not found at " + Context.getCurrentSourceNameAndLine());
} else {
if (ThreadMessage.suspendThread()) try {
- JS.Function callback = args.length() < 2 ? null : (Function)args.elementAt(1);
+ JS.Callable callback = args.length() < 2 ? null : (Callable)args.elementAt(1);
t.apply(b, null, null, callback, 0, t.numUnits());
} finally {
ThreadMessage.resumeThread();
private boolean changed = false;
/** the script on the static node of this template, null if it has already been executed */
- private JS.Function staticscript = null;
+ private CompiledFunction staticscript = null;
/** the script on this node */
- private JS.Function script = null;
+ private CompiledFunction script = null;
/** during XML parsing, this holds the list of currently-parsed children; null otherwise */
private Vec childvect = new Vec();
* @param pboxes a vector of all box parents on which to put $-references
* @param ptemplates a vector of the nodeNames to recieve private references on the pboxes
*/
- void apply(Box b, Vec pboxes, Vec ptemplates, JS.Function callback, int numerator, int denominator) {
+ void apply(Box b, Vec pboxes, Vec ptemplates, JS.Callable callback, int numerator, int denominator) {
int original_numerator = numerator;
if (redirect != null && !"self".equals(redirect)) redir = (Box)b.get("$" + redirect);
if (script != null) try {
- script.cloneWithNewParentScope(b).call(new JS.Array());
+ script.cloneWithNewParentScope(b).call(new Array());
+ } catch (IOException e) {
+ throw new Error("this should never happen");
} catch (JS.Exn e) {
if (Log.on) Log.log(this, "WARNING: uncaught ecmascript exception: " + e.getMessage());
}
if (callback != null)
try {
- JS.Array args = new JS.Array();
+ Array args = new Array();
args.addElement(new Double(numerator));
args.addElement(new Double(denominator));
callback.call(args);
}
/** adds a theme mapping, retemplatizing as needed */
- public static void retheme(JS.Function callback) {
+ public static void retheme(JS.Callable callback) {
XWF.flushXWFs();
// clear changed marker and relink
if (callback != null)
try {
- JS.Array args = new JS.Array();
+ Array args = new Array();
args.addElement(new Double(1.0));
args.addElement(new Double(1.0));
callback.call(args);
if (staticscript != null) try {
JS.Scope s = Static.createStatic(nodeName, false);
if (staticscript != null) {
- JS.Function temp = staticscript;
+ CompiledFunction temp = staticscript;
staticscript = null;
// we layer a transparent scope over the Static so that we can catch requests for the xwt object
temp.cloneWithNewParentScope(varScope).call(null);
}
+ } catch (IOException e) {
+ throw new Error("this should never happen");
} catch (JS.Exn e) {
if (Log.on) Log.log(this, "WARNING: uncaught ecmascript exception: " + e.getMessage());
}
}
}
- private JS.Function genscript(boolean isstatic) {
- JS.Function thisscript = null;
+ private CompiledFunction genscript(boolean isstatic) {
+ CompiledFunction thisscript = null;
try {
- thisscript = JS.parse(new StringReader(t.content.toString()), t.nodeName + (isstatic ? "._" : ""), t.content_start);
+ thisscript = JS.parse(t.nodeName + (isstatic ? "._" : ""), t.content_start, new StringReader(t.content.toString()), null);
} catch (JS.Exn ee) {
if (Log.on) Log.log(this, " ERROR: " + ee.getMessage());
thisscript = null;
private volatile static int threadcount = 0;
/** the JavaScript function that we are executing */
- volatile JS.Function f;
+ volatile JS.Callable f;
/** the ThreadMessage thread blocks on this before executing any JavaScript */
Semaphore go = new Semaphore();
private static Object[] emptyobj = new Object[] { };
/** creates a new thread to execute function <tt>f</tt> */
- public static synchronized void newthread(JS.Function f) {
+ public static synchronized void newthread(JS.Callable f) {
ThreadMessage ret = (ThreadMessage)spare.remove(false);
if (ret == null) {
if (threadcount < Platform.maxThreads()) ret = new ThreadMessage();
// put ourselves in the background
Thread thread = Thread.currentThread();
if (!(thread instanceof ThreadMessage)) {
- if (Log.on) Log.log(ThreadMessage.class, "attempt to perform background-only operation in a foreground thread at " + JS.getCurrentFunctionSourceName());
+ if (Log.on) Log.log(ThreadMessage.class, "attempt to perform background-only operation in a foreground thread at " + Context.getCurrentSourceNameAndLine());
return false;
}
ThreadMessage mythread = (ThreadMessage)thread;
while (true) {
try {
go.block();
- f.call(new JS.Array());
+ f.call(new Array());
} catch (JS.Exn e) {
if (Log.on) Log.log(this, "WARNING: uncaught ecmascript exception: " + e);
}
done.release();
synchronized(waiting) {
if (waiting.size() > 0) {
- f = (JS.Function)waiting.remove(false);
+ f = (JS.Callable)waiting.remove(false);
MessageQueue.add(this);
} else if (spare.size() < 10) {
spare.append(this);
private JS rp = null;
/** the function for this trap */
- JS.Function f = null;
+ CompiledFunction f = null;
/** the name of the property that this trap was placed on */
private String name = null;
* @param isreadtrap true iff this is a read (double-underscore) trap
* @param rp if this trap is being placed via a rootproxy, this is that proxy object.
*/
- static void addTrap(Box trapee, String name, JS.Function f, boolean isreadtrap, JS rp) {
+ static void addTrap(Box trapee, String name, CompiledFunction f, boolean isreadtrap, JS rp) {
if (PROHIBITED.get(name) != null || name.startsWith("xwt_")) {
Log.log(Trap.class, "Error: you cannot place traps on special property \"" + name + "\"");
}
// find out what script is currently running
- String placerNodeName = JS.getCurrentFunctionSourceName();
+ CompiledFunction placer = Context.getContextForThread(Thread.currentThread()).getCurrentFunction();
+ if (placer == null) { Log.log(Trap.class, "placer is null"); return; }
+ String placerNodeName = placer.getSourceName();
// check if this script has already placed a trap on this property
if (trapee.traps == null) trapee.traps = new Hash(10, 3);
public static Trap getTrap(Box b, String name) {
if (b.traps == null) return null;
- String currentFunctionNodeName = JS.getCurrentFunctionSourceName();
+ String currentFunctionNodeName = Context.getContextForThread(Thread.currentThread()).getCurrentFunction().getSourceName();
for(Trap cur = (Trap)b.traps.get(name); cur != null; cur = cur.next)
if (cur.placerNodeName.equals(currentFunctionNodeName))
return cur;
/** Called by Rhino's arguments.cascade. Note: cx will be null if this was invoked from perform() rather than from a script. */
public static final CascadeFunction cascadeFunction = new CascadeFunction();
- private static class CascadeFunction extends JS.Function {
- CascadeFunction() { super(-1, "java", null, null); setSeal(true); }
- public Object _call(JS.Array args) { return _call(args, JS.getCurrentFunction()); }
- public Object _call(JS.Array args, Function currentFunction) {
+ private static class CascadeFunction extends JS.Callable {
+ CascadeFunction() { setSeal(true); }
+ public Object call(Array args) { return call(args, Context.getContextForThread(Thread.currentThread()).getCurrentFunction()); }
+ public Object call(Array args, CompiledFunction currentFunction) {
Trap currentTrap = TrapContext.get().currentTrap;
if (args.length() != 0) TrapContext.get().putCascadeHappened = true;
Trap t = currentTrap.next;
private Trap() { allTraps.put(myWeak, dummy); }
/** perform this trap -- arg.length == 0 if this is a get; otherwise it contains a single element to be put */
- public Object perform(JS.Array args) {
+ public Object perform(Array args) {
TrapContext tc = TrapContext.get();
// save both thread-locals on the stack and update their values
// invoke the trap function
try {
- if (!isreadtrap && args.length() == 0) return cascadeFunction._call(args, f);
+ if (!isreadtrap && args.length() == 0) return cascadeFunction.call(args, f);
if (f == null) {
if (Log.verbose) Log.log(this, "debug: reclaimed a dangling trap on property " + name);
- Object ret = cascadeFunction._call(args, f);
+ Object ret = cascadeFunction.call(args, f);
delete();
return ret;
}
Object ret = f.call(args);
// autocascade if required
- if (args.length() > 0 && !isreadtrap && !tc.putCascadeHappened) cascadeFunction._call(args, f);
+ if (args.length() > 0 && !isreadtrap && !tc.putCascadeHappened) cascadeFunction.call(args, f);
return ret;
* convert.
* </ol>
*/
-class XMLRPC extends JS.Function {
+class XMLRPC extends JS.Callable {
public Object[] keys() { throw new Error("not implemented"); }
} else if (c.localName.equals("data")) {
int i;
for(i=objects.size() - 1; objects.elementAt(i) != null; i--);
- JS.Array arr = new JS.Array();
+ Array arr = new Array();
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("</dateTime.iso8601></value>\n");
*/
- } else if (o instanceof JS.Array) {
+ } else if (o instanceof Array) {
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;
+ Array a = (Array)o;
for(int i=0; i<a.length(); i++) appendObject(a.elementAt(i), sb);
sb.append(" </data></array></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 synchronized Object call2(Array args) throws JS.Exn, IOException {
if (Log.verbose) Log.log(this, "call to " + url + " : " + methodname);
if (tracker == null) tracker = new Hash();
}
}
- protected String send(JS.Array args, HTTP http) throws JS.Exn, IOException {
+ protected String send(Array 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(JS.Array args) throws JS.Exn {
+ public final Object call(Array args) throws JS.Exn {
if (!ThreadMessage.suspendThread()) return null;
}
public XMLRPC(String url, String methodname, HTTP http) {
- super(-1, "java", null, null);
this.http = http;
this.url = url;
this.methodname = methodname;
}
public void put(Object name, Object value) {
- if (name.equals("thread") && value != null && value instanceof JS.Function) ThreadMessage.newthread((JS.Function)value);
+ if (name.equals("thread") && value != null && value instanceof JS.Callable) ThreadMessage.newthread((JS.Callable)value);
else if (name.equals("clipboard")) Platform.setClipBoard(value.toString());
else if (name.equals("proxyAuthorization")) {
// FIXME: undocumented, possibly insecure
put("fileSeparator", File.separator);
put("homeDir", System.getProperty("user.home"));
put("tempDir", System.getProperty("java.io.tempdir"));
+ put("math", org.xwt.js.Math.singleton);
- put("math", new JS.Obj() { public Object get(Object name) {
- if ("ceil".equals(name)) return new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args)
- { if (args.elementAt(0) == null) return null;
- return new Long((long)Math.ceil(Double.parseDouble(args.elementAt(0).toString()))); } };
- else if ("floor".equals(name)) return new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args)
- { if (args.elementAt(0) == null) return null;
- return new Long((long)Math.floor(Double.parseDouble(args.elementAt(0).toString()))); } };
- else if ("round".equals(name)) return new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args)
- { if (args.elementAt(0) == null) return null;
- return new Long((long)Math.round(Double.parseDouble(args.elementAt(0).toString()))); } };
- else if ("abs".equals(name)) return new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args)
- { if (args.elementAt(0) == null) return null;
- return new Long((long)Math.abs(Double.parseDouble(args.elementAt(0).toString()))); } };
- else if ("min".equals(name)) return new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) {
- if (args.length() < 2 || args.elementAt(0) == null || args.elementAt(1) == null) return args.elementAt(0);
- return new Double(Math.min(((Number)args.elementAt(0)).doubleValue(),
- ((Number)args.elementAt(1)).doubleValue())); } };
- else if ("max".equals(name)) return new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) {
- if (args.length() < 2) return args.elementAt(0);
- return new Double(Math.max(((Number)args.elementAt(0)).doubleValue(),
- ((Number)args.elementAt(1)).doubleValue())); } };
- else if ("cos".equals(name)) return new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) {
- return new Double(Math.cos(((Number)args.elementAt(0)).doubleValue())); } };
- else if ("sin".equals(name)) return new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) {
- return new Double(Math.sin(((Number)args.elementAt(0)).doubleValue())); } };
- else if ("tan".equals(name)) return new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) {
- return new Double(Math.tan(((Number)args.elementAt(0)).doubleValue())); } };
- else if ("acos".equals(name)) return new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) {
- return new Double(Math.acos(((Number)args.elementAt(0)).doubleValue())); } };
- else if ("asin".equals(name)) return new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) {
- return new Double(Math.asin(((Number)args.elementAt(0)).doubleValue())); } };
- else if ("atan".equals(name)) return new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) {
- return new Double(Math.atan(((Number)args.elementAt(0)).doubleValue())); } };
- else if ("sqrt".equals(name)) return new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) {
- return new Double(Math.sqrt(((Number)args.elementAt(0)).doubleValue())); } };
- return null;
- }});
-
- put("newBrowserWindow", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("newBrowserWindow", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (args.length() != 1 || args.elementAt(0) == null) return null;
Platform.newBrowserWindow(args.elementAt(0).toString());
return null;
}});
- put("parseFloat", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("parseFloat", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (args.length() != 1 || args.elementAt(0) == null) return null;
return new Float(args.elementAt(0).toString());
}});
- put("parseInt", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("parseInt", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (args.length() != 1 || args.elementAt(0) == null) return null;
return new Float(args.elementAt(0).toString());
}});
- put("yield", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("yield", new JS.Callable() { public Object call(Array args) throws JS.Exn {
sleep(0);
return null;
}});
- put("theme", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("theme", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (args.length() != 2) return null;
if (args.elementAt(0) == null || args.elementAt(1) == null) return null;
for(int i=1; i<args.length(); i++) {
Resources.mapTo.addElement(to);
}
}
- JS.Function callback = args.elementAt(args.length() - 1) instanceof Function ?
- (Function)args.elementAt(args.length() - 1) : null;
+ JS.Callable callback = args.elementAt(args.length() - 1) instanceof JS.Callable ?
+ (JS.Callable)args.elementAt(args.length() - 1) : null;
Template.retheme(callback);
return null;
}});
- put("println", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("println", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (args.length() != 1) return null;
- if (Log.on) Log.log(this, JS.getFileAndLine() + " " +
+ if (Log.on) Log.log(this, Context.getCurrentSourceNameAndLine() + " " +
(args.elementAt(0) == null ? "**null**" : args.elementAt(0).toString()));
return null;
}});
- put("date", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("date", new JS.Callable() { public Object call(Array args) throws JS.Exn {
Log.log(XWT.class, "date not implemented");
//throw new Error("not implemented");
return null;
}});
- put("regexp", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("regexp", new JS.Callable() { public Object call(Array args) throws JS.Exn {
//throw new Error("not implemented");
Log.log(XWT.class, "regexp not implemented");
return null;
}});
- put("listfonts", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("listfonts", new JS.Callable() { public Object call(Array args) throws JS.Exn {
Object[] fonts = Platform.listFonts();
- JS.Array ret = new JS.Array();
+ Array ret = new Array();
for(int i=0; i<fonts.length; i++) ret.addElement(fonts[i]);
return ret;
}});
- put("xmlrpc", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("xmlrpc", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (args.length() != 1 || args.elementAt(0) == null) return null;
return new XMLRPC(args.elementAt(0).toString(), "");
}});
- put("soap", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("soap", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (args.length() == 1 && args.elementAt(0) != null) return new SOAP(args.elementAt(0).toString(), "", null, null);
else if (args.length() == 2 && args.elementAt(0) != null && args.elementAt(1) != null)
return new SOAP(args.elementAt(0).toString(), "", args.elementAt(1).toString(), null);
else return null;
}});
- put("textwidth", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("textwidth", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (args.length() < 1 || args.length() > 2) return null;
if (args.elementAt(0) == null || (args.length() == 2 && args.elementAt(1) == null)) return null;
String font = args.length() == 1 ? Platform.getDefaultFont() : args.elementAt(0).toString();
else return new Integer(xwf.stringWidth(text));
}});
- put("textheight", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("textheight", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (args.length() > 1) return null;
if (args.length() == 1 && args.elementAt(0) == null) return null;
String font = args.length() == 0 || args.elementAt(0) == null ? Platform.getDefaultFont() : args.elementAt(0).toString();
else return new Integer(xwf.getMaxAscent() + xwf.getMaxDescent());
}});
- put("newBox", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("newBox", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (args.length() > 0) Log.log(XWT.class, "DEPRECATED: xwt.newBox() with multiple arguments is deprecated; use xwt.newBox().apply()");
- JS.Function callback = null;
+ JS.Callable callback = null;
for(int i=1; i<args.length(); i++)
- if (args.elementAt(i) instanceof JS.Function && callback == null)
- callback = (JS.Function)args.elementAt(i);
+ if (args.elementAt(i) instanceof JS.Callable && callback == null)
+ callback = (JS.Callable)args.elementAt(i);
Box ret = new Box(args.length() == 0 || args.elementAt(0) == null ? "box" : args.elementAt(0).toString(),
Template.defaultImportList, callback);
for(int i=1; i<args.length(); i++)
if (args.elementAt(i) instanceof Box)
ret.put(ret.numChildren(), (Box)args.elementAt(i));
for(int i=1; i<args.length(); i++)
- if (args.elementAt(i) instanceof JS && !(args.elementAt(i) instanceof Box) && !(args.elementAt(i) instanceof JS.Function)) {
+ if (args.elementAt(i) instanceof JS && !(args.elementAt(i) instanceof Box) && !(args.elementAt(i) instanceof JS.Callable)) {
JS s = (JS)args.elementAt(i);
Object[] keys = s.keys();
for(int j=0; j<keys.length; j++) ret.put(keys[j].toString(), s.get(keys[j].toString()));
return ret;
}});
- put("sleep", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("sleep", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (args != null && (args.length() != 1 || args.elementAt(0) == null)) return null;
int i = args == null ? 0 : SpecialBoxProperty.stoi(args.elementAt(0).toString());
sleep(i);
return null;
}});
- put("openFile", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("openFile", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (args.length() != 1) return null;
String file = Platform.fileDialog(args.elementAt(0).toString(), false);
return file == null ? null : new ByteStream(file);
}});
- put("saveFile", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("saveFile", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (args.length() != 2) return null;
if (!(args.elementAt(1) instanceof ByteStream)) return null;
String file = args.elementAt(0).toString();
}
}});
- put("saveFileAs", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("saveFileAs", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (args.length() != 2) return null;
if (!(args.elementAt(1) instanceof ByteStream)) return null;
String file = args.elementAt(0).toString();
}
}});
- put("utfEncode", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("utfEncode", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (args == null || args.length() != 1) return null;
return new ByteStream(args.elementAt(0).toString().getBytes());
}});
- put("parseHTML", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("parseHTML", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (args == null || args.length() != 1 || args.elementAt(0) == null) return null;
try {
if (args.elementAt(0) instanceof ByteStream) {
}
});
- put("recursivePrintObject", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) {
+ put("recursivePrintObject", new JS.Callable() { public Object call(Array args) {
if (args.length() != 1) return null;
recurse("", "", args.elementAt(0));
return null;
}});
- put("loadArchive", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("loadArchive", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (!ThreadMessage.suspendThread()) return null;
try {
if (args == null || args.length() < 1 || args.elementAt(0) == null) return null;
URL u = new URL(args.elementAt(0).toString());
- JS.Function callback = null;
- if (args.length() == 2 && args.elementAt(1) != null && args.elementAt(1) instanceof JS.Function)
- callback = (JS.Function)args.elementAt(1);
+ JS.Callable callback = null;
+ if (args.length() == 2 && args.elementAt(1) != null && args.elementAt(1) instanceof JS.Callable)
+ callback = (JS.Callable)args.elementAt(1);
if (!u.getFile().endsWith(".xwar")) {
if (Log.on) Log.log(this, "Error: archive names must end with .xwar: " + u.getFile());
return null;
}});
- put("prefetchImage", new JS.Function(-1, "java", null, null) { public Object _call(JS.Array args) throws JS.Exn {
+ put("prefetchImage", new JS.Callable() { public Object call(Array args) throws JS.Exn {
if (args == null || args.length() < 1 || args.elementAt(0) == null) return null;
Box.getImage(args.elementAt(0).toString(),
- args.length() > 1 && args.elementAt(1) instanceof JS.Function ? (JS.Function)args.elementAt(1) : null);
+ args.length() > 1 && args.elementAt(1) instanceof JS.Callable ? (JS.Callable)args.elementAt(1) : null);
return null;
}});
}
if (!name.equals("")) name += " : ";
if (o == null) {
- Log.log(JS.getCurrentFunction().getSourceName(), indent + name + "<null>");
+ Log.log(Context.getCurrentSourceNameAndLine() , indent + name + "<null>");
- } else if (o instanceof JS.Array) {
- Log.log(JS.getCurrentFunction().getSourceName(), indent + name + "<array>");
- JS.Array na = (JS.Array)o;
+ } else if (o instanceof Array) {
+ Log.log(Context.getCurrentSourceNameAndLine() , indent + name + "<array>");
+ Array na = (Array)o;
for(int i=0; i<na.length(); i++)
recurse(indent + " ", i + "", na.elementAt(i));
} else if (o instanceof JS) {
- Log.log(JS.getCurrentFunction().getSourceName(), indent + name + "<object>");
+ Log.log(Context.getCurrentSourceNameAndLine() , indent + name + "<object>");
JS s = (JS)o;
Object[] keys = s.keys();
for(int i=0; i<keys.length; i++)
s.get(((Integer)keys[i])) : s.get(keys[i].toString()));
} else {
- Log.log(JS.getCurrentFunction().getSourceName(), indent + name + o);
+ Log.log(Context.getCurrentSourceNameAndLine(), indent + name + o);
}
}
--- /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.*;
+
+/** A JavaScript Array */
+public class Array extends JS.Obj {
+ private Vec vec = new Vec();
+ public Array() { }
+ public Array(int size) { vec.setSize(size); }
+ private static int intVal(Object o) {
+ if (o instanceof Number) {
+ int intVal = ((Number)o).intValue();
+ if (intVal == ((Number)o).doubleValue()) return intVal;
+ return Integer.MIN_VALUE;
+ }
+ if (!(o instanceof String)) return Integer.MIN_VALUE;
+ String s = (String)o;
+ for(int i=0; i<s.length(); i++) if (s.charAt(i) < '0' || s.charAt(i) > '9') return Integer.MIN_VALUE;
+ return Integer.parseInt(s);
+ }
+ public Object get(Object key) throws JS.Exn {
+ // FIXME: HACK!
+ if (key.equals("cascade")) return org.xwt.Trap.cascadeFunction;
+ if (key.equals("trapee")) return org.xwt.Trap.currentTrapee();
+ if (key.equals("length")) return new Long(vec.size());
+ int i = intVal(key);
+ if (i == Integer.MIN_VALUE) return super.get(key);
+ try {
+ return vec.elementAt(i);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return null;
+ }
+ }
+ 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);
+ else {
+ if (i >= vec.size()) vec.setSize(i+1);
+ 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 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); }
+}
+
+++ /dev/null
-// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
-package org.xwt.js;
-
-import org.xwt.util.*;
-import java.io.*;
-
-/** A block of JavaScript bytecode run on a very simple stack machine. */
-public class ByteCodeBlock implements ByteCodes, Tokens {
-
- // FIXME: this should be line-by-line
- private int line;
- private String sourceName;
-
- /** the instructions */
- private int[] op = new int[10];
-
- /** the arguments to the instructions */
- private Object[] arg = new Object[10];
-
- /** the number of instruction/argument pairs */
- private int size = 0;
-
- public ByteCodeBlock(int line, String sourceName) { this.line = line; this.sourceName = sourceName; }
- public String getSourceName() { return sourceName; }
-
- public int size() { return size; }
- public void set(int pos, int op_, Object arg_) { op[pos] = op_; arg[pos] = arg_; }
- public void set(int pos, Object arg_) { arg[pos] = arg_; }
- public void paste(ByteCodeBlock other) { for(int i=0; i<other.size; i++) add(other.op[i], other.arg[i]); }
- public ByteCodeBlock add(int op_) { return add(op_, null); }
- public ByteCodeBlock add(int op_, Object arg_) {
- if (size == op.length - 1) {
- int[] op2 = new int[op.length * 2]; System.arraycopy(op, 0, op2, 0, op.length); op = op2;
- Object[] arg2 = new Object[op.length * 2]; System.arraycopy(arg, 0, arg2, 0, arg.length); arg = arg2;
- }
- op[size] = op_;
- arg[size] = arg_;
- size++;
- return this;
- }
-
- public void eval(JS.Scope s) throws ControlTransferException {
- Vec t = Context.getContextForCurrentThread().stack;
- OUTER: for(int i=0; i<size; i++) {
- switch(op[i]) {
- case LABEL: break;
- case LITERAL: t.push(arg[i]); break;
- case OBJECT: t.push(new JS.Obj()); break;
- case ARRAY: t.push(new JS.Array(JS.toNumber(arg[i]).intValue())); break;
- case DECLARE: s.declare((String)t.pop()); break;
- case TOPSCOPE: t.push(s); break;
- case JT: if (JS.toBoolean(t.pop())) i += JS.toNumber(arg[i]).intValue() - 1; break;
- case JF: if (!JS.toBoolean(t.pop())) i += JS.toNumber(arg[i]).intValue() - 1; break;
- case JMP: i += JS.toNumber(arg[i]).intValue() - 1; break;
- case POP: t.pop(); break;
- case SWAP: { Object o1 = t.pop(); Object o2 = t.pop(); t.push(o1); t.push(o2); break; }
- case DUP: t.push(t.peek()); break;
-
- case PUSHSCOPE: s = new JS.Scope(s); break;
- case POPSCOPE: s = s.getParentScope(); break;
-
- case ASSERT: if (!JS.toBoolean(t.pop())) throw new EvaluatorException(line, sourceName, "assertion failed"); break;
- case RETURN: {
- Object retval = t.pop();
- while(t.size() > 0) {
- Object o = t.pop();
- if (o != null && o instanceof Context.CallMarker) {
- t.push(retval);
- return;
- }
- }
- throw new Error("error: RETURN invoked but couldn't find a CallMarker!");
- }
-
- case THROW: throw new JS.Exn(t.pop());
- case TRY: break;
- case TYPEOF: break; // FIXME: implement
-
- case BITNOT: t.push(new Long(~JS.toLong(t.pop()))); break;
- case BANG: t.push(new Boolean(!JS.toBoolean(t.pop()))); break;
-
- case NEWFUNCTION: {
- ByteCodeBlock bytes = (ByteCodeBlock)arg[i];
- t.push(new JS.Function(bytes.line, bytes.sourceName, bytes, s));
- break;
- }
-
- case PUSHKEYS: {
- Object o = t.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);
- t.push(a);
- break;
- }
-
- case LOOP: {
- t.push(s);
- t.push(new Context.LoopMarker(i));
- t.push(Boolean.TRUE);
- break;
- }
-
- case BREAK:
- case CONTINUE:
- while(t.size() > 0) {
- Object o = t.pop();
- if (o != null && o instanceof Context.LoopMarker) {
- int loopInstructionLocation = ((Context.LoopMarker)o).location;
- int endOfLoop = ((Integer)arg[loopInstructionLocation]).intValue() + loopInstructionLocation;
- s = (JS.Scope)t.pop();
- if (op[i] == CONTINUE) { t.push(s); t.push(o); t.push(Boolean.FALSE); }
- i = op[i] == BREAK ? endOfLoop - 1 : loopInstructionLocation;
- continue OUTER;
- }
- }
- throw new Error("CONTINUE/BREAK invoked but couldn't find a LoopMarker at " + sourceName + ":" + line);
-
- /*
- ByteCodeBlock loop = (ByteCodeBlock)arg[i];
- Vec stack2 = new Vec();
- Context cur = Context.getContextForCurrentThread();
- boolean first = true;
- try {
- while (true) {
- Boolean result;
- try {
- Context cx = new Context();
- cx.bindToCurrentThread();
- cx.stack.push(new Boolean(first));
- loop.eval(new JS.Scope(s));
- result = (Boolean)cx.stack.pop();
- } catch (ContinueException c) { result = Boolean.TRUE;
- } catch (BreakException b) { result = Boolean.FALSE;
- }
- first = false;
- if (result == Boolean.FALSE) break;
- }
- } finally {
- cur.bindToCurrentThread();
- }
- break;
- }
- */
-
- case PUT: {
- Object val = t.pop();
- Object key = t.pop();
- Object target = t.peek();
- if (target == null)
- throw new JS.Exn(sourceName + ":" + line + ": tried to put a value to the " + key +
- " property on the null value");
- if (!(target instanceof JS))
- throw new JS.Exn(sourceName + ":" + line + ": tried to put a value to the " + key +
- " property on a " + target.getClass().getName());
- ((JS)target).put(key, val);
- t.push(val);
- break;
- }
-
- case GET: {
- Object v = t.pop();
- Object o = t.pop();
- t.push(doGet(o, v));
- break;
- }
-
- case GET_PRESERVE: {
- Object v = t.pop();
- Object o = t.peek();
- t.push(v);
- t.push(doGet(o, v));
- break;
- }
-
- case CALL: {
- JS.Array arguments = new JS.Array();
- int numArgs = JS.toNumber(arg[i]).intValue();
- arguments.setSize(numArgs);
- for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(t.pop(), j);
- JS.Function f = (JS.Function)t.pop();
- if (f == null) throw new JS.Exn(sourceName + ":" + line + ": attempted to call null");
- t.push(f.call(arguments));
- break;
- }
-
- case INC: case DEC: {
- boolean isPrefix = JS.toBoolean(arg[i]);
- Object key = t.pop();
- JS obj = (JS)t.pop();
- Number num = JS.toNumber(obj.get(key));
- Number val = new Double(op[i] == INC ? num.doubleValue() + 1.0 : num.doubleValue() - 1.0);
- obj.put(key, val);
- t.push(isPrefix ? val : num);
- break;
- }
-
- default: {
- Object right = t.pop();
- Object left = t.pop();
- switch(op[i]) {
-
- case ADD: {
- Object l = left;
- Object r = right;
- if (l instanceof String || r instanceof String) {
- if (l == null) l = "null";
- if (r == null) r = "null";
- if (l instanceof Number && ((Number)l).doubleValue() == ((Number)l).longValue())
- l = new Long(((Number)l).longValue());
- if (r instanceof Number && ((Number)r).doubleValue() == ((Number)r).longValue())
- r = new Long(((Number)r).longValue());
- t.push(l.toString() + r.toString()); break;
- }
- t.push(new Double(JS.toDouble(l) + JS.toDouble(r))); break;
- }
-
- case BITOR: t.push(new Long(JS.toLong(left) | JS.toLong(right))); break;
- case BITXOR: t.push(new Long(JS.toLong(left) ^ JS.toLong(right))); break;
- case BITAND: t.push(new Long(JS.toLong(left) & JS.toLong(right))); break;
-
- case SUB: t.push(new Double(JS.toDouble(left) - JS.toDouble(right))); break;
- case MUL: t.push(new Double(JS.toDouble(left) * JS.toDouble(right))); break;
- case DIV: t.push(new Double(JS.toDouble(left) / JS.toDouble(right))); break;
- case MOD: t.push(new Double(JS.toDouble(left) % JS.toDouble(right))); break;
-
- case LSH: t.push(new Long(JS.toLong(left) << JS.toLong(right))); break;
- case RSH: t.push(new Long(JS.toLong(left) >> JS.toLong(right))); break;
- case URSH: t.push(new Long(JS.toLong(left) >>> JS.toLong(right))); break;
-
- case LT: t.push(JS.toDouble(left) < JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
- case LE: t.push(JS.toDouble(left) <= JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
- case GT: t.push(JS.toDouble(left) > JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
- case GE: t.push(JS.toDouble(left) >= JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
-
- case EQ:
- case NE: {
- Object l = left;
- Object r = right;
- boolean ret;
- if (l == null) { Object tmp = r; r = l; l = tmp; }
- if (l == null && r == null) ret = true;
- else if (l instanceof Boolean) ret = new Boolean(JS.toBoolean(r)).equals(l);
- else if (l instanceof Number) ret = JS.toNumber(r).doubleValue() == JS.toNumber(l).doubleValue();
- else if (l instanceof String) ret = r != null && l.equals(r.toString());
- else ret = l.equals(r);
- t.push(new Boolean(op[i] == EQ ? ret : !ret)); break;
- }
-
- default: throw new Error("unknown opcode " + op[i]);
- } }
- }
- }
- }
-
- public Object doGet(final Object o, final Object v) {
- if (o == null)
- throw new JS.Exn(sourceName + ":" + line + ": tried to get property \"" + v + "\" from the null value");
- if (o instanceof String) {
- if (v.equals("length")) return new Integer(((String)o).length());
- else if (v.equals("substring")) return new JS.Function(-1, "java", null, null) {
- public Object _call(JS.Array args) {
- if (args.length() == 1) return ((String)o).substring(JS.toNumber(args.elementAt(0)).intValue());
- else if (args.length() == 2) return ((String)o).substring(JS.toNumber(args.elementAt(0)).intValue(),
- JS.toNumber(args.elementAt(1)).intValue());
- else throw new Error("String.substring() can only take one or two arguments");
- }
- };
- else if (v.equals("toLowerCase")) return new JS.Function(-1, "java", null, null) {
- public Object _call(JS.Array args) {
- return ((String)o).toLowerCase();
- } };
- else if (v.equals("toUpperCase")) return new JS.Function(-1, "java", null, null) {
- public Object _call(JS.Array args) {
- return ((String)o).toString().toUpperCase();
- } };
- else if (v.equals("charAt")) return new JS.Function(-1, "java", null, null) {
- public Object _call(JS.Array args) {
- return ((String)o).charAt(JS.toNumber(args.elementAt(0)).intValue()) + "";
- } };
- else if (v.equals("lastIndexOf")) return new JS.Function(-1, "java", null, null) {
- public Object _call(JS.Array args) {
- if (args.length() != 1) return null;
- return new Integer(((String)o).lastIndexOf(args.elementAt(0).toString()));
- } };
- else if (v.equals("indexOf")) return new JS.Function(-1, "java", null, null) {
- public Object _call(JS.Array args) {
- if (args.length() != 1) return null;
- return new Integer(((String)o).indexOf(args.elementAt(0).toString()));
- } };
- throw new Error("Not Implemented: propery " + v + " on String objects");
- } else if (o instanceof Boolean) {
- throw new Error("Not Implemented: properties on Boolean objects");
- } else if (o instanceof Number) {
- Log.log(this, "Not Implemented: properties on Number objects");
- return null;
- //throw new Error("Not Implemented: properties on Number objects");
- } else if (o instanceof JS) {
- return ((JS)o).get(v);
- }
- return null;
- }
-
- static class EvaluatorException extends RuntimeException {
- public EvaluatorException(int line, String sourceName, String s) {
- super(sourceName + ":" + line + " " + s);
- }
- }
-
- static abstract class ControlTransferException extends RuntimeException { }
- static class BreakException extends ControlTransferException {
- public String label;
- BreakException(String label) { this.label = label; }
- }
- static class ContinueException extends ControlTransferException {
- public String label;
- ContinueException(String label) { this.label = label; }
- }
- public static class ReturnException extends ControlTransferException {
- public Object retval;
- ReturnException(Object retval) { this.retval = retval; }
- }
-
-}
public static final byte SWAP = -17;
/** execute the ForthBlock pointed to by the literal in a fresh scope with parentScope==THIS */
- public static final byte PUSHSCOPE = -18;
+ public static final byte NEWSCOPE = -18;
/** execute the ForthBlock pointed to by the literal in a fresh scope with parentScope==THIS */
- public static final byte POPSCOPE = -19;
+ public static final byte OLDSCOPE = -19;
/** push a copy of the top stack element */
public static final byte DUP = -20;
- /** declare a label */
- public static final byte LABEL = -21;
+ /** a NOP; confers a label upon the following instruction */
+ public static final byte LABEL = -21;
/** execute the ForthBlock pointed to by the literal until BREAK encountered; push TRUE onto the stack for the first iteration
* and FALSE for all subsequent iterations */
--- /dev/null
+// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt.js;
+
+import org.xwt.util.*;
+import java.io.*;
+
+/** a JavaScript function, compiled into bytecode */
+public final class CompiledFunction extends JS.Callable implements ByteCodes, Tokens {
+
+ // Fields and Accessors ///////////////////////////////////////////////
+
+ /** the source code file that this block was drawn from */
+ private String sourceName;
+ public String getSourceName() throws JS.Exn { return sourceName; }
+
+ /** the first line of this function */
+ private int firstLine;
+ public int getFirstLine() throws JS.Exn { return firstLine; }
+
+ /** the line numbers */
+ private int[] line = new int[10];
+
+ /** the instructions */
+ private int[] op = new int[10];
+
+ /** the arguments to the instructions */
+ private Object[] arg = new Object[10];
+
+ /** the number of instruction/argument pairs */
+ private int size = 0;
+ int size() { return size; }
+
+ /** the scope in which this function was declared */
+ private JS.Scope parentScope;
+ JS.Scope getParentScope() { return parentScope; }
+
+
+ // Constructors ////////////////////////////////////////////////////////
+
+ public CompiledFunction cloneWithNewParentScope(JS.Scope s) throws IOException {
+ CompiledFunction ret = new CompiledFunction(sourceName, firstLine, s);
+ ret.paste(this);
+ return ret;
+ }
+
+ CompiledFunction(String sourceName, int firstLine, JS.Scope parentScope) throws IOException {
+ this.sourceName = sourceName;
+ this.firstLine = firstLine;
+ this.parentScope = parentScope;
+ }
+
+ CompiledFunction(String sourceName, int firstLine, Reader sourceCode, JS.Scope parentScope) throws IOException {
+ this(sourceName, firstLine, parentScope);
+ Parser p = new Parser(sourceCode, sourceName, firstLine);
+ try {
+ while(true) {
+ int s = size();
+ p.parseStatement(false, this);
+ if (s == size()) break;
+ }
+ add(-1, Tokens.RETURN);
+ } catch (Exception e) {
+ if (Log.on) Log.log(Parser.class, e);
+ }
+ }
+
+ public Object call(Array args) throws JS.Exn {
+ CompiledFunction saved = (CompiledFunction)Context.currentFunction.get(Thread.currentThread());
+ try {
+ Context.currentFunction.put(Thread.currentThread(), this);
+ Context cx = Context.getContextForThread(Thread.currentThread());
+ int size = cx.stack.size();
+ cx.stack.push(new Context.CallMarker());
+ cx.stack.push(args);
+
+ // FIXME, ugly
+ eval(args == null ? parentScope : new FunctionScope(sourceName, parentScope));
+
+ Object ret = cx.stack.pop();
+ if (cx.stack.size() > size)
+ Log.log(this, "warning, stack grew by " + (cx.stack.size() - size) +
+ " elements during call to " + Context.getCurrentSourceNameAndLine());
+ return ret;
+ } finally {
+ if (saved == null) Context.currentFunction.remove(Thread.currentThread());
+ else Context.currentFunction.put(Thread.currentThread(), saved);
+ }
+ }
+
+
+ // Adding and Altering Bytecodes ///////////////////////////////////////////////////
+
+ void set(int pos, int op_, Object arg_) { op[pos] = op_; arg[pos] = arg_; }
+ void set(int pos, Object arg_) { arg[pos] = arg_; }
+ void paste(CompiledFunction other) { for(int i=0; i<other.size; i++) add(other.line[i], other.op[i], other.arg[i]); }
+ CompiledFunction add(int line, int op_) { return add(line, op_, null); }
+ CompiledFunction 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;
+ int[] op2 = new int[op.length * 2]; System.arraycopy(op, 0, op2, 0, op.length); op = op2;
+ }
+ this.line[size] = line;
+ op[size] = op_;
+ arg[size] = arg_;
+ size++;
+ return this;
+ }
+
+
+ // Invoking the Bytecode ///////////////////////////////////////////////////////
+
+ int pc = 0;
+ void eval(JS.Scope s) {
+ final Vec t = Context.getContextForThread(Thread.currentThread()).stack;
+ OUTER: for(pc=0; pc<size; pc++) {
+ String label = null;
+ switch(op[pc]) {
+ case LITERAL: t.push(arg[pc]); break;
+ case OBJECT: t.push(new JS.Obj()); break;
+ case ARRAY: t.push(new Array(JS.toNumber(arg[pc]).intValue())); break;
+ case DECLARE: s.declare((String)t.pop()); break;
+ case TOPSCOPE: t.push(s); break;
+ case JT: if (JS.toBoolean(t.pop())) pc += JS.toNumber(arg[pc]).intValue() - 1; break;
+ case JF: if (!JS.toBoolean(t.pop())) pc += JS.toNumber(arg[pc]).intValue() - 1; break;
+ case JMP: pc += JS.toNumber(arg[pc]).intValue() - 1; break;
+ case POP: t.pop(); break;
+ case SWAP: { Object o1 = t.pop(); Object o2 = t.pop(); t.push(o1); t.push(o2); break; }
+ case DUP: t.push(t.peek()); break;
+ case NEWSCOPE: s = new JS.Scope(s); break;
+ case OLDSCOPE: s = s.getParentScope(); break;
+ case ASSERT: if (!JS.toBoolean(t.pop())) throw je("assertion failed"); break;
+ case BITNOT: t.push(new Long(~JS.toLong(t.pop()))); break;
+ case BANG: t.push(new Boolean(!JS.toBoolean(t.pop()))); break;
+
+ case TYPEOF: {
+ Object o = t.pop();
+ if (o == null) t.push(null);
+ else if (o instanceof org.xwt.Box) t.push("Box");
+ else if (o instanceof JS) t.push("Object");
+ else if (o instanceof String) t.push("String");
+ else if (o instanceof Number) t.push("Number");
+ else if (o.getClass().getName().startsWith("org.xwt.js.")) t.push(o.getClass().getName().substring(11));
+ else t.push("unknown");
+ break;
+ }
+
+ case NEWFUNCTION: {
+ CompiledFunction bytes = (CompiledFunction)arg[pc];
+ try {
+ t.push(bytes.cloneWithNewParentScope(s));
+ } catch (IOException e) {
+ throw new Error("this should never happen");
+ }
+ break;
+ }
+
+ case PUSHKEYS: {
+ Object o = t.peek();
+ Object[] keys = ((JS)o).keys();
+ Array a = new Array();
+ a.setSize(keys.length);
+ for(int j=0; j<keys.length; j++) a.setElementAt(keys[j], j);
+ t.push(a);
+ break;
+ }
+
+ case LABEL: break;
+ case LOOP: {
+ t.push(s);
+ t.push(new Context.LoopMarker(pc, pc > 0 && op[pc - 1] == LABEL ? (String)arg[pc - 1] : (String)null));
+ t.push(Boolean.TRUE);
+ break;
+ }
+
+ case BREAK:
+ case CONTINUE:
+ while(t.size() > 0) {
+ Object o = t.pop();
+ if (o instanceof Context.CallMarker) ee("break or continue not within a loop");
+ if (o != null && o instanceof Context.LoopMarker) {
+ if (arg[pc] == null || arg[pc].equals(((Context.LoopMarker)o).label)) {
+ int loopInstructionLocation = ((Context.LoopMarker)o).location;
+ int endOfLoop = ((Integer)arg[loopInstructionLocation]).intValue() + loopInstructionLocation;
+ s = (JS.Scope)t.pop();
+ if (op[pc] == CONTINUE) { t.push(s); t.push(o); t.push(Boolean.FALSE); }
+ pc = op[pc] == BREAK ? endOfLoop - 1 : loopInstructionLocation;
+ continue OUTER;
+ }
+ }
+ }
+ throw new Error("CONTINUE/BREAK invoked but couldn't find a LoopMarker at " + sourceName + ":" + line);
+
+ case TRY: {
+ t.push(new Context.TryMarker(pc + ((Integer)arg[pc]).intValue()));
+ break;
+ }
+
+ case RETURN: {
+ Object retval = t.pop();
+ while(t.size() > 0) {
+ Object o = t.pop();
+ if (o != null && o instanceof Context.CallMarker) {
+ t.push(retval);
+ return;
+ }
+ }
+ throw new Error("error: RETURN invoked but couldn't find a CallMarker!");
+ }
+
+ case PUT: {
+ Object val = t.pop();
+ Object key = t.pop();
+ Object target = t.peek();
+ if (target == null)
+ throw je("tried to put a value to the " + key + " property on the null value");
+ if (!(target instanceof JS))
+ throw je("tried to put a value to the " + key + " property on a " + target.getClass().getName());
+ ((JS)target).put(key, val);
+ t.push(val);
+ break;
+ }
+
+ case GET:
+ case GET_PRESERVE: {
+ Object o, v;
+ if (op[pc] == GET) {
+ v = t.pop();
+ o = t.pop();
+ } else {
+ v = t.pop();
+ o = t.peek();
+ t.push(v);
+ }
+ Object ret = null;
+ if (o == null) throw je("tried to get property \"" + v + "\" from the null value");
+ if (o instanceof String) {
+ ret = getFromString((String)o, v);
+ } else if (o instanceof Boolean) {
+ throw je("Not Implemented: properties on Boolean objects");
+ } else if (o instanceof Number) {
+ Log.log(this, "Not Implemented: properties on Number objects");
+ } else if (o instanceof JS) {
+ ret = ((JS)o).get(v);
+ }
+ t.push(ret);
+ break;
+ }
+
+ case CALL: {
+ Array arguments = new Array();
+ int numArgs = JS.toNumber(arg[pc]).intValue();
+ arguments.setSize(numArgs);
+ for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(t.pop(), j);
+ // FIXME: we really need to distinguish between compiled functions and callables
+ JS.Callable f = (JS.Callable)t.pop();
+ if (f == null) throw je("attempted to call null");
+ try {
+ t.push(f.call(arguments));
+ break;
+ } catch (JS.Exn e) {
+ t.push(e);
+ }
+ }
+ // fall through if exception was thrown
+ case THROW: {
+ Object exn = t.pop();
+ while(t.size() > 0) {
+ Object o = t.pop();
+ if (o instanceof Context.TryMarker) {
+ t.push(exn);
+ pc = ((Context.TryMarker)o).location - 1;
+ continue OUTER;
+ }
+ }
+ throw new JS.Exn(exn);
+ }
+
+ case INC: case DEC: {
+ boolean isPrefix = JS.toBoolean(arg[pc]);
+ Object key = t.pop();
+ JS obj = (JS)t.pop();
+ Number num = JS.toNumber(obj.get(key));
+ Number val = new Double(op[pc] == INC ? num.doubleValue() + 1.0 : num.doubleValue() - 1.0);
+ obj.put(key, val);
+ t.push(isPrefix ? val : num);
+ break;
+ }
+
+ default: {
+ Object right = t.pop();
+ Object left = t.pop();
+ switch(op[pc]) {
+
+ case ADD: {
+ Object l = left;
+ Object r = right;
+ if (l instanceof String || r instanceof String) {
+ if (l == null) l = "null";
+ if (r == null) r = "null";
+ if (l instanceof Number && ((Number)l).doubleValue() == ((Number)l).longValue())
+ l = new Long(((Number)l).longValue());
+ if (r instanceof Number && ((Number)r).doubleValue() == ((Number)r).longValue())
+ r = new Long(((Number)r).longValue());
+ t.push(l.toString() + r.toString()); break;
+ }
+ t.push(new Double(JS.toDouble(l) + JS.toDouble(r))); break;
+ }
+
+ case BITOR: t.push(new Long(JS.toLong(left) | JS.toLong(right))); break;
+ case BITXOR: t.push(new Long(JS.toLong(left) ^ JS.toLong(right))); break;
+ case BITAND: t.push(new Long(JS.toLong(left) & JS.toLong(right))); break;
+
+ case SUB: t.push(new Double(JS.toDouble(left) - JS.toDouble(right))); break;
+ case MUL: t.push(new Double(JS.toDouble(left) * JS.toDouble(right))); break;
+ case DIV: t.push(new Double(JS.toDouble(left) / JS.toDouble(right))); break;
+ case MOD: t.push(new Double(JS.toDouble(left) % JS.toDouble(right))); break;
+
+ case LSH: t.push(new Long(JS.toLong(left) << JS.toLong(right))); break;
+ case RSH: t.push(new Long(JS.toLong(left) >> JS.toLong(right))); break;
+ case URSH: t.push(new Long(JS.toLong(left) >>> JS.toLong(right))); break;
+
+ case LT: case LE: case GT: case GE: {
+ if (left == null) left = new Integer(0);
+ if (right == null) right = new Integer(0);
+ int result = 0;
+ if (left instanceof String || right instanceof String) {
+ result = left.toString().compareTo(right.toString());
+ } else {
+ result = (int)java.lang.Math.ceil(JS.toDouble(left) - JS.toDouble(right));
+ }
+ t.push(new Boolean((op[pc] == LT && result < 0) || (op[pc] == LE && result <= 0) ||
+ (op[pc] == GT && result > 0) || (op[pc] == GE && result >= 0)));
+ break;
+ }
+
+ case EQ:
+ case NE: {
+ Object l = left;
+ Object r = right;
+ boolean ret;
+ if (l == null) { Object tmp = r; r = l; l = tmp; }
+ if (l == null && r == null) ret = true;
+ else if (l instanceof Boolean) ret = new Boolean(JS.toBoolean(r)).equals(l);
+ else if (l instanceof Number) ret = JS.toNumber(r).doubleValue() == JS.toNumber(l).doubleValue();
+ else if (l instanceof String) ret = r != null && l.equals(r.toString());
+ else ret = l.equals(r);
+ t.push(new Boolean(op[pc] == EQ ? ret : !ret)); break;
+ }
+
+ default: throw new Error("unknown opcode " + op[pc]);
+ } }
+ }
+ }
+ }
+
+
+ // Helpers for Number, String, and Boolean ////////////////////////////////////////
+
+ private Object getFromString(final String o, final Object v) {
+ if (v.equals("length")) return new Integer(((String)o).length());
+ else if (v.equals("substring")) return new JS.Callable() {
+ public Object call(Array args) {
+ if (args.length() == 1) return ((String)o).substring(JS.toNumber(args.elementAt(0)).intValue());
+ else if (args.length() == 2) return ((String)o).substring(JS.toNumber(args.elementAt(0)).intValue(),
+ JS.toNumber(args.elementAt(1)).intValue());
+ else throw je("String.substring() can only take one or two arguments");
+ }
+ };
+ else if (v.equals("toLowerCase")) return new JS.Callable() {
+ public Object call(Array args) {
+ return ((String)o).toLowerCase();
+ } };
+ else if (v.equals("toUpperCase")) return new JS.Callable() {
+ public Object call(Array args) {
+ return ((String)o).toString().toUpperCase();
+ } };
+ else if (v.equals("charAt")) return new JS.Callable() {
+ public Object call(Array args) {
+ return ((String)o).charAt(JS.toNumber(args.elementAt(0)).intValue()) + "";
+ } };
+ else if (v.equals("lastIndexOf")) return new JS.Callable() {
+ public Object call(Array args) {
+ if (args.length() != 1) return null;
+ return new Integer(((String)o).lastIndexOf(args.elementAt(0).toString()));
+ } };
+ else if (v.equals("indexOf")) return new JS.Callable() {
+ public Object call(Array args) {
+ if (args.length() != 1) return null;
+ return new Integer(((String)o).indexOf(args.elementAt(0).toString()));
+ } };
+ throw je("Not Implemented: propery " + v + " on String objects");
+ }
+
+
+ // Exception Stuff ////////////////////////////////////////////////////////////////
+
+ static class EvaluatorException extends RuntimeException { public EvaluatorException(String s) { super(s); } }
+ EvaluatorException ee(String s) { throw new EvaluatorException(sourceName + ":" + line[pc] + " " + s); }
+ JS.Exn je(String s) { throw new JS.Exn(sourceName + ":" + line[pc] + " " + s); }
+
+
+ // FunctionScope /////////////////////////////////////////////////////////////////
+
+ private class FunctionScope extends JS.Scope {
+ String sourceName;
+ public FunctionScope(String sourceName, Scope parentScope) { super(parentScope); this.sourceName = sourceName; }
+ public String getSourceName() { return sourceName; }
+ public Object get(Object key) throws JS.Exn {
+ if (key.equals("trapee")) return org.xwt.Trap.currentTrapee();
+ else if (key.equals("cascade")) return org.xwt.Trap.cascadeFunction;
+ return super.get(key);
+ }
+ }
+}
import org.xwt.util.*;
import java.io.*;
+import java.util.*;
/** encapsulates a single JavaScript thread and its state */
public class Context {
- private static Hash javaThreadToContextMap = new Hash();
+ private static Hashtable javaThreadToContextMap = new Hashtable();
+ static Hashtable currentFunction = new Hashtable();
public Vec stack = new Vec();
- /** at any point in time, one JS Context can be bound to each Java thread; this determines which context any call()s execute in */
+ /**
+ * at any point in time, one JS Context can be bound to each Java thread;
+ * this determines which context any call()s execute in
+ */
public void bindToCurrentThread() { javaThreadToContextMap.put(Thread.currentThread(), this); }
- public static Context getContextForCurrentThread() {
- Context ret = (Context)javaThreadToContextMap.get(Thread.currentThread());
+
+ /** returns the current file and line number; intended for displaying to the user */
+ public static String getCurrentSourceNameAndLine() {
+ return getContextForThread(Thread.currentThread()).getCurrentFunction().getSourceName() + ":??";
+ }
+
+ public static String getSourceNameAndLineForThread(Thread t) {
+ CompiledFunction cf = getContextForThread(t).getCurrentFunction();
+ if (cf == null) return "null";
+ return cf.getSourceName() + ":??";
+ }
+
+ /** fetches the currently-executing javascript function */
+ public CompiledFunction getCurrentFunction() { return (CompiledFunction)currentFunction.get(Thread.currentThread()); }
+
+ public static Context getContextForThread(Thread t) {
+ Context ret = (Context)javaThreadToContextMap.get(t);
if (ret == null) {
ret = new Context();
ret.bindToCurrentThread();
return ret;
}
+ public static class TryMarker { public int location; public TryMarker(int location) { this.location = location; } }
public static class CallMarker { public CallMarker() { } }
- public static class LoopMarker { public int location; public LoopMarker(int location) { this.location = location; } }
+ public static class LoopMarker {
+ public int location;
+ public String label;
+ public LoopMarker(int location, String label) {
+ this.location = location;
+ this.label = label;
+ }
+ }
}
--- /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.*;
+
+/** a JavaScript Date object */
+public class Date extends JS.Obj {
+
+}
*/
public abstract class JS {
-
- // Static Methods //////////////////////////////////////////////////////////////////////
-
- private static Hashtable currentFunction = new Hashtable();
- public static Function getCurrentFunction() { return (Function)currentFunction.get(Thread.currentThread()); }
- public static String getCurrentFunctionSourceName() { return getCurrentFunctionSourceName(Thread.currentThread()); }
- public static String getFileAndLine() { return getCurrentFunctionSourceName() + ":" + getCurrentFunction().getLine(); }
- public static String getCurrentFunctionSourceName(Thread t) {
- Function f = (Function)currentFunction.get(t);
- if (f == null) return "null";
- return f.getSourceName();
- }
+ // Public Helper Methods //////////////////////////////////////////////////////////////////////
public static boolean toBoolean(Object o) {
if (o == null) return false;
public Object getObject() { return js; }
}
- /** A JavaScript Array */
- public static class Array extends Obj {
- private Vec vec = new Vec();
- public Array() { }
- public Array(int size) { vec.setSize(size); }
- private static int intVal(Object o) {
- if (o instanceof Number) {
- int intVal = ((Number)o).intValue();
- if (intVal == ((Number)o).doubleValue()) return intVal;
- return Integer.MIN_VALUE;
- }
- if (!(o instanceof String)) return Integer.MIN_VALUE;
- String s = (String)o;
- for(int i=0; i<s.length(); i++) if (s.charAt(i) < '0' || s.charAt(i) > '9') return Integer.MIN_VALUE;
- return Integer.parseInt(s);
- }
- public Object get(Object key) throws JS.Exn {
- // FIXME: HACK!
- if (key.equals("cascade")) return org.xwt.Trap.cascadeFunction;
- if (key.equals("trapee")) return org.xwt.Trap.currentTrapee();
- if (key.equals("length")) return new Long(vec.size());
- int i = intVal(key);
- if (i == Integer.MIN_VALUE) return super.get(key);
- try {
- return vec.elementAt(i);
- } catch (ArrayIndexOutOfBoundsException e) {
- return null;
- }
- }
- 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);
- else {
- if (i >= vec.size()) vec.setSize(i+1);
- 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 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); }
- }
-
- /** Anything that is callable */
- public static class Function extends Obj {
- ByteCodeBlock bytecodes;
- int line;
- String sourceName;
- Scope parentScope;
- public Function(int line, String sourceName, ByteCodeBlock bytecodes, Scope parentScope) {
- this.sourceName = sourceName;
- this.line = line;
- this.bytecodes = bytecodes;
- this.parentScope = parentScope;
- }
- public Function cloneWithNewParentScope(Scope s) {
- if (this.getClass() != Function.class)
- throw new Error("org.xwt.js.JS.Function.cloneWithNewParentScope() is not valid for subclasses");
- return new Function(line, sourceName, bytecodes, s);
- }
- public String getSourceName() throws JS.Exn { return sourceName; }
- public int getLine() throws JS.Exn { return line; }
- public Object _call(JS.Array args) throws JS.Exn, ByteCodeBlock.ControlTransferException {
- if (bytecodes == null) throw new Error("tried to call() a JS.Function with bytecodes == null");
- Context cx = Context.getContextForCurrentThread();
- int size = cx.stack.size();
- cx.stack.push(new Context.CallMarker());
- cx.stack.push(args);
- bytecodes.eval(args == null ? parentScope : new FunctionScope(sourceName, parentScope));
- Object ret = cx.stack.pop();
- if (cx.stack.size() > size) {
- Log.log(this, "warning, stack grew by " + (cx.stack.size() - size) +
- " elements during call to " + getSourceName() + ":" + getLine());
- Log.log(this, "top element is " + cx.stack.peek());
- }
- return ret;
- }
- public final Object call(JS.Array args) throws JS.Exn {
- Function saved = (Function)currentFunction.get(Thread.currentThread());
- if (!getSourceName().equals("java")) currentFunction.put(Thread.currentThread(), this);
- try {
- return _call(args);
- } catch (ByteCodeBlock.ReturnException e) { // ignore
- return e.retval;
- } catch (ByteCodeBlock.ControlTransferException e) {
- throw new RuntimeException(getSourceName() + ":" + getLine() +
- " error, ControlTransferException tried to leave a function");
- } finally {
- if (saved == null) currentFunction.remove(Thread.currentThread());
- else currentFunction.put(Thread.currentThread(), saved);
- }
- }
- }
-
- public static Function parse(Reader r, String sourceName, int line) throws IOException {
- ByteCodeBlock b = new ByteCodeBlock(line, sourceName);
- Parser p = new Parser(r, sourceName, line);
- try {
- while(true) {
- int size = b.size();
- p.parseStatement(false, b);
- if (size == b.size()) break;
- }
- b.add(Tokens.RETURN);
- return new Function(line, sourceName, b, null);
- } catch (Exception e) {
- if (Log.on) Log.log(Parser.class, e);
- return null;
- }
- }
-
/** Any object which becomes part of the scope chain must support this interface */
public static class Scope extends Obj {
private Scope parentScope;
else super.put(s, NULL);
}
}
+
+ public static CompiledFunction parse(String sourceName, int firstLine, Reader sourceCode, JS.Scope parentScope) throws IOException {
+ return new CompiledFunction(sourceName, firstLine, sourceCode, parentScope);
+ }
- private class FunctionScope extends JS.Scope {
- String sourceName;
- public FunctionScope(String sourceName, Scope parentScope) { super(parentScope); this.sourceName = sourceName; }
- public String getSourceName() { return sourceName; }
- public Object get(Object key) throws JS.Exn {
- if (key.equals("trapee")) return org.xwt.Trap.currentTrapee();
- else if (key.equals("cascade")) return org.xwt.Trap.cascadeFunction;
- return super.get(key);
- }
+ /** anything that is callable with the () operator */
+ public static abstract class Callable extends JS.Obj {
+ public abstract Object call(Array args) throws JS.Exn;
}
+
}
--- /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 Math() { setSeal(true); }
+
+ public Object get(Object name) {
+ if ("ceil".equals(name)) return new JS.Callable() { public Object call(Array args)
+ { if (args.elementAt(0) == null) return null;
+ return new Long((long)java.lang.Math.ceil(Double.parseDouble(args.elementAt(0).toString()))); } };
+ else if ("floor".equals(name)) return new JS.Callable() { public Object call(Array args)
+ { if (args.elementAt(0) == null) return null;
+ return new Long((long)java.lang.Math.floor(Double.parseDouble(args.elementAt(0).toString()))); } };
+ else if ("round".equals(name)) return new JS.Callable() { public Object call(Array args)
+ { if (args.elementAt(0) == null) return null;
+ return new Long((long)java.lang.Math.round(Double.parseDouble(args.elementAt(0).toString()))); } };
+ else if ("abs".equals(name)) return new JS.Callable() { public Object call(Array args)
+ { if (args.elementAt(0) == null) return null;
+ return new Long((long)java.lang.Math.abs(Double.parseDouble(args.elementAt(0).toString()))); } };
+ else if ("min".equals(name)) return new JS.Callable() { public Object call(Array args) {
+ if (args.length() < 2 || args.elementAt(0) == null || args.elementAt(1) == null) return args.elementAt(0);
+ return new Double(java.lang.Math.min(((Number)args.elementAt(0)).doubleValue(),
+ ((Number)args.elementAt(1)).doubleValue())); } };
+ else if ("max".equals(name)) return new JS.Callable() { public Object call(Array args) {
+ if (args.length() < 2) return args.elementAt(0);
+ return new Double(java.lang.Math.max(((Number)args.elementAt(0)).doubleValue(),
+ ((Number)args.elementAt(1)).doubleValue())); } };
+ else if ("cos".equals(name)) return new JS.Callable() { public Object call(Array args) {
+ return new Double(java.lang.Math.cos(((Number)args.elementAt(0)).doubleValue())); } };
+ else if ("sin".equals(name)) return new JS.Callable() { public Object call(Array args) {
+ return new Double(java.lang.Math.sin(((Number)args.elementAt(0)).doubleValue())); } };
+ else if ("tan".equals(name)) return new JS.Callable() { public Object call(Array args) {
+ return new Double(java.lang.Math.tan(((Number)args.elementAt(0)).doubleValue())); } };
+ else if ("acos".equals(name)) return new JS.Callable() { public Object call(Array args) {
+ return new Double(java.lang.Math.acos(((Number)args.elementAt(0)).doubleValue())); } };
+ else if ("asin".equals(name)) return new JS.Callable() { public Object call(Array args) {
+ return new Double(java.lang.Math.asin(((Number)args.elementAt(0)).doubleValue())); } };
+ else if ("atan".equals(name)) return new JS.Callable() { public Object call(Array args) {
+ return new Double(java.lang.Math.atan(((Number)args.elementAt(0)).doubleValue())); } };
+ else if ("sqrt".equals(name)) return new JS.Callable() { public Object call(Array args) {
+ return new Double(java.lang.Math.sqrt(((Number)args.elementAt(0)).doubleValue())); } };
+ return null;
+ }
+}
import org.xwt.util.*;
import java.io.*;
-/** Parses a stream of lexed tokens into a tree of ByteCodeBlock's */
+/** Parses a stream of lexed tokens into a tree of CompiledFunction's */
class Parser extends Lexer implements ByteCodes {
/** for debugging */
public static void main(String[] s) throws Exception {
- Parser p = new Parser(new InputStreamReader(System.in), "stdin", 0);
- while(true) {
- ByteCodeBlock block = new ByteCodeBlock(0, "stdin");
- p.parseStatement(false, block);
- if (block == null) return;
- System.out.println(block);
- if (p.peekToken() == -1) return;
- }
+ CompiledFunction block = new CompiledFunction("stdin", 0, new InputStreamReader(System.in), null);
+ if (block == null) return;
+ System.out.println(block);
}
// Parsing Logic /////////////////////////////////////////////////////////
- private ByteCodeBlock newbb(int line) { return new ByteCodeBlock(line, sourceName); }
+ /** the label for the current statement */
+ private String label = null;
/** gets a token and throws an exception if it is not <tt>code</tt> */
public void consume(int code) throws IOException {
}
/** append the largest expression beginning with prefix containing no operators of precedence below <tt>minPrecedence</tt> */
- public void startExpr(ByteCodeBlock block) throws IOException { startExpr(-1, block); }
+ public void startExpr(CompiledFunction block) throws IOException { startExpr(-1, block); }
/*
- public ByteCodeBlock startExpr(int minPrecedence) throws IOException {
- ByteCodeBlock ret = new ByteCodeBlock(line, sourceName);
+ public CompiledFunction startExpr(int minPrecedence) throws IOException {
+ CompiledFunction ret = new CompiledFunction(line, sourceName);
startExpr(minPrecedence, ret);
return ret.size() == 0 ? null : ret;
}
*/
- public void startExpr(int minPrecedence, ByteCodeBlock appendTo) throws IOException {
+ public void startExpr(int minPrecedence, CompiledFunction appendTo) throws IOException {
int tok = getToken();
- int curLine = line;
- if (tok == -1) return;
-
- ByteCodeBlock b = appendTo;
-
+ int line = this.line;
+ CompiledFunction b = appendTo;
switch (tok) {
- case NUMBER: continueExpr(b.add(ByteCodeBlock.LITERAL, number), minPrecedence); return;
- case STRING: continueExpr(b.add(ByteCodeBlock.LITERAL, string), minPrecedence); return;
- case THIS: continueExpr(b.add(TOPSCOPE, null), minPrecedence); return;
- case NULL: continueExpr(b.add(ByteCodeBlock.LITERAL, null), minPrecedence); return;
- case TRUE: case FALSE: continueExpr(b.add(ByteCodeBlock.LITERAL, new Boolean(tok == TRUE)), minPrecedence); return;
+ case -1: return;
+ case NUMBER: continueExpr(b.add(line, CompiledFunction.LITERAL, number), minPrecedence); return;
+ case STRING: continueExpr(b.add(line, CompiledFunction.LITERAL, string), minPrecedence); return;
+ case THIS: continueExpr(b.add(line, TOPSCOPE, null), minPrecedence); return;
+ case NULL: continueExpr(b.add(line, CompiledFunction.LITERAL, null), minPrecedence); return;
+ case TRUE: case FALSE: continueExpr(b.add(line, CompiledFunction.LITERAL, new Boolean(tok == TRUE)), minPrecedence); return;
case LB: {
- b.add(ARRAY, new Integer(0));
+ b.add(line, ARRAY, new Integer(0));
int i = 0;
while(true) {
int size = b.size();
startExpr(b);
if (size == b.size())
if (peekToken() == RB) { consume(RB); continueExpr(b, minPrecedence); return; }
- b.add(LITERAL, new Integer(i++));
- if (size == b.size()) b.add(LITERAL, null);
- b.add(PUT);
- b.add(POP);
+ b.add(line, LITERAL, new Integer(i++));
+ if (size == b.size()) b.add(line, LITERAL, null);
+ b.add(line, PUT);
+ b.add(line, POP);
if (peekToken() == RB) { consume(RB); continueExpr(b, minPrecedence); return; }
consume(COMMA);
}
}
case SUB: {
consume(NUMBER);
- b.add(ByteCodeBlock.LITERAL, new Double(number.doubleValue() * -1));
+ b.add(line, CompiledFunction.LITERAL, new Double(number.doubleValue() * -1));
continueExpr(b, minPrecedence);
return;
}
}
case BANG: case BITNOT: case TYPEOF: {
startExpr(precedence[tok], b);
- b.add(tok);
+ b.add(line, tok);
continueExpr(b, minPrecedence);
return;
}
case LC: {
- b.add(OBJECT, null);
+ b.add(line, OBJECT, null);
if (peekToken() != RC) while(true) {
if (peekToken() != NAME && peekToken() != STRING) throw new Error("expected NAME or STRING");
getToken();
- b.add(LITERAL, string);
+ b.add(line, LITERAL, string);
consume(COLON);
startExpr(b);
- b.add(PUT);
- b.add(POP);
+ b.add(line, PUT);
+ b.add(line, POP);
if (peekToken() == RC) break;
consume(COMMA);
if (peekToken() == RC) break;
String name = string;
if (peekToken() == ASSIGN) {
consume(ASSIGN);
- b.add(TOPSCOPE);
- b.add(ByteCodeBlock.LITERAL, name);
+ b.add(line, TOPSCOPE);
+ b.add(line, CompiledFunction.LITERAL, name);
startExpr(minPrecedence, b);
- b.add(ByteCodeBlock.PUT);
- b.add(ByteCodeBlock.SWAP);
- b.add(ByteCodeBlock.POP);
+ b.add(line, CompiledFunction.PUT);
+ b.add(line, CompiledFunction.SWAP);
+ b.add(line, CompiledFunction.POP);
} else {
- b.add(TOPSCOPE);
- b.add(ByteCodeBlock.LITERAL, name);
- b.add(ByteCodeBlock.GET);
+ b.add(line, TOPSCOPE);
+ b.add(line, CompiledFunction.LITERAL, name);
+ b.add(line, CompiledFunction.GET);
}
continueExpr(b, minPrecedence);
return;
case FUNCTION: {
consume(LP);
int numArgs = 0;
- ByteCodeBlock b2 = newbb(curLine);
- b2.add(TOPSCOPE);
- b2.add(SWAP);
- b2.add(LITERAL, "arguments");
- b2.add(LITERAL, "arguments");
- b2.add(DECLARE);
- b2.add(SWAP);
- b2.add(PUT);
- b2.add(SWAP);
- b2.add(POP);
+ CompiledFunction b2 = new CompiledFunction(sourceName, line, null);
+ b2.add(line, TOPSCOPE);
+ b2.add(line, SWAP);
+ b2.add(line, LITERAL, "arguments");
+ b2.add(line, LITERAL, "arguments");
+ b2.add(line, DECLARE);
+ b2.add(line, SWAP);
+ b2.add(line, PUT);
+ b2.add(line, SWAP);
+ b2.add(line, POP);
if (peekToken() == RP) consume(RP);
else while(true) {
if (peekToken() == COMMA) {
consume(NAME);
// declare the name
- b2.add(LITERAL, string);
- b2.add(DECLARE);
+ b2.add(line, LITERAL, string);
+ b2.add(line, DECLARE);
// retrieve it from the arguments array
- b2.add(LITERAL, new Integer(numArgs));
- b2.add(GET_PRESERVE);
- b2.add(SWAP);
- b2.add(POP);
+ b2.add(line, LITERAL, new Integer(numArgs));
+ b2.add(line, GET_PRESERVE);
+ b2.add(line, SWAP);
+ b2.add(line, POP);
// put it to the current scope
- b2.add(TOPSCOPE);
- b2.add(SWAP);
- b2.add(LITERAL, string);
- b2.add(SWAP);
- b2.add(PUT);
+ b2.add(line, TOPSCOPE);
+ b2.add(line, SWAP);
+ b2.add(line, LITERAL, string);
+ b2.add(line, SWAP);
+ b2.add(line, PUT);
// clean the stack
- b2.add(POP);
- b2.add(POP);
+ b2.add(line, POP);
+ b2.add(line, POP);
if (peekToken() == RP) { consume(RP); break; }
consume(COMMA);
numArgs++;
}
// pop off the arguments array
- b2.add(POP);
+ b2.add(line, POP);
parseStatement(true, b2);
- b2.add(LITERAL, null);
- b2.add(RETURN);
- continueExpr(b.add(NEWFUNCTION, b2), minPrecedence);
+ b2.add(line, LITERAL, null);
+ b2.add(line, RETURN);
+ continueExpr(b.add(line, NEWFUNCTION, b2), minPrecedence);
return;
}
default: pushBackToken(); return;
}
}
- public void continueExpr(ByteCodeBlock prefix, int minPrecedence) throws IOException {
+ public void continueExpr(CompiledFunction prefix, int minPrecedence) throws IOException {
int tok = getToken();
- int curLine = line;
if (tok == -1) return;
if (minPrecedence > 0 && precedence[tok] != 0)
if (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))
{ pushBackToken(); return; }
-
if (prefix == null) throw new Error("got null prefix");
- ByteCodeBlock b = prefix;
+ CompiledFunction b = prefix;
switch (tok) {
case ASSIGN_ADD: case ASSIGN_SUB: case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: {
prefix.set(prefix.size() - 1, b.GET_PRESERVE, new Boolean(true));
startExpr(precedence[tok - 1], b);
- prefix.add(tok - 1);
- prefix.add(PUT);
- prefix.add(SWAP);
- prefix.add(POP);
+ prefix.add(line, tok - 1);
+ prefix.add(line, PUT);
+ prefix.add(line, SWAP);
+ prefix.add(line, POP);
continueExpr(b, minPrecedence);
return;
}
consume(COMMA);
}
consume(RP);
- b.add(CALL, new Integer(i));
+ b.add(line, CALL, new Integer(i));
continueExpr(b, minPrecedence);
return;
}
case RSH: case URSH: case ADD: case MUL: case DIV: case MOD:
case GT: case GE: case EQ: case NE: case LT: case LE: case SUB: {
startExpr(precedence[tok], b);
- b.add(tok);
+ b.add(line, tok);
continueExpr(b, minPrecedence);
return;
}
case OR: case AND: {
- b.add(tok == AND ? b.JF : b.JT, new Integer(0));
+ b.add(line, tok == AND ? b.JF : b.JT, new Integer(0));
int size = b.size();
startExpr(precedence[tok], b);
b.set(size - 1, new Integer(b.size() - size + 2));
- b.add(JMP, new Integer(2));
- b.add(LITERAL, tok == AND ? new Boolean(false) : new Boolean(true));
+ b.add(line, JMP, new Integer(2));
+ b.add(line, LITERAL, tok == AND ? new Boolean(false) : new Boolean(true));
continueExpr(b, minPrecedence);
return;
}
String target = string;
if (peekToken() == ASSIGN) {
consume(ASSIGN);
- b.add(LITERAL, target);
+ b.add(line, LITERAL, target);
startExpr(b);
- b.add(PUT);
- b.add(SWAP);
- b.add(POP);
+ b.add(line, PUT);
+ b.add(line, SWAP);
+ b.add(line, POP);
} else {
- b.add(LITERAL, target);
- b.add(GET);
+ b.add(line, LITERAL, target);
+ b.add(line, GET);
}
continueExpr(b, minPrecedence);
return;
if (peekToken() == ASSIGN) {
consume(ASSIGN);
startExpr(b);
- b.add(PUT);
- b.add(SWAP);
- b.add(POP);
+ b.add(line, PUT);
+ b.add(line, SWAP);
+ b.add(line, POP);
} else {
- b.add(GET);
+ b.add(line, GET);
}
continueExpr(b, minPrecedence);
return;
}
case HOOK: {
- b.add(JF, new Integer(0));
+ b.add(line, JF, new Integer(0));
int size = b.size();
startExpr(b);
b.set(size - 1, new Integer(b.size() - size + 2));
- b.add(JMP, new Integer(0));
+ b.add(line, JMP, new Integer(0));
consume(COLON);
size = b.size();
startExpr(b);
}
}
- /** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */
-
- public ByteCodeBlock parseStatement() throws IOException {
- ByteCodeBlock ret = new ByteCodeBlock(line, sourceName);
- ret.add(ret.LITERAL, null);
+ /**
+ * a block is either a single statement or a list of statements surrounded by curly braces;
+ * all expressions are also statements
+ */
+ public CompiledFunction parseStatement() throws IOException {
+ CompiledFunction ret = new CompiledFunction(sourceName, line, null);
+ ret.add(line, ret.LITERAL, null);
parseStatement(false, ret);
if (ret.size() == 1) return null;
return ret;
}
- public void parseStatement(boolean requireBraces, ByteCodeBlock b) throws IOException {
+ public void parseStatement(boolean requireBraces, CompiledFunction b) throws IOException {
int tok = peekToken();
if (tok == -1) return;
+ int line = this.line;
boolean braced = tok == LC;
if (requireBraces && !braced) throw new ParserException("expected {, got " + codeToString[tok]);
if (braced) consume(LC);
- int curLine = line;
while(true) {
switch(tok = peekToken()) {
case THROW: case RETURN: case ASSERT: {
getToken();
- if (tok == RETURN && peekToken() == SEMI) b.add(LITERAL, null);
+ if (tok == RETURN && peekToken() == SEMI) b.add(line, LITERAL, null);
else startExpr(b);
consume(SEMI);
- b.add(tok);
+ b.add(line, tok);
break;
}
case BREAK: case CONTINUE: {
getToken();
if (peekToken() == NAME) consume(NAME);
- b.add(tok, string);
+ b.add(line, tok, string);
consume(SEMI);
break;
}
- case SEMI:
+ case SEMI: {
consume(SEMI);
if (!braced) return;
break;
+ }
case VAR: {
consume(VAR);
- b.add(TOPSCOPE); // push the current scope
+ b.add(line, TOPSCOPE); // push the current scope
while(true) {
consume(NAME);
String name = string;
- b.add(LITERAL, name); // push the name to be declared
- b.add(DECLARE); // declare it
+ b.add(line, LITERAL, name); // push the name to be declared
+ b.add(line, DECLARE); // declare it
if (peekToken() == ASSIGN) { // if there is an '=' after the variable name
- b.add(LITERAL, name); // put the var name back on the stack
+ b.add(line, LITERAL, name); // put the var name back on the stack
consume(ASSIGN);
startExpr(b);
- b.add(PUT);
- b.add(POP);
+ b.add(line, PUT);
+ b.add(line, POP);
}
if (peekToken() != COMMA) break;
consume(COMMA);
}
- b.add(POP);
+ b.add(line, POP);
if (peekToken() == SEMI) consume(SEMI);
break;
}
startExpr(b);
consume(RP);
- b.add(JF, new Integer(0));
+ b.add(line, JF, new Integer(0));
int size = b.size();
parseStatement(false, b);
if (peekToken() == ELSE) {
consume(ELSE);
b.set(size - 1, new Integer(2 + b.size() - size));
- b.add(JMP, new Integer(0));
+ b.add(line, JMP, new Integer(0));
size = b.size();
parseStatement(false, b);
}
case WHILE: {
consume(WHILE);
consume(LP);
- b.add(LOOP);
+ if (label != null) b.add(line, LABEL, label);
+ b.add(line, LOOP);
int size = b.size();
- b.add(POP);
+ b.add(line, POP);
startExpr(b);
- b.add(JT, new Integer(2));
- b.add(BREAK);
+ b.add(line, JT, new Integer(2));
+ b.add(line, BREAK);
consume(RP);
parseStatement(false, b);
- b.add(CONTINUE); // if we fall out of the end, definately continue
+ b.add(line, CONTINUE); // if we fall out of the end, definately continue
b.set(size - 1, new Integer(b.size() - size + 1)); // end of the loop
break;
}
case SWITCH: {
consume(SWITCH);
consume(LP);
- b.add(LOOP);
+ if (label != null) b.add(line, LABEL, label);
+ b.add(line, LOOP);
int size0 = b.size();
startExpr(b);
consume(RP);
while(true)
if (peekToken() == CASE) {
consume(CASE);
- b.add(DUP);
+ b.add(line, DUP);
startExpr(b);
consume(COLON);
- b.add(EQ);
- b.add(JF, new Integer(0));
+ b.add(line, EQ);
+ b.add(line, JF, new Integer(0));
int size = b.size();
while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
int size2 = b.size();
}
} else if (peekToken() == RC) {
consume(RC);
- b.add(BREAK);
+ b.add(line, BREAK);
break;
} else {
throw new ParserException("expected CASE, DEFAULT, or RC; got " + codeToString[peekToken()]);
}
- b.add(BREAK);
+ b.add(line, BREAK);
b.set(size0 - 1, new Integer(b.size() - size0 + 1)); // end of the loop
break;
}
case DO: {
consume(DO);
- b.add(LOOP);
+ if (label != null) b.add(line, LABEL, label);
+ b.add(line, LOOP);
int size = b.size();
parseStatement(false, b);
consume(WHILE);
consume(LP);
startExpr(b);
- b.add(JT, new Integer(2));
- b.add(BREAK);
- b.add(CONTINUE);
+ b.add(line, JT, new Integer(2));
+ b.add(line, BREAK);
+ b.add(line, CONTINUE);
consume(RP);
consume(SEMI);
b.set(size - 1, new Integer(b.size() - size + 1)); // end of the loop
case TRY: {
// We deliberately allow you to omit braces in catch{}/finally{} if they are single statements...
consume(TRY);
+ b.add(line, TRY);
+ int size = b.size();
parseStatement(true, b);
+ b.add(line, POP); // pop the TryMarker
+ b.add(line, JMP); // jump forward to the end of the catch block
+ int size2 = b.size();
+ b.set(size - 1, new Integer(b.size() - size + 1));// the TRY argument points at the start of the CATCH block
if (peekToken() == CATCH) {
getToken();
consume(LP);
consume(NAME);
consume(RP);
- parseStatement(); // just discard the catchblock
+ // FIXME, we need an extra scope here
+ b.add(line, TOPSCOPE); // the exception is on top of the stack; put it to the variable
+ b.add(line, SWAP);
+ b.add(line, LITERAL);
+ b.add(line, SWAP);
+ b.add(line, PUT);
+ b.add(line, POP);
+ b.add(line, POP);
+ parseStatement(false, b);
}
+ b.set(size2 - 1, new Integer(b.size() - size2 + 1)); // jump here if no exception was thrown
+
+ // FIXME: not implemented correctly
if (peekToken() == FINALLY) {
consume(FINALLY);
parseStatement(false, b);
break;
}
- case FOR: {
- consume(FOR);
- consume(LP);
-
- tok = getToken();
- boolean hadVar = false;
- if (tok == VAR) { hadVar = true; tok = getToken(); }
- String varName = string;
- boolean forIn = peekToken() == IN;
- pushBackToken(tok, varName);
-
- if (forIn) {
- // FIXME: break needs to work in here
- consume(NAME);
- consume(IN);
- startExpr(b);
- b.add(PUSHKEYS);
- b.add(LITERAL, "length");
- b.add(GET);
- consume(RP);
- ByteCodeBlock b2 = newbb(curLine);
-
- b.add(PUSHSCOPE);
-
- b.add(LITERAL, new Integer(1));
- b.add(SUB);
- b.add(DUP);
- b.add(LITERAL, new Integer(0));
- b.add(LT);
- b.add(JT, new Integer(7));
- b.add(GET_PRESERVE);
- b.add(LITERAL, varName);
- b.add(LITERAL, varName);
- b.add(DECLARE);
- b.add(PUT);
- parseStatement(false, b);
-
- b.add(POPSCOPE);
-
- break;
+ case FOR: {
+ consume(FOR);
+ consume(LP);
- } else {
- if (hadVar) pushBackToken(VAR, null);
- b.add(PUSHSCOPE);
-
- parseStatement(false, b);
- ByteCodeBlock e2 = new ByteCodeBlock(line, sourceName);
- startExpr(e2);
- consume(SEMI);
- if (e2 == null) e2 = newbb(curLine).add(b.LITERAL, Boolean.TRUE);
-
- b.add(LOOP);
- int size2 = b.size();
-
- b.add(JT, new Integer(0));
- int size = b.size();
- startExpr(b);
- if (b.size() > size) b.add(POP);
- b.set(size - 1, new Integer(b.size() - size + 1));
- consume(RP);
-
- b.paste(e2);
- b.add(JT, new Integer(2));
- b.add(BREAK);
- parseStatement(false, b);
- b.add(CONTINUE);
- b.set(size2 - 1, new Integer(b.size() - size2 + 1)); // end of the loop
-
- b.add(POPSCOPE);
- break;
+ tok = getToken();
+ boolean hadVar = false;
+ if (tok == VAR) { hadVar = true; tok = getToken(); }
+ String varName = string;
+ boolean forIn = peekToken() == IN;
+ pushBackToken(tok, varName);
+
+ if (forIn) {
+ // FIXME: break needs to work in here
+ consume(NAME);
+ consume(IN);
+ startExpr(b);
+ b.add(line, PUSHKEYS);
+ b.add(line, LITERAL, "length");
+ b.add(line, GET);
+ consume(RP);
+ CompiledFunction b2 = new CompiledFunction(sourceName, line, null);
+
+ b.add(line, NEWSCOPE);
+
+ b.add(line, LITERAL, new Integer(1));
+ b.add(line, SUB);
+ b.add(line, DUP);
+ b.add(line, LITERAL, new Integer(0));
+ b.add(line, LT);
+ b.add(line, JT, new Integer(7));
+ b.add(line, GET_PRESERVE);
+ b.add(line, LITERAL, varName);
+ b.add(line, LITERAL, varName);
+ b.add(line, DECLARE);
+ b.add(line, PUT);
+ parseStatement(false, b);
+
+ b.add(line, OLDSCOPE);
+
+ break;
+
+ } else {
+ if (hadVar) pushBackToken(VAR, null);
+ b.add(line, NEWSCOPE);
+
+ parseStatement(false, b);
+ CompiledFunction e2 = new CompiledFunction(sourceName, line, null);
+ startExpr(e2);
+ consume(SEMI);
+ if (e2 == null) e2 = new CompiledFunction(sourceName, line, null).add(line, b.LITERAL, Boolean.TRUE);
+
+ if (label != null) b.add(line, LABEL, label);
+ b.add(line, LOOP);
+ int size2 = b.size();
+
+ b.add(line, JT, new Integer(0));
+ int size = b.size();
+ startExpr(b);
+ if (b.size() > size) b.add(line, POP);
+ b.set(size - 1, new Integer(b.size() - size + 1));
+ consume(RP);
+
+ b.paste(e2);
+ b.add(line, JT, new Integer(2));
+ b.add(line, BREAK);
+ parseStatement(false, b);
+ b.add(line, CONTINUE);
+ b.set(size2 - 1, new Integer(b.size() - size2 + 1)); // end of the loop
+
+ b.add(line, OLDSCOPE);
+ break;
+ }
}
- }
-
+
case NAME: {
consume(NAME);
- String name = string;
+ String oldlabel = label;
+ label = string;
if (peekToken() == COLON) {
consume(COLON);
- b.add(ByteCodeBlock.LABEL, string);
+ parseStatement(false, b);
+ label = oldlabel;
break;
} else {
- pushBackToken(NAME, name);
+ pushBackToken(NAME, label);
+ label = oldlabel;
// fall through to default case
}
}
int size = b.size();
startExpr(b);
if (size == b.size()) return;
- b.add(POP);
+ b.add(line, POP);
if (peekToken() == SEMI) consume(SEMI);
break;
}
}
}
- class ParserException extends RuntimeException {
+ private class ParserException extends RuntimeException {
public ParserException(String s) { super(sourceName + ":" + line + " " + s); }
}