2003/06/12 17:57:37
authormegacz <megacz@xwt.org>
Fri, 30 Jan 2004 07:01:02 +0000 (07:01 +0000)
committermegacz <megacz@xwt.org>
Fri, 30 Jan 2004 07:01:02 +0000 (07:01 +0000)
darcs-hash:20040130070102-2ba56-20b2a89a5dd3a6650c167e60bf55fdd2fb322ef2.gz

19 files changed:
src/org/xwt/MessageQueue.java
src/org/xwt/Proxy.java
src/org/xwt/Resources.java
src/org/xwt/SOAP.java
src/org/xwt/SpecialBoxProperty.java
src/org/xwt/Template.java
src/org/xwt/ThreadMessage.java
src/org/xwt/Trap.java
src/org/xwt/XMLRPC.java
src/org/xwt/XWT.java
src/org/xwt/js/Array.java [new file with mode: 0644]
src/org/xwt/js/ByteCodeBlock.java [deleted file]
src/org/xwt/js/ByteCodes.java
src/org/xwt/js/CompiledFunction.java [new file with mode: 0644]
src/org/xwt/js/Context.java
src/org/xwt/js/Date.java [new file with mode: 0644]
src/org/xwt/js/JS.java
src/org/xwt/js/Math.java [new file with mode: 0644]
src/org/xwt/js/Parser.java

index 6de9185..cfc388b 100644 (file)
@@ -117,18 +117,18 @@ public class MessageQueue extends Thread {
                 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();
index 81318a0..6067464 100644 (file)
@@ -35,7 +35,7 @@ public class Proxy {
     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();
@@ -87,14 +87,9 @@ public class 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 = "";
@@ -118,28 +113,27 @@ public class Proxy {
                 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;
         }
-       */
     }
 
 
@@ -175,13 +169,14 @@ public class Proxy {
 
 
     // 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;
@@ -195,54 +190,54 @@ public class Proxy {
             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] &&
@@ -254,18 +249,18 @@ public class Proxy {
                 }
             };
         
-        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) {
@@ -275,9 +270,9 @@ public class Proxy {
                 }
             };
         
-        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);
@@ -293,11 +288,11 @@ public class Proxy {
             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;
                 }
@@ -305,23 +300,23 @@ public class Proxy {
         
         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
@@ -331,18 +326,18 @@ public class Proxy {
                 }
             };
         
-        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");
                 }
             };
         
     }
-    */    
+
 }
