From 1da33fe9e7c5104ec5d0b9b312d1114fdf2931be Mon Sep 17 00:00:00 2001 From: brian Date: Fri, 30 Jan 2004 07:02:49 +0000 Subject: [PATCH] 2003/07/05 03:23:31 darcs-hash:20040130070249-aa32f-6a9615781acf55e91008b0e48a2cde5c956da7f9.gz --- src/org/xwt/Box.java | 3 + src/org/xwt/js/ArrayImpl.java | 1 + src/org/xwt/js/ByteCodes.java | 8 ++- src/org/xwt/js/CompiledFunctionImpl.java | 94 ++++++++---------------------- src/org/xwt/js/JS.java | 21 ++++++- src/org/xwt/js/Parser.java | 33 +++++++---- 6 files changed, 75 insertions(+), 85 deletions(-) diff --git a/src/org/xwt/Box.java b/src/org/xwt/Box.java index 1f6cb0c..f04c3c2 100644 --- a/src/org/xwt/Box.java +++ b/src/org/xwt/Box.java @@ -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); + } } diff --git a/src/org/xwt/js/ArrayImpl.java b/src/org/xwt/js/ArrayImpl.java index 13f7d80..110b301 100644 --- a/src/org/xwt/js/ArrayImpl.java +++ b/src/org/xwt/js/ArrayImpl.java @@ -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 { diff --git a/src/org/xwt/js/ByteCodes.java b/src/org/xwt/js/ByteCodes.java index c74c564..bac280a 100644 --- a/src/org/xwt/js/ByteCodes.java +++ b/src/org/xwt/js/ByteCodes.java @@ -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" }; } diff --git a/src/org/xwt/js/CompiledFunctionImpl.java b/src/org/xwt/js/CompiledFunctionImpl.java index ae344d0..0f08f2b 100644 --- a/src/org/xwt/js/CompiledFunctionImpl.java +++ b/src/org/xwt/js/CompiledFunctionImpl.java @@ -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); } } diff --git a/src/org/xwt/js/JS.java b/src/org/xwt/js/JS.java index 187cb4d..542d465 100644 --- a/src/org/xwt/js/JS.java +++ b/src/org/xwt/js/JS.java @@ -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 */ diff --git a/src/org/xwt/js/Parser.java b/src/org/xwt/js/Parser.java index cc3128b..3a3dcd9 100644 --- a/src/org/xwt/js/Parser.java +++ b/src/org/xwt/js/Parser.java @@ -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 { -- 1.7.10.4