2003/07/05 03:23:31
authorbrian <brian@xwt.org>
Fri, 30 Jan 2004 07:02:49 +0000 (07:02 +0000)
committerbrian <brian@xwt.org>
Fri, 30 Jan 2004 07:02:49 +0000 (07:02 +0000)
darcs-hash:20040130070249-aa32f-6a9615781acf55e91008b0e48a2cde5c956da7f9.gz

src/org/xwt/Box.java
src/org/xwt/js/ArrayImpl.java
src/org/xwt/js/ByteCodes.java
src/org/xwt/js/CompiledFunctionImpl.java
src/org/xwt/js/JS.java
src/org/xwt/js/Parser.java

index 1f6cb0c..f04c3c2 100644 (file)
@@ -1418,6 +1418,9 @@ public final class Box extends JS.Scope {
         public Object get(Object name) { return box.get(name); }
         public void put(Object name, Object value) { box.put(name, value, false, this); }
         public Object[] keys() { return box.keys(); }
+        public Object callMethod(Object method, JS.Array args, boolean justChecking) {
+            return box.callMethod(method,args,justChecking);
+        }
     }
 
 
index 13f7d80..110b301 100644 (file)
@@ -6,6 +6,7 @@ import java.io.*;
 import java.util.*;
 
 // FIXME: could use some cleaning up...
+// FIXME: Finish implementing ECMA-262
 
 /** A JavaScript Array */
 class ArrayImpl extends JS.Obj {
index c74c564..bac280a 100644 (file)
@@ -71,12 +71,16 @@ interface ByteCodes {
 
     /** 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 */
-    public static final byte LOOP = -22;         
+    public static final byte LOOP = -22;        
+     
+    /** pop three elements off the stack; method arguments, method name, and an object to call the method on, then calls the method 
+        Has a similar effect a a GET followed by a CALL */
+    public static final byte CALLMETHOD = -23;
 
 
     public static final String[] bytecodeToString = new String[] {
         "", "", "LITERAL", "ARRAY", "OBJECT", "NEWFUNCTION", "DECLARE", "TOPSCOPE",
         "GET", "GET_PRESERVE", "PUT", "JT", "JF", "JMP", "POP", "CALL", "PUSHKEYS",
-        "SWAP", "NEWSCOPE", "OLDSCOPE", "DUP", "LABEL", "LOOP"
+        "SWAP", "NEWSCOPE", "OLDSCOPE", "DUP", "LABEL", "LOOP", "CALLMETHOD"
     };
 }
index ae344d0..0f08f2b 100644 (file)
@@ -234,28 +234,40 @@ 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 " + o);
-                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) {
+                if (o instanceof String || o instanceof Number || o instanceof Boolean)
+                    ret = Internal.getFromPrimitive(o,v);
+                else if (o instanceof JS)
                     ret = ((JS)o).get(v);
-                }
+                else 
+                    throw je("tried to get property " + v + " from a " + o.getClass().getName());
                 t.push(ret);
                 break;
             }