index be4eb99..c4e512e 100644 (file)
@@ -70,7 +70,7 @@ public class Resources {
 
     /** 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();
@@ -86,10 +86,10 @@ public class Resources {
                     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);
index bceb99b..648562f 100644 (file)
@@ -69,7 +69,7 @@ class SOAP extends XMLRPC {
                     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());
                 }
             }
         }
@@ -147,9 +147,9 @@ class SOAP extends XMLRPC {
         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);
@@ -221,8 +221,8 @@ class SOAP extends XMLRPC {
             }
             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");
@@ -236,7 +236,7 @@ class SOAP extends XMLRPC {
         }
     }
 
-    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");
index 00f43db..b492e6c 100644 (file)
@@ -106,7 +106,7 @@ class SpecialBoxProperty {
                     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();
@@ -149,7 +149,7 @@ class SpecialBoxProperty {
                     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();
@@ -219,8 +219,7 @@ class SpecialBoxProperty {
 
         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);
@@ -686,22 +685,22 @@ class SpecialBoxProperty {
     }
         
     /** 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();
index 4022c42..63a9daa 100644 (file)
@@ -73,10 +73,10 @@ public class Template {
     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();
@@ -165,7 +165,7 @@ public class Template {
      *  @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;
 
@@ -206,7 +206,9 @@ public class Template {
         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());
         }
@@ -238,7 +240,7 @@ public class Template {
 
         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);
@@ -264,7 +266,7 @@ public class Template {
     }
 
     /** 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
@@ -284,7 +286,7 @@ public class Template {
 
         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);
@@ -363,7 +365,7 @@ public class Template {
         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
@@ -376,6 +378,8 @@ public class Template {
 
                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());
         }
@@ -650,10 +654,10 @@ public class Template {
             }
         }
 
-        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;
index f4f95a9..faf7577 100644 (file)
@@ -20,7 +20,7 @@ public class ThreadMessage extends Thread implements Message {
     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();
@@ -38,7 +38,7 @@ public class ThreadMessage extends Thread implements Message {
     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();
@@ -56,7 +56,7 @@ public class ThreadMessage extends Thread implements Message {
         // 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;
@@ -79,14 +79,14 @@ public class ThreadMessage extends Thread implements Message {
             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);
index 1ce4289..581bd2a 100644 (file)
@@ -44,7 +44,7 @@ public class Trap {
     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;
@@ -72,7 +72,7 @@ public class Trap {
      *  @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 + "\"");
@@ -80,7 +80,9 @@ public class Trap {
         }
 
         // 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);
@@ -108,7 +110,7 @@ public class Trap {
     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;
@@ -118,10 +120,10 @@ public class Trap {
 
     /** 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;
@@ -156,7 +158,7 @@ public class Trap {
     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
@@ -168,11 +170,11 @@ public class Trap {
 
         // 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;
             }
@@ -180,7 +182,7 @@ public class Trap {
            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;
 
index f2e8eee..ce1fab2 100644 (file)
@@ -30,7 +30,7 @@ import org.bouncycastle.util.encoders.Base64;
  *         convert.
  *  </ol>
  */
-class XMLRPC extends JS.Function {
+class XMLRPC extends JS.Callable {
 
     public Object[] keys() { throw new Error("not implemented"); }
 
@@ -151,7 +151,7 @@ class XMLRPC extends JS.Function {
            } 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);
@@ -263,11 +263,11 @@ class XMLRPC extends JS.Function {
             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");
 
@@ -292,7 +292,7 @@ class XMLRPC extends JS.Function {
 
     // 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();
@@ -333,7 +333,7 @@ class XMLRPC extends JS.Function {
         }
     }
 
-    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");
@@ -366,7 +366,7 @@ class XMLRPC extends JS.Function {
         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;
 
@@ -395,7 +395,6 @@ class XMLRPC extends JS.Function {
     }
 
     public XMLRPC(String url, String methodname, HTTP http) {
-       super(-1, "java", null, null);
         this.http = http;
         this.url = url;
         this.methodname = methodname;
index ed8feb1..f20cf26 100644 (file)
@@ -37,7 +37,7 @@ public final class XWT extends JS.Obj {
     }
 
     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
@@ -55,67 +55,30 @@ public final class XWT extends JS.Obj {
         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++) {
@@ -127,44 +90,44 @@ public final class XWT extends JS.Obj {
                        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);
@@ -173,7 +136,7 @@ public final class XWT extends JS.Obj {
            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();
@@ -183,7 +146,7 @@ public final class XWT extends JS.Obj {
            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();
@@ -192,19 +155,19 @@ public final class XWT extends JS.Obj {
            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()));
@@ -212,20 +175,20 @@ public final class XWT extends JS.Obj {
            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();
@@ -244,7 +207,7 @@ public final class XWT extends JS.Obj {
            }
        }});
 
-       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();
@@ -261,12 +224,12 @@ public final class XWT extends JS.Obj {
            }
        }});
 
-       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) {
@@ -282,21 +245,21 @@ public final class XWT extends JS.Obj {
             }
         });
     
-    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());
@@ -347,10 +310,10 @@ public final class XWT extends JS.Obj {
        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;
     }});
     }
@@ -359,16 +322,16 @@ public final class XWT extends JS.Obj {
         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++)
@@ -377,7 +340,7 @@ public final class XWT extends JS.Obj {
                        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);
 
         }
     }
diff --git a/src/org/xwt/js/Array.java b/src/org/xwt/js/Array.java
new file mode 100644 (file)
index 0000000..9fda147
--- /dev/null
@@ -0,0 +1,60 @@
+// 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); }
+}
+
diff --git a/src/org/xwt/js/ByteCodeBlock.java b/src/org/xwt/js/ByteCodeBlock.java
deleted file mode 100644 (file)
index 9cd3f0e..0000000
+++ /dev/null
@@ -1,325 +0,0 @@
-// 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; }
-    }
-    
-}
index 1089f64..1c73e9f 100644 (file)
@@ -58,16 +58,16 @@ interface ByteCodes {
     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 */
diff --git a/src/org/xwt/js/CompiledFunction.java b/src/org/xwt/js/CompiledFunction.java
new file mode 100644 (file)
index 0000000..76f2bf9
--- /dev/null
@@ -0,0 +1,415 @@
+// 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);
+       }
+    }
+}
index 4fc5eaa..ee00fc8 100644 (file)
@@ -3,18 +3,38 @@ package org.xwt.js;
 
 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();
