2003/07/05 03:23:21
[org.ibex.core.git] / src / org / xwt / js / CompiledFunctionImpl.java
index 9511333..ae344d0 100644 (file)
@@ -33,7 +33,6 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
     /** the scope in which this function was declared; by default this function is called in a fresh subscope of the parentScope */
     private JS.Scope parentScope;
 
-
     // Constructors ////////////////////////////////////////////////////////
 
     private CompiledFunctionImpl cloneWithNewParentScope(JS.Scope s) throws IOException {
@@ -53,7 +52,8 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
             p.parseStatement(this, null);
             if (s == size()) break;
         }
-        add(-1, Tokens.RETURN);
+        add(-1, LITERAL, null); 
+        add(-1, RETURN);
     }
     
     public Object call(JS.Array args) throws JS.Exn { return call(args, new FunctionScope(sourceName, parentScope)); }
@@ -67,7 +67,10 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
             cx.stack.push(args);
             eval(scope);
             Object ret = cx.stack.pop();
-            if (cx.stack.size() > size) Log.logJS(this, "warning, stack grew by " + (cx.stack.size() - size) + " elements during call");
+            // FIXME: if we catch an exception in Java, JS won't notice and the stack will be messed up
+            if (cx.stack.size() > size)
+                // this should never happen
+                throw new Error("ERROR: stack grew by " + (cx.stack.size() - size) + " elements during call");
             return ret;
         } finally {
             cx.currentCompiledFunction = saved;
@@ -94,17 +97,23 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
         size++;
         return this;
     }
+    
+    public int getLine(int pc) {
+        if(pc < 0 || pc >= size) return -1;
+        return line[pc];
+    }
 
 
     // Invoking the Bytecode ///////////////////////////////////////////////////////
         
-    int pc = 0;
     void eval(JS.Scope s) {
         final JS.Thread cx = JS.Thread.fromJavaThread(java.lang.Thread.currentThread());
         final Vec t = cx.stack;
+        int pc;
+        int lastPC = -1;
         OUTER: for(pc=0; pc<size; pc++) {
             String label = null;
-            cx.line = line[pc];
+            cx.pc = lastPC = pc;
             switch(op[pc]) {
             case LITERAL: t.push(arg[pc]); break;
             case OBJECT: t.push(new JS.Obj()); break;
@@ -126,11 +135,10 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
             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 if (o instanceof JS) t.push(((JS)o).typeName());
+                else if (o instanceof String) t.push("string");
+                else if (o instanceof Number) t.push("number");
+                else if (o instanceof Boolean) t.push("boolean");
                 else t.push("unknown");
                 break;
             }
@@ -205,6 +213,8 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
                     throw je("tried to put a value to the " + key + " property on the null value");
                 if (!(target instanceof JS))
                     throw je("tried to put a value to the " + key + " property on a " + target.getClass().getName());
+                if (key == null)
+                    throw je("tried to assign \"" + (val==null?"(null)":val.toString()) + "\" to the null key");
                 ((JS)target).put(key, val);
                 t.push(val);
                 break;
@@ -223,7 +233,7 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
                 }
                 Object ret = null;
                 if (o == null) throw je("tried to get property \"" + v + "\" from the null value");
-                if (v == null) throw je("tried to get the null key from " + v);
+                if (v == null) throw je("tried to get the null key from " + o);
                 if (o instanceof String) {
                     ret = getFromString((String)o, v);
                 } else if (o instanceof Boolean) {
@@ -331,6 +341,7 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
                     boolean ret;
                     if (l == null) { Object tmp = r; r = l; l = tmp; }
                     if (l == null && r == null) ret = true;
+                    else if (r == null) ret = false; // l != null, so its false
                     else if (l instanceof Boolean) ret = new Boolean(JS.toBoolean(r)).equals(l);
                     else if (l instanceof Number) ret = JS.toNumber(r).doubleValue() == JS.toNumber(l).doubleValue();
                     else if (l instanceof String) ret = r != null && l.equals(r.toString());
@@ -342,8 +353,32 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
                 } }
             }
         }
+        // this should never happen, we will ALWAYS have a RETURN at the end of the func
+        throw new Error("Just fell out of CompiledFunction::eval() loop. Last PC was " + lastPC);
     }
 
+    // Debugging //////////////////////////////////////////////////////////////////////
+
+    public String toString() {
+        StringBuffer sb = new StringBuffer(1024);
+        sb.append("\n" + sourceName + ": " + firstLine + "\n");
+        for (int i=0; i < size; i++) {
+            sb.append(i);
+            sb.append(": ");
+            if (op[i] < 0)
+                sb.append(bytecodeToString[-op[i]]);
+            else
+                sb.append(codeToString[op[i]]);
+            sb.append(" ");
+            sb.append(arg[i] == null ? "(no arg)" : arg[i]);
+            if((op[i] == JF || op[i] == JT || op[i] == JMP) && arg[i] != null && arg[i] instanceof Number) {
+                sb.append(" jump to ");
+                sb.append(i+((Number) arg[i]).intValue());
+            }
+            sb.append("\n");
+        }
+        return sb.toString();
+    } 
 
     // Helpers for Number, String, and Boolean ////////////////////////////////////////
 
@@ -379,6 +414,24 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
                     if (args.length() != 1) return null;
                     return new Integer(((String)o).indexOf(args.elementAt(0).toString()));
                 } };
+        else if(v.equals("match")) return new JS.Callable() {
+                public Object call(JS.Array args) {
+                    return Regexp.stringMatch(o,args);
+                } };
+        else if(v.equals("search")) return new JS.Callable() {
+                public Object call(JS.Array args) {
+                    return Regexp.stringSearch(o,args);
+                } };
+        else if(v.equals("replace")) return new JS.Callable() {
+                public Object call(JS.Array args) {
+                    return Regexp.stringReplace(o,args);
+                } };
+        else if(v.equals("split")) return new JS.Callable() {
+                public Object call(JS.Array args) {
+                    return Regexp.stringSplit(o,args);
+                } };
+                        
+                        
         throw new JS.Exn("Not Implemented: propery " + v + " on String objects");
     }
 
@@ -386,8 +439,8 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
     // 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); }
+    EvaluatorException ee(String s) { throw new EvaluatorException(sourceName + ":" + JS.Thread.currentJSThread().getLine() + " " + s); }
+    JS.Exn je(String s) { throw new JS.Exn(sourceName + ":" + JS.Thread.currentJSThread().getLine() + " " + s); }
 
 
     // FunctionScope /////////////////////////////////////////////////////////////////
@@ -396,11 +449,6 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
         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);
-        }
     }
 
 
@@ -426,6 +474,7 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
 
 }
 
+/** this class exists solely to work around a GCJ bug */
 abstract class JSCallable extends JS.Callable {
         public abstract Object call(JS.Array args) throws JS.Exn;
 }