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);
+ }
}
import java.util.*;
// FIXME: could use some cleaning up...
+// FIXME: Finish implementing ECMA-262
/** A JavaScript Array */
class ArrayImpl extends JS.Obj {
/** 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"
};
}
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);
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); } }
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"); }
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 */
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);
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:
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 {