@@ -22,7 +42,15 @@ public class Context {
        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;
+       }
+    }
 
 }
diff --git a/src/org/xwt/js/Date.java b/src/org/xwt/js/Date.java
new file mode 100644 (file)
index 0000000..a3d4b36
--- /dev/null
@@ -0,0 +1,11 @@
+// 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 {
+    
+}
index 11faeaa..85189b4 100644 (file)
@@ -12,18 +12,7 @@ import java.util.*;
  */
 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;
@@ -78,127 +67,6 @@ public abstract class JS {
        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;
@@ -225,17 +93,16 @@ public abstract class JS {
            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;
     }
+
 } 
 
 
diff --git a/src/org/xwt/js/Math.java b/src/org/xwt/js/Math.java
new file mode 100644 (file)
index 0000000..4f8319a
--- /dev/null
@@ -0,0 +1,50 @@
+// 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;
+    }
+}
index d4a2753..9aed73a 100644 (file)
@@ -4,7 +4,7 @@ package org.xwt.js;
 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 {
 
 
@@ -14,14 +14,9 @@ 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);
     }
 
 
@@ -56,7 +51,8 @@ class Parser extends Lexer implements ByteCodes {
 
     // 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 {
@@ -65,47 +61,45 @@ class Parser extends Lexer implements ByteCodes {
     }
 
     /** 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;
        }
@@ -124,20 +118,20 @@ class Parser extends Lexer implements ByteCodes {
        }
        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;
@@ -150,16 +144,16 @@ class Parser extends Lexer implements ByteCodes {
            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;
@@ -167,16 +161,16 @@ class Parser extends Lexer implements ByteCodes {
        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) {
@@ -186,25 +180,25 @@ class Parser extends Lexer implements ByteCodes {
                    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);
@@ -212,27 +206,25 @@ class Parser extends Lexer implements ByteCodes {
                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) {
 
@@ -240,10 +232,10 @@ class Parser extends Lexer implements ByteCodes {
        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;
        }
@@ -265,7 +257,7 @@ class Parser extends Lexer implements ByteCodes {
                consume(COMMA);
            }
            consume(RP);
-           b.add(CALL, new Integer(i));
+           b.add(line, CALL, new Integer(i));
            continueExpr(b, minPrecedence);
            return;
        }
@@ -274,18 +266,18 @@ class Parser extends Lexer implements ByteCodes {
        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;
        }
@@ -295,14 +287,14 @@ class Parser extends Lexer implements ByteCodes {
            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;
@@ -314,22 +306,22 @@ class Parser extends Lexer implements ByteCodes {
            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);
@@ -342,67 +334,70 @@ class Parser extends Lexer implements ByteCodes {
        }
     }
     
-    /** 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;
            }
@@ -413,14 +408,14 @@ class Parser extends Lexer implements ByteCodes {
                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);
                }
@@ -431,15 +426,16 @@ class Parser extends Lexer implements ByteCodes {
            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;
            }
@@ -447,7 +443,8 @@ class Parser extends Lexer implements ByteCodes {
            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);
@@ -455,11 +452,11 @@ class Parser extends Lexer implements ByteCodes {
                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();
@@ -477,27 +474,28 @@ class Parser extends Lexer implements ByteCodes {
                        }
                    } 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
@@ -507,16 +505,33 @@ class Parser extends Lexer implements ByteCodes {
            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);
@@ -524,88 +539,92 @@ class Parser extends Lexer implements ByteCodes {
                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
                }
            }
@@ -617,7 +636,7 @@ class Parser extends Lexer implements ByteCodes {
                int size = b.size();
                startExpr(b);
                if (size == b.size()) return;
-               b.add(POP);
+               b.add(line, POP);
                if (peekToken() == SEMI) consume(SEMI);
                break;
            }
@@ -627,7 +646,7 @@ class Parser extends Lexer implements ByteCodes {
        }
     }
     
-    class ParserException extends RuntimeException {
+    private class ParserException extends RuntimeException {
        public ParserException(String s) { super(sourceName + ":" + line + " " + s); }
     }