-                    
-            case CALL: {
+            
+            case CALLMETHOD:
+            case CALL:
+            {
                 JS.Array arguments = new JS.Array();
                 int numArgs = JS.toNumber(arg[pc]).intValue();
                 arguments.setSize(numArgs);
                 for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(t.pop(), j);
-                JS.Callable f = (JS.Callable)t.pop();
-                if (f == null) throw je("attempted to call null");
+                Object o = t.pop();
+                if(o == null) throw je("attempted to call null");
                 try {
-                    t.push(f.call(arguments));
+                    Object ret;
+                    if(op[pc] == CALLMETHOD) {
+                        Object method = o;
+                        o = t.pop();
+                        if(o instanceof String || o instanceof Number || o instanceof Boolean)
+                            ret = Internal.callMethodOnPrimitive(o,method,arguments);
+                        else if(o instanceof JS)
+                            ret = ((JS)o).callMethod(method,arguments,false);
+                        else
+                            throw new JS.Exn("Tried to call a method on an object that isn't a JS object");
+                    } else {                   
+                        ret = ((JS.Callable)o).call(arguments);
+                    }
+                    t.push(ret);
                     break;
                 } catch (JS.Exn e) {
                     t.push(e);
@@ -380,62 +392,6 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
         return sb.toString();
     } 
 
-    // Helpers for Number, String, and Boolean ////////////////////////////////////////
-
-    private static 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(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 JS.Exn("String.substring() can only take one or two arguments");
-                }
-            };
-        else if (v.equals("toLowerCase")) return new JS.Callable() {
-                public Object call(JS.Array args) {
-                    return ((String)o).toLowerCase();
-                } };
-        else if (v.equals("toUpperCase")) return new JS.Callable() {
-                public Object call(JS.Array args) {
-                    return ((String)o).toString().toUpperCase();
-                } };
-        else if (v.equals("charAt")) return new JS.Callable() {
-                public Object call(JS.Array args) {
-                    return ((String)o).charAt(JS.toNumber(args.elementAt(0)).intValue()) + "";
-                } };
-        else if (v.equals("lastIndexOf")) return new JS.Callable() {
-                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.Callable() {
-                public Object call(JS.Array args) {
-                    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");
-    }
-
-
     // Exception Stuff ////////////////////////////////////////////////////////////////
 
     static class EvaluatorException extends RuntimeException { public EvaluatorException(String s) { super(s); } }
index 187cb4d..542d465 100644 (file)
@@ -58,13 +58,13 @@ public abstract class JS {
         if (o instanceof JS) return ((JS)o).coerceToNumber();
         throw new Error("toNumber() got object of type " + o.getClass().getName() + " which we don't know how to handle");
     }
-
-
+    
     // Instance Methods ////////////////////////////////////////////////////////////////////
  
     public abstract Object get(Object key) throws JS.Exn; 
     public abstract void put(Object key, Object val) throws JS.Exn; 
     public abstract Object[] keys(); 
+    public abstract Object callMethod(Object method, JS.Array args, boolean justChecking);
 
     public Number coerceToNumber() { throw new Error("you cannot coerce a " + this.getClass().getName() + " into a Number"); }
     public String coerceToString() { throw new Error("you cannot coerce a " + this.getClass().getName() + " into a String"); }
@@ -83,9 +83,24 @@ public abstract class JS {
         public Obj(boolean sealed) { this.sealed = sealed; }
         /** a sealed object cannot have its properties modified */
         public void setSeal(boolean sealed) { this.sealed = sealed; }
-        public Object get(Object key) { return entries.get(key); }
         public void put(Object key, Object val) { if (!sealed) entries.put(key, val); }
         public Object[] keys() { return(entries.keys()); }
+        public Object get(Object key) {
+            if(callMethod((String)key,null,true) == Boolean.TRUE)
+                return new Internal.CallableStub(this,key);
+            return entries.get(key);
+        }
+        public Object callMethod(Object method, JS.Array args, boolean checkOnly) throws JS.Exn {
+            if(checkOnly) return Boolean.FALSE;
+            Object o = get(method);
+            if(o instanceof JS.Callable) {
+                return ((JS.Callable)o).call(args);
+            } else if(o == null) {
+                throw new JS.Exn("Attempted to call non-existent method: " + method);
+            } else {
+                throw new JS.Exn("Attempted to call a non-method: " +method);
+            }
+        }
     }
 
     /** An exception which can be thrown and caught by JavaScript code */
index cc3128b..3a3dcd9 100644 (file)
@@ -336,6 +336,11 @@ class Parser extends Lexer implements ByteCodes {
             b.add(parserLine, POP);
             break;
         }
+        case LP: {
+            int n = parseArgs(b);
+            b.add(parserLine,CALLMETHOD,new Integer(n));
+            break;
+        }
         default: {
             pushBackToken();
             b.add(parserLine, GET);
@@ -372,17 +377,8 @@ class Parser extends Lexer implements ByteCodes {
 
         switch (tok) {
         case LP: {  // invocation (not grouping)
-            int i = 0;
-            while(peekToken() != RP) {
-                i++;
-                if (peekToken() != COMMA) {
-                    startExpr(b, -1);
-                    if (peekToken() == RP) break;
-                }
-                consume(COMMA);
-            }
-            consume(RP);
-            b.add(parserLine, CALL, new Integer(i));
+            int n = parseArgs(b);
+            b.add(parserLine, CALL, new Integer(n));
             break;
         }
         case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH:
@@ -435,6 +431,21 @@ class Parser extends Lexer implements ByteCodes {
         continueExpr(b, minPrecedence);                           // try to continue the expression
     }
     
+    // parse a set of comma separated function arguments, assume LP has already been consumed
+    private int parseArgs(CompiledFunctionImpl b) throws IOException {
+        int i = 0;
+        while(peekToken() != RP) {
+            i++;
+            if (peekToken() != COMMA) {
+                startExpr(b, -1);
+                if (peekToken() == RP) break;
+            }
+            consume(COMMA);
+        }
+        consume(RP);
+        return i;
+    }
+    
     /** Parse a block of statements which must be surrounded by LC..RC. */
     void parseBlock(CompiledFunctionImpl b) throws IOException { parseBlock(b, null); }
     void parseBlock(CompiledFunctionImpl b, String label) throws IOException {