-// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+// Copyright 2000-2005 the Contributors, as shown in the revision logs.
+// Licensed under the Apache Public Source License 2.0 ("the License").
+// You may not use this file except in compliance with the License.
+
package org.ibex.js;
+import java.io.InputStream;
import org.ibex.util.*;
-import java.util.*;
/** A JavaScript JSArray */
-public class JSArray extends JS {
- private static final Object NULL = new Object();
-
+public class JSArray extends Basket.Array implements JS, Basket.CompareFunc {
+ private static final JS.Method METHOD = new JS.Method();
+ private static final String[] empty = new String[0];
+
public JSArray() { }
- public JSArray(int size) { 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;
+ public JSArray(int size) { super(size); }
+ public JSArray(JS[] args) { super(args.length); addAll(args); }
+ public JSArray(JS arg) { super(1); add(arg); }
+
+ public JS unclone() { return this; }
+ public JS.Enumeration keys() throws JSExn {
+ return new Enumeration(null) {
+ private int n = 0;
+ public boolean _hasNext() { return n < size(); }
+ public JS _next() { return JSU.N(n++); }
+ };
+ }
+ public JS get(JS key) throws JSExn {
+ if (key == null || !(key instanceof JSNumber.I)) {
+ //#switch(JSU.str(key))
+ case "pop": return METHOD;
+ case "reverse": return METHOD;
+ case "toString": return METHOD;
+ case "shift": return METHOD;
+ case "join": return METHOD;
+ case "sort": return METHOD;
+ case "slice": return METHOD;
+ case "push": return METHOD;
+ case "unshift": return METHOD;
+ case "splice": return METHOD;
+ case "length": return JSU.N(size());
+ //#end
+ throw new JSExn("arrays only support positive integer keys, can not use: "+JSU.str(key));
}
- 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);
+ return (JS)get(((JSNumber.I)key).toInt());
}
-
- public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
- //#switch(method)
- case "pop": {
- int oldSize = size();
- if(oldSize == 0) return null;
- return removeElementAt(oldSize-1);
- }
- case "reverse": return reverse();
+ public void put(JS key, JS val) throws JSExn {
+ if (JSU.str(key).equals("length")) { setSize(JSU.toInt(val)); }
+
+ if (key == null || !(key instanceof JSNumber.I)) throw new JSExn(
+ "arrays only support positive integer keys, can not use: "+JSU.str(key));
+ int i = ((JSNumber.I)key).toInt();
+ if (i < 0) throw new JSExn("arrays can not use negative integer keys "+i);
+ size(i + 1); while (size() < i) add(null);
+ set(i, val);
+ }
+
+ public String[] getFormalArgs() { return empty; }
+ public String coerceToString() throws JSExn { throw new JSExn("cannot coerce a "+getClass().getName()+" to a string"); }
+
+ public JS call(JS method, JS[] args) throws JSExn {
+ //#switch(JSU.str(method))
+ case "pop": return size() == 0 ? null : (JS)remove(size() - 1);
+ case "push": addAll(args); return JSU.N(size());
+ case "reverse": reverse(); return this;
case "toString": return join(",");
- case "shift":
- if(length() == 0) return null;
- return removeElementAt(0);
- case "join":
- return join(nargs == 0 ? "," : JS.toString(a0));
- case "sort":
- return sort(nargs < 1 ? null : a0);
+ case "shift": return size() == 0 ? null : (JS)remove(0);
+ case "join": return join(args.length == 0 ? "," : JSU.str(args[0]));
+ case "sort": return sort(args.length == 0 ? null : args[0]);
case "slice":
- int start = toInt(nargs < 1 ? null : a0);
- int end = nargs < 2 ? length() : toInt(a1);
+ int start = JSU.toInt(args.length < 1 ? null : args[0]);
+ int end = args.length < 2 ? size() : JSU.toInt(args[1]);
return slice(start, end);
- case "push": {
- int oldSize = size();
- for(int i=0; i<nargs; i++) insertElementAt(i==0?a0:i==1?a1:i==2?a2:rest[i-3],oldSize+i);
- return N(oldSize + nargs);
- }
case "unshift":
- for(int i=0; i<nargs; i++) insertElementAt(i==0?a0:i==1?a1:i==2?a2:rest[i-3],i);
- return N(size());
- case "splice":
- JSArray array = new JSArray();
- for(int i=0; i<nargs; i++) array.addElement(i==0?a0:i==1?a1:i==2?a2:rest[i-3]);
- return splice(array);
+ for (int i=0; i < args.length; i++) add(i, args[i]);
+ return JSU.N(size());
+ case "splice": return splice(args);
//#end
- return super.callMethod(method, a0, a1, a2, rest, nargs);
- }
-
- public Object get(Object key) throws JSExn {
- int i = intVal(key);
- if (i != Integer.MIN_VALUE) {
- if (i < 0 || i >= size()) return null;
- return elementAt(i);
- }
- //#switch(key)
- case "pop": return METHOD;
- case "reverse": return METHOD;
- case "toString": return METHOD;
- case "shift": return METHOD;
- case "join": return METHOD;
- case "sort": return METHOD;
- case "slice": return METHOD;
- case "push": return METHOD;
- case "unshift": return METHOD;
- case "splice": return METHOD;
- case "length": return N(size());
- //#end
- return super.get(key);
+ throw new JSExn("arrays have no function: "+JSU.str(method));
}
- public void put(Object key, Object val) throws JSExn {
- if (key.equals("length")) setSize(toInt(val));
- int i = intVal(key);
- if (i == Integer.MIN_VALUE)
- super.put(key, val);
- else {
- int oldSize = size();
- if(i < oldSize) {
- setElementAt(val,i);
- } else {
- if(i > oldSize) setSize(i);
- insertElementAt(val,i);
- }
- }
- }
+ public JS putAndTriggerTraps(JS key, JS val) throws JSExn { put(key, val); return val; }
+ public JS getAndTriggerTraps(JS key) throws JSExn { return get(key); }
+ public JS justTriggerTraps(JS key, JS val) throws JSExn { return val; }
- public Enumeration keys() {
- return new Enumeration() {
- private int n = size();
- public boolean hasMoreElements() { return n > 0; }
- public Object nextElement() {
- if(n == 0) throw new NoSuchElementException();
- return new Integer(--n);
- }
- };
- }
+ public void addTrap(JS k, JS f) throws JSExn { throw new JSExn("arrays do not support traps"); }
+ public void delTrap(JS k, JS f) throws JSExn { throw new JSExn("arrays do not support traps"); }
+ public JS.Trap getTrap(JS k) throws JSExn { throw new JSExn("arrays do not support traps"); }
- public final void setSize(int newSize) {
- // FEATURE: This could be done a lot more efficiently in BalancedTree
- int oldSize = size();
- for(int i=oldSize;i<newSize;i++) insertElementAt(null,i);
- for(int i=oldSize-1;i>=newSize;i--) removeElementAt(i);
- }
-
- public final int length() { return size(); }
- public final Object elementAt(int i) {
- if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
- Object o = getNode(i);
- return o == NULL ? null : o;
- }
- public final void addElement(Object o) {
- insertNode(size(),o==null ? NULL : o);
- }
- public final void setElementAt(Object o, int i) {
- if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
- replaceNode(i,o==null ? NULL : o);
- }
- public final void insertElementAt(Object o, int i) {
- if(i < 0 || i > size()) throw new ArrayIndexOutOfBoundsException(i);
- insertNode(i,o==null ? NULL : o);
- }
- public final Object removeElementAt(int i) {
- if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
- Object o = deleteNode(i);
- return o == NULL ? null : o;
+ /** FEATURE: move to specialised ArrayStore superclass. */
+ public void addAll(JS[] entries) { for (int i=0; i < entries.length; i++) add(entries[i]); }
+
+ public void setSize(int newSize) {
+ size(newSize);
+ for (int i=size(); i < newSize; i++) add(null);
+ for (int i=size() - 1; i >= newSize; i--) remove(i);
}
-
- public final int size() { return treeSize(); }
- public String typeName() { return "array"; }
-
- private Object join(String sep) {
+
+
+ // ECMA Implementation ////////////////////////////////////////////////////
+
+ private JS join(String sep) throws JSExn {
int length = size();
- if(length == 0) return "";
+ if(length == 0) return JSU.S("");
StringBuffer sb = new StringBuffer(64);
int i=0;
while(true) {
- Object o = elementAt(i);
- if(o != null) sb.append(JS.toString(o));
+ JS o = (JS)get(i);
+ if(o != null) sb.append(JSU.toString(o));
if(++i == length) break;
sb.append(sep);
}
- return sb.toString();
+ return JSU.S(sb.toString());
}
-
- // FEATURE: Implement this more efficiently
- private Object reverse() {
- int size = size();
- if(size < 2) return this;
- Vec vec = toVec();
- clear();
- for(int i=size-1,j=0;i>=0;i--,j++) insertElementAt(vec.elementAt(i),j);
- return this;
- }
-
- private Object slice(int start, int end) {
- int length = length();
+
+ private JS slice(int start, int end) {
+ int length = size();
if(start < 0) start = length+start;
if(end < 0) end = length+end;
if(start < 0) start = 0;
if(start > length) start = length;
if(end > length) end = length;
JSArray a = new JSArray(end-start);
- for(int i=0;i<end-start;i++)
- a.setElementAt(elementAt(start+i),i);
+ for(int i=0;i<end-start;i++) // FEATURE: with ArrayStore do System.arraycopy()
+ a.set(i, get(start+i));
return a;
}
-
- private static final Vec.CompareFunc defaultSort = new Vec.CompareFunc() {
- public int compare(Object a, Object b) {
- return JS.toString(a).compareTo(JS.toString(b));
- }
- };
- private Object sort(Object tmp) throws JSExn {
- Vec vec = toVec();
- if(tmp instanceof JS) {
- final JSArray funcArgs = new JSArray(2);
- final JS jsFunc = (JS) tmp;
- vec.sort(new Vec.CompareFunc() {
- public int compare(Object a, Object b) {
- try {
- funcArgs.setElementAt(a,0);
- funcArgs.setElementAt(b,1);
- return JS.toInt(jsFunc.call(a, b, null, null, 2));
- } catch (Exception e) {
- // FIXME
- throw new JSRuntimeExn(e.toString());
- }
- }
- });
- } else {
- vec.sort(defaultSort);
- }
- setFromVec(vec);
- return this;
- }
-
- private Object splice(JSArray args) {
- int oldLength = length();
- int start = JS.toInt(args.length() < 1 ? null : args.elementAt(0));
- int deleteCount = JS.toInt(args.length() < 2 ? null : args.elementAt(1));
- int newCount = args.length() - 2;
+
+ private JS splice(JS[] args) throws JSExn {
+ int oldLength = size();
+ int start = JSU.toInt(args.length < 1 ? null : args[0]);
+ int deleteCount = JSU.toInt(args.length < 2 ? null : args[1]);
+ int newCount = args.length - 2;
if(newCount < 0) newCount = 0;
if(start < 0) start = oldLength+start;
if(start < 0) start = 0;
int newLength = oldLength - deleteCount + newCount;
int lengthChange = newLength - oldLength;
JSArray ret = new JSArray(deleteCount);
- for(int i=0;i<deleteCount;i++)
- ret.setElementAt(elementAt(start+i),i);
+ for(int i=0;i<deleteCount;i++) // FEATURE: ArrayStore System.arraycopy()
+ ret.set(i, get(start+i));
if(lengthChange > 0) {
setSize(newLength);
for(int i=newLength-1;i>=start+newCount;i--)
- setElementAt(elementAt(i-lengthChange),i);
+ set(i, get(i-lengthChange));
} else if(lengthChange < 0) {
for(int i=start+newCount;i<newLength;i++)
- setElementAt(elementAt(i-lengthChange),i);
+ set(i, get(i-lengthChange));
setSize(newLength);
}
for(int i=0;i<newCount;i++)
- setElementAt(args.elementAt(i+2),start+i);
+ set(start+i, args[i+2]);
return ret;
}
-
- protected Vec toVec() {
- int count = size();
- Vec vec = new Vec();
- vec.setSize(count);
- for(int i=0;i<count;i++) {
- Object o = getNode(i);
- vec.setElementAt(o == NULL ? null : o,i);
+
+ private static final Basket.CompareFunc defaultSort = new Basket.CompareFunc() {
+ public int compare(Object a, Object b) {
+ try { return JSU.toString((JS)a).compareTo(JSU.toString((JS)b)); }
+ catch (JSExn e) { throw new JSExn.Wrapper(e); }
}
- return vec;
+ };
+ private JS sort(JS comparator) throws JSExn {
+ try {
+ if (comparator == null) sort(defaultSort);
+ else { sort = comparator; sort((CompareFunc)this); }
+ return this;
+ } catch (JSExn.Wrapper w) { throw w.unwrap();
+ } finally { sort = null; }
}
-
- protected void setFromVec(Vec vec) {
- int count = vec.size();
- clear();
- for(int i=0;i<count;i++) {
- Object o = vec.elementAt(i);
- insertNode(i,o==null ? NULL : o);
- }
+
+ private JS sort = null;
+ private final JS[] sortargs = new JS[2];
+ public int compare(Object a, Object b) throws JSExn.Wrapper {
+ try {
+ sortargs[0] = (JS)a; sortargs[1] = (JS)b;
+ return JSU.toInt(sort.call(null, sortargs));
+ } catch (JSExn e) { throw new JSExn.Wrapper(e);
+ } finally { sortargs[0] = null; sortargs[1] = null; }
}
-
- public String toString() { return JS.toString(join(",")); }
+
}