+++ /dev/null
-/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
- *\r
- * The contents of this file are subject to the Netscape Public\r
- * License Version 1.1 (the "License"); you may not use this file\r
- * except in compliance with the License. You may obtain a copy of\r
- * the License at http://www.mozilla.org/NPL/\r
- *\r
- * Software distributed under the License is distributed on an "AS\r
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
- * implied. See the License for the specific language governing\r
- * rights and limitations under the License.\r
- *\r
- * The Original Code is Rhino code, released\r
- * May 6, 1999.\r
- *\r
- * The Initial Developer of the Original Code is Netscape\r
- * Communications Corporation. Portions created by Netscape are\r
- * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
- * Rights Reserved.\r
- *\r
- * Contributor(s): \r
- * Norris Boyd\r
- * Mike McCabe\r
- * Igor Bukanov\r
- *\r
- * Alternatively, the contents of this file may be used under the\r
- * terms of the GNU Public License (the "GPL"), in which case the\r
- * provisions of the GPL are applicable instead of those above.\r
- * If you wish to allow use of your version of this file only\r
- * under the terms of the GPL and not to allow others to use your\r
- * version of this file under the NPL, indicate your decision by\r
- * deleting the provisions above and replace them with the notice\r
- * and other provisions required by the GPL. If you do not delete\r
- * the provisions above, a recipient may use your version of this\r
- * file under either the NPL or the GPL.\r
- */\r
-\r
-package org.mozilla.javascript;\r
-import java.util.Hashtable;\r
-\r
-/**\r
- * This class implements the Array native object.\r
- * @author Norris Boyd\r
- * @author Mike McCabe\r
- */\r
-public class NativeArray extends IdScriptable {\r
-\r
- /*\r
- * Optimization possibilities and open issues:\r
- * - Long vs. double schizophrenia. I suspect it might be better\r
- * to use double throughout.\r
-\r
- * - Most array operations go through getElem or setElem (defined\r
- * in this file) to handle the full 2^32 range; it might be faster\r
- * to have versions of most of the loops in this file for the\r
- * (infinitely more common) case of indices < 2^31.\r
-\r
- * - Functions that need a new Array call "new Array" in the\r
- * current scope rather than using a hardwired constructor;\r
- * "Array" could be redefined. It turns out that js calls the\r
- * equivalent of "new Array" in the current scope, except that it\r
- * always gets at least an object back, even when Array == null.\r
- */\r
-\r
- public static void init(Context cx, Scriptable scope, boolean sealed) {\r
- NativeArray obj = new NativeArray();\r
- obj.prototypeFlag = true;\r
- obj.addAsPrototype(MAX_PROTOTYPE_ID, cx, scope, sealed);\r
- }\r
-\r
- /**\r
- * Zero-parameter constructor: just used to create Array.prototype\r
- */\r
- public NativeArray() {\r
- dense = null;\r
- this.length = 0;\r
- }\r
-\r
- public NativeArray(long length) {\r
- int intLength = (int) length;\r
- if (intLength == length && intLength > 0) {\r
- if (intLength > maximumDenseLength)\r
- intLength = maximumDenseLength;\r
- dense = new Object[intLength];\r
- for (int i=0; i < intLength; i++)\r
- dense[i] = NOT_FOUND;\r
- }\r
- this.length = length;\r
- }\r
-\r
- public NativeArray(Object[] array) {\r
- dense = array;\r
- this.length = array.length;\r
- }\r
-\r
- public String getClassName() {\r
- return "Array";\r
- }\r
-\r
- protected int getIdDefaultAttributes(int id) {\r
- if (id == Id_length) {\r
- return DONTENUM | PERMANENT;\r
- }\r
- return super.getIdDefaultAttributes(id);\r
- }\r
-\r
- protected Object getIdValue(int id) {\r
- if (id == Id_length) {\r
- return wrap_double(length);\r
- }\r
- return super.getIdValue(id);\r
- }\r
- \r
- protected void setIdValue(int id, Object value) {\r
- if (id == Id_length) {\r
- jsSet_length(value); return;\r
- }\r
- super.setIdValue(id, value);\r
- }\r
- \r
- public int methodArity(int methodId) {\r
- if (prototypeFlag) {\r
- switch (methodId) {\r
- case Id_constructor: return 1;\r
- case Id_toString: return 0;\r
- case Id_toLocaleString: return 1;\r
- case Id_join: return 1;\r
- case Id_reverse: return 0;\r
- case Id_sort: return 1;\r
- case Id_push: return 1;\r
- case Id_pop: return 1;\r
- case Id_shift: return 1;\r
- case Id_unshift: return 1;\r
- case Id_splice: return 1;\r
- case Id_concat: return 1;\r
- case Id_slice: return 1;\r
- }\r
- }\r
- return super.methodArity(methodId);\r
- }\r
-\r
- public Object execMethod\r
- (int methodId, IdFunction f,\r
- Context cx, Scriptable scope, Scriptable thisObj, Object[] args)\r
- throws JavaScriptException\r
- {\r
- if (prototypeFlag) {\r
- switch (methodId) {\r
- case Id_constructor:\r
- return jsConstructor(cx, scope, args, f, thisObj == null);\r
-\r
- case Id_toString:\r
- return jsFunction_toString(cx, thisObj, args);\r
-\r
- case Id_toLocaleString:\r
- return jsFunction_toLocaleString(cx, thisObj, args);\r
-\r
- case Id_join:\r
- return jsFunction_join(cx, thisObj, args);\r
-\r
- case Id_reverse:\r
- return jsFunction_reverse(cx, thisObj, args);\r
-\r
- case Id_sort:\r
- return jsFunction_sort(cx, scope, thisObj, args);\r
-\r
- case Id_push:\r
- return jsFunction_push(cx, thisObj, args);\r
-\r
- case Id_pop:\r
- return jsFunction_pop(cx, thisObj, args);\r
-\r
- case Id_shift:\r
- return jsFunction_shift(cx, thisObj, args);\r
-\r
- case Id_unshift:\r
- return jsFunction_unshift(cx, thisObj, args);\r
-\r
- case Id_splice:\r
- return jsFunction_splice(cx, scope, thisObj, args);\r
-\r
- case Id_concat:\r
- return jsFunction_concat(cx, scope, thisObj, args);\r
-\r
- case Id_slice:\r
- return jsFunction_slice(cx, thisObj, args);\r
- }\r
- }\r
- return super.execMethod(methodId, f, cx, scope, thisObj, args);\r
- }\r
-\r
- public Object get(int index, Scriptable start) {\r
- if (dense != null && 0 <= index && index < dense.length)\r
- return dense[index];\r
- return super.get(index, start);\r
- }\r
-\r
- public boolean has(int index, Scriptable start) {\r
- if (dense != null && 0 <= index && index < dense.length)\r
- return dense[index] != NOT_FOUND;\r
- return super.has(index, start);\r
- }\r
-\r
- public void put(String id, Scriptable start, Object value) {\r
- if (start == this) {\r
- // only set the array length if given an array index (ECMA 15.4.0)\r
-\r
- // try to get an array index from id\r
- double d = ScriptRuntime.toNumber(id);\r
-\r
- if (ScriptRuntime.toUint32(d) == d &&\r
- ScriptRuntime.numberToString(d, 10).equals(id) &&\r
- this.length <= d && d != 4294967295.0)\r
- {\r
- this.length = (long)d + 1;\r
- }\r
- }\r
- super.put(id, start, value);\r
- }\r
-\r
- public void put(int index, Scriptable start, Object value) {\r
- if (start == this) {\r
- // only set the array length if given an array index (ECMA 15.4.0)\r
- if (this.length <= index) {\r
- // avoid overflowing index!\r
- this.length = (long)index + 1;\r
- }\r
-\r
- if (dense != null && 0 <= index && index < dense.length) {\r
- dense[index] = value;\r
- return;\r
- }\r
- }\r
- super.put(index, start, value);\r
- }\r
-\r
- public void delete(int index) {\r
- if (!isSealed()) {\r
- if (dense != null && 0 <= index && index < dense.length) {\r
- dense[index] = NOT_FOUND;\r
- return;\r
- }\r
- }\r
- super.delete(index);\r
- }\r
-\r
- public Object[] getIds() {\r
- Object[] superIds = super.getIds();\r
- if (dense == null)\r
- return superIds;\r
- int count = 0;\r
- int last = dense.length;\r
- if (last > length)\r
- last = (int) length;\r
- for (int i=last-1; i >= 0; i--) {\r
- if (dense[i] != NOT_FOUND)\r
- count++;\r
- }\r
- count += superIds.length;\r
- Object[] result = new Object[count];\r
- System.arraycopy(superIds, 0, result, 0, superIds.length);\r
- for (int i=last-1; i >= 0; i--) {\r
- if (dense[i] != NOT_FOUND)\r
- result[--count] = new Integer(i);\r
- }\r
- return result;\r
- }\r
- \r
- public Object getDefaultValue(Class hint) {\r
- if (hint == ScriptRuntime.NumberClass) {\r
- Context cx = Context.getContext();\r
- if (cx.getLanguageVersion() == Context.VERSION_1_2)\r
- return new Long(length);\r
- }\r
- return super.getDefaultValue(hint);\r
- }\r
-\r
- /**\r
- * See ECMA 15.4.1,2\r
- */\r
- private static Object jsConstructor(Context cx, Scriptable scope, \r
- Object[] args, IdFunction ctorObj,\r
- boolean inNewExpr)\r
- throws JavaScriptException\r
- {\r
- if (!inNewExpr) {\r
- // FunctionObject.construct will set up parent, proto\r
- return ctorObj.construct(cx, scope, args);\r
- }\r
- if (args.length == 0)\r
- return new NativeArray();\r
-\r
- // Only use 1 arg as first element for version 1.2; for\r
- // any other version (including 1.3) follow ECMA and use it as\r
- // a length.\r
- if (cx.getLanguageVersion() == cx.VERSION_1_2) {\r
- return new NativeArray(args);\r
- }\r
- else {\r
- if ((args.length > 1) || (!(args[0] instanceof Number)))\r
- return new NativeArray(args);\r
- else {\r
- long len = ScriptRuntime.toUint32(args[0]);\r
- if (len != (((Number)(args[0])).doubleValue()))\r
- throw Context.reportRuntimeError0("msg.arraylength.bad");\r
- return new NativeArray(len);\r
- }\r
- }\r
- }\r
-\r
- public long jsGet_length() {\r
- return length;\r
- }\r
-\r
- private void jsSet_length(Object val) {\r
- /* XXX do we satisfy this?\r
- * 15.4.5.1 [[Put]](P, V):\r
- * 1. Call the [[CanPut]] method of A with name P.\r
- * 2. If Result(1) is false, return.\r
- * ?\r
- */\r
-\r
- if (!(val instanceof Number))\r
- throw Context.reportRuntimeError0("msg.arraylength.bad");\r
- \r
- long longVal = ScriptRuntime.toUint32(val);\r
- if (longVal != (((Number)val).doubleValue()))\r
- throw Context.reportRuntimeError0("msg.arraylength.bad");\r
-\r
- if (longVal < length) {\r
- // remove all properties between longVal and length\r
- if (length - longVal > 0x1000) {\r
- // assume that the representation is sparse\r
- Object[] e = getIds(); // will only find in object itself\r
- for (int i=0; i < e.length; i++) {\r
- if (e[i] instanceof String) {\r
- // > MAXINT will appear as string\r
- String id = (String) e[i];\r
- double d = ScriptRuntime.toNumber(id);\r
- if (d == d && d < length)\r
- delete(id);\r
- continue;\r
- }\r
- int index = ((Number) e[i]).intValue();\r
- if (index >= longVal)\r
- delete(index);\r
- }\r
- } else {\r
- // assume a dense representation\r
- for (long i=longVal; i < length; i++) {\r
- // only delete if defined in the object itself\r
- if (hasElem(this, i))\r
- ScriptRuntime.delete(this, new Long(i));\r
- }\r
- }\r
- }\r
- length = longVal;\r
- }\r
-\r
- /* Support for generic Array-ish objects. Most of the Array\r
- * functions try to be generic; anything that has a length\r
- * property is assumed to be an array. hasLengthProperty is\r
- * needed in addition to getLengthProperty, because\r
- * getLengthProperty always succeeds - tries to convert strings, etc.\r
- */\r
- static double getLengthProperty(Scriptable obj) {\r
- // These will both give numeric lengths within Uint32 range.\r
- if (obj instanceof NativeString)\r
- return (double)((NativeString)obj).jsGet_length();\r
- if (obj instanceof NativeArray)\r
- return (double)((NativeArray)obj).jsGet_length();\r
- return ScriptRuntime.toUint32(ScriptRuntime\r
- .getProp(obj, "length", obj));\r
- }\r
-\r
- static boolean hasLengthProperty(Object obj) {\r
- if (!(obj instanceof Scriptable) || obj == Context.getUndefinedValue())\r
- return false;\r
- if (obj instanceof NativeString || obj instanceof NativeArray)\r
- return true;\r
- Scriptable sobj = (Scriptable)obj;\r
-\r
- // XXX some confusion as to whether or not to walk to get the length\r
- // property. Pending review of js/[new ecma submission] treatment\r
- // of 'arrayness'.\r
-\r
- Object property = ScriptRuntime.getProp(sobj, "length", sobj);\r
- return property instanceof Number;\r
- }\r
-\r
- /* Utility functions to encapsulate index > Integer.MAX_VALUE\r
- * handling. Also avoids unnecessary object creation that would\r
- * be necessary to use the general ScriptRuntime.get/setElem\r
- * functions... though this is probably premature optimization.\r
- */\r
- private static boolean hasElem(Scriptable target, long index) {\r
- return index > Integer.MAX_VALUE\r
- ? target.has(Long.toString(index), target)\r
- : target.has((int)index, target);\r
- }\r
-\r
- private static Object getElem(Scriptable target, long index) {\r
- if (index > Integer.MAX_VALUE) {\r
- String id = Long.toString(index);\r
- return ScriptRuntime.getElem(target, id, target);\r
- } else {\r
- return ScriptRuntime.getElem(target, (int)index);\r
- }\r
- }\r
-\r
- private static void setElem(Scriptable target, long index, Object value) {\r
- if (index > Integer.MAX_VALUE) {\r
- String id = Long.toString(index);\r
- ScriptRuntime.setElem(target, id, value, target);\r
- } else {\r
- ScriptRuntime.setElem(target, (int)index, value);\r
- }\r
- }\r
-\r
- private static String jsFunction_toString(Context cx, Scriptable thisObj,\r
- Object[] args)\r
- throws JavaScriptException\r
- {\r
- return toStringHelper(cx, thisObj, \r
- cx.getLanguageVersion() == cx.VERSION_1_2,\r
- false);\r
- }\r
- \r
- private static String jsFunction_toLocaleString(Context cx, \r
- Scriptable thisObj,\r
- Object[] args)\r
- throws JavaScriptException\r
- {\r
- return toStringHelper(cx, thisObj, false, true);\r
- }\r
- \r
- private static String toStringHelper(Context cx, Scriptable thisObj,\r
- boolean toSource, boolean toLocale)\r
- throws JavaScriptException\r
- {\r
- /* It's probably redundant to handle long lengths in this\r
- * function; StringBuffers are limited to 2^31 in java.\r
- */\r
-\r
- long length = (long)getLengthProperty(thisObj);\r
-\r
- StringBuffer result = new StringBuffer();\r
-\r
- if (cx.iterating == null)\r
- cx.iterating = new Hashtable(31);\r
- boolean iterating = cx.iterating.get(thisObj) == Boolean.TRUE;\r
-\r
- // whether to return '4,unquoted,5' or '[4, "quoted", 5]'\r
- String separator;\r
-\r
- if (toSource) {\r
- result.append('[');\r
- separator = ", ";\r
- } else {\r
- separator = ",";\r
- }\r
-\r
- boolean haslast = false;\r
- long i = 0;\r
-\r
- if (!iterating) {\r
- for (i = 0; i < length; i++) {\r
- if (i > 0)\r
- result.append(separator);\r
- Object elem = getElem(thisObj, i);\r
- if (elem == null || elem == Undefined.instance) {\r
- haslast = false;\r
- continue;\r
- }\r
- haslast = true;\r
-\r
- if (elem instanceof String) {\r
- if (toSource) {\r
- result.append('\"');\r
- result.append(ScriptRuntime.escapeString\r
- (ScriptRuntime.toString(elem)));\r
- result.append('\"');\r
- } else {\r
- result.append(ScriptRuntime.toString(elem));\r
- }\r
- } else {\r
- /* wrap changes to cx.iterating in a try/finally\r
- * so that the reference always gets removed, and\r
- * we don't leak memory. Good place for weak\r
- * references, if we had them. */\r
- try {\r
- // stop recursion.\r
- cx.iterating.put(thisObj, Boolean.TRUE);\r
- if (toLocale && elem != Undefined.instance && \r
- elem != null) \r
- {\r
- Scriptable obj = cx.toObject(elem, thisObj);\r
- Object tls = ScriptRuntime.getProp(obj, \r
- "toLocaleString", thisObj);\r
- elem = ScriptRuntime.call(cx, tls, elem, \r
- ScriptRuntime.emptyArgs);\r
- }\r
- result.append(ScriptRuntime.toString(elem));\r
- } finally {\r
- cx.iterating.remove(thisObj);\r
- }\r
- }\r
- }\r
- }\r
-\r
- if (toSource) {\r
- //for [,,].length behavior; we want toString to be symmetric.\r
- if (!haslast && i > 0)\r
- result.append(", ]");\r
- else\r
- result.append(']');\r
- }\r
- return result.toString();\r
- }\r
-\r
- /**\r
- * See ECMA 15.4.4.3\r
- */\r
- private static String jsFunction_join(Context cx, Scriptable thisObj,\r
- Object[] args) \r
- {\r
- StringBuffer result = new StringBuffer();\r
- String separator;\r
-\r
- double length = getLengthProperty(thisObj);\r
-\r
- // if no args, use "," as separator\r
- if (args.length < 1) {\r
- separator = ",";\r
- } else {\r
- separator = ScriptRuntime.toString(args[0]);\r
- }\r
- for (long i=0; i < length; i++) {\r
- if (i > 0)\r
- result.append(separator);\r
- Object temp = getElem(thisObj, i);\r
- if (temp == null || temp == Undefined.instance)\r
- continue;\r
- result.append(ScriptRuntime.toString(temp));\r
- }\r
- return result.toString();\r
- }\r
-\r
- /**\r
- * See ECMA 15.4.4.4\r
- */\r
- private static Scriptable jsFunction_reverse(Context cx, \r
- Scriptable thisObj, \r
- Object[] args) \r
- {\r
- long len = (long)getLengthProperty(thisObj);\r
-\r
- long half = len / 2;\r
- for(long i=0; i < half; i++) {\r
- long j = len - i - 1;\r
- Object temp1 = getElem(thisObj, i);\r
- Object temp2 = getElem(thisObj, j);\r
- setElem(thisObj, i, temp2);\r
- setElem(thisObj, j, temp1);\r
- }\r
- return thisObj;\r
- }\r
-\r
- /**\r
- * See ECMA 15.4.4.5\r
- */\r
- private static Scriptable jsFunction_sort(Context cx, Scriptable scope,\r
- Scriptable thisObj, \r
- Object[] args)\r
- throws JavaScriptException\r
- {\r
- long length = (long)getLengthProperty(thisObj);\r
-\r
- Object compare;\r
- if (args.length > 0 && Undefined.instance != args[0])\r
- // sort with given compare function\r
- compare = args[0];\r
- else\r
- // sort with default compare\r
- compare = null;\r
-\r
-\r
- // OPT: Would it make sense to use the extended sort for very small\r
- // arrays?\r
-\r
- // Should we use the extended sort function, or the faster one?\r
- if (length >= Integer.MAX_VALUE) {\r
- qsort_extended(cx, compare, thisObj, 0, length - 1);\r
- } else {\r
- // copy the JS array into a working array, so it can be\r
- // sorted cheaply.\r
- Object[] working = new Object[(int)length];\r
- for (int i=0; i<length; i++) {\r
- working[i] = getElem(thisObj, i);\r
- }\r
-\r
- qsort(cx, compare, working, 0, (int)length - 1, scope);\r
-\r
- // copy the working array back into thisObj\r
- for (int i=0; i<length; i++) {\r
- setElem(thisObj, i, working[i]);\r
- }\r
- }\r
- return thisObj;\r
- }\r
-\r
- private static double qsortCompare(Context cx, Object jsCompare, Object x,\r
- Object y, Scriptable scope)\r
- throws JavaScriptException\r
- {\r
- Object undef = Undefined.instance;\r
-\r
- // sort undefined to end\r
- if (undef == x || undef == y) {\r
- if (undef != x)\r
- return -1;\r
- if (undef != y)\r
- return 1;\r
- return 0;\r
- }\r
-\r
- if (jsCompare == null) {\r
- // if no compare function supplied, sort lexicographically\r
- String a = ScriptRuntime.toString(x);\r
- String b = ScriptRuntime.toString(y);\r
-\r
- return a.compareTo(b);\r
- } else {\r
- // assemble args and call supplied JS compare function\r
- // OPT: put this argument creation in the caller and reuse it.\r
- // XXX what to do when compare function returns NaN? ECMA states\r
- // that it's then not a 'consistent compararison function'... but\r
- // then what do we do? Back out and start over with the generic\r
- // compare function when we see a NaN? Throw an error?\r
- Object[] args = {x, y};\r
-// return ScriptRuntime.toNumber(ScriptRuntime.call(jsCompare, null,\r
-// args));\r
- // for now, just ignore it:\r
- double d = ScriptRuntime.\r
- toNumber(ScriptRuntime.call(cx, jsCompare, null, args, scope));\r
-\r
- return (d == d) ? d : 0;\r
- }\r
- }\r
-\r
- private static void qsort(Context cx, Object jsCompare, Object[] working,\r
- int lo, int hi, Scriptable scope)\r
- throws JavaScriptException\r
- {\r
- Object pivot;\r
- int i, j;\r
- int a, b;\r
-\r
- while (lo < hi) {\r
- i = lo;\r
- j = hi;\r
- a = i;\r
- pivot = working[a];\r
- while (i < j) {\r
- for(;;) {\r
- b = j;\r
- if (qsortCompare(cx, jsCompare, working[j], pivot, \r
- scope) <= 0)\r
- break;\r
- j--;\r
- }\r
- working[a] = working[b];\r
- while (i < j && qsortCompare(cx, jsCompare, working[a],\r
- pivot, scope) <= 0)\r
- {\r
- i++;\r
- a = i;\r
- }\r
- working[b] = working[a];\r
- }\r
- working[a] = pivot;\r
- if (i - lo < hi - i) {\r
- qsort(cx, jsCompare, working, lo, i - 1, scope);\r
- lo = i + 1;\r
- } else {\r
- qsort(cx, jsCompare, working, i + 1, hi, scope);\r
- hi = i - 1;\r
- }\r
- }\r
- }\r
-\r
- // A version that knows about long indices and doesn't use\r
- // a working array. Probably will never be used.\r
- private static void qsort_extended(Context cx, Object jsCompare,\r
- Scriptable target, long lo, long hi)\r
- throws JavaScriptException\r
- {\r
- Object pivot;\r
- long i, j;\r
- long a, b;\r
-\r
- while (lo < hi) {\r
- i = lo;\r
- j = hi;\r
- a = i;\r
- pivot = getElem(target, a);\r
- while (i < j) {\r
- for(;;) {\r
- b = j;\r
- if (qsortCompare(cx, jsCompare, getElem(target, j),\r
- pivot, target) <= 0)\r
- break;\r
- j--;\r
- }\r
- setElem(target, a, getElem(target, b));\r
- while (i < j && qsortCompare(cx, jsCompare,\r
- getElem(target, a), \r
- pivot, target) <= 0)\r
- {\r
- i++;\r
- a = i;\r
- }\r
- setElem(target, b, getElem(target, a));\r
- }\r
- setElem(target, a, pivot);\r
- if (i - lo < hi - i) {\r
- qsort_extended(cx, jsCompare, target, lo, i - 1);\r
- lo = i + 1;\r
- } else {\r
- qsort_extended(cx, jsCompare, target, i + 1, hi);\r
- hi = i - 1;\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Non-ECMA methods.\r
- */\r
-\r
- private static Object jsFunction_push(Context cx, Scriptable thisObj,\r
- Object[] args)\r
- {\r
- double length = getLengthProperty(thisObj);\r
- for (int i = 0; i < args.length; i++) {\r
- setElem(thisObj, (long)length + i, args[i]);\r
- }\r
-\r
- length += args.length;\r
- ScriptRuntime.setProp(thisObj, "length", new Double(length), thisObj);\r
-\r
- /*\r
- * If JS1.2, follow Perl4 by returning the last thing pushed.\r
- * Otherwise, return the new array length.\r
- */\r
- if (cx.getLanguageVersion() == Context.VERSION_1_2)\r
- // if JS1.2 && no arguments, return undefined.\r
- return args.length == 0\r
- ? Context.getUndefinedValue()\r
- : args[args.length - 1];\r
-\r
- else\r
- return new Long((long)length);\r
- }\r
-\r
- private static Object jsFunction_pop(Context cx, Scriptable thisObj,\r
- Object[] args) {\r
- Object result;\r
- double length = getLengthProperty(thisObj);\r
- if (length > 0) {\r
- length--;\r
-\r
- // Get the to-be-deleted property's value.\r
- result = getElem(thisObj, (long)length);\r
-\r
- // We don't need to delete the last property, because\r
- // setLength does that for us.\r
- } else {\r
- result = Context.getUndefinedValue();\r
- }\r
- // necessary to match js even when length < 0; js pop will give a\r
- // length property to any target it is called on.\r
- ScriptRuntime.setProp(thisObj, "length", new Double(length), thisObj);\r
-\r
- return result;\r
- }\r
-\r
- private static Object jsFunction_shift(Context cx, Scriptable thisObj,\r
- Object[] args)\r
- {\r
- Object result;\r
- double length = getLengthProperty(thisObj);\r
- if (length > 0) {\r
- long i = 0;\r
- length--;\r
-\r
- // Get the to-be-deleted property's value.\r
- result = getElem(thisObj, i);\r
-\r
- /*\r
- * Slide down the array above the first element. Leave i\r
- * set to point to the last element.\r
- */\r
- if (length > 0) {\r
- for (i = 1; i <= length; i++) {\r
- Object temp = getElem(thisObj, i);\r
- setElem(thisObj, i - 1, temp);\r
- }\r
- }\r
- // We don't need to delete the last property, because\r
- // setLength does that for us.\r
- } else {\r
- result = Context.getUndefinedValue();\r
- }\r
- ScriptRuntime.setProp(thisObj, "length", new Double(length), thisObj);\r
- return result;\r
- }\r
-\r
- private static Object jsFunction_unshift(Context cx, Scriptable thisObj,\r
- Object[] args)\r
- {\r
- Object result;\r
- double length = (double)getLengthProperty(thisObj);\r
- int argc = args.length;\r
-\r
- if (args.length > 0) {\r
- /* Slide up the array to make room for args at the bottom */\r
- if (length > 0) {\r
- for (long last = (long)length - 1; last >= 0; last--) {\r
- Object temp = getElem(thisObj, last);\r
- setElem(thisObj, last + argc, temp);\r
- }\r
- }\r
-\r
- /* Copy from argv to the bottom of the array. */\r
- for (int i = 0; i < args.length; i++) {\r
- setElem(thisObj, i, args[i]);\r
- }\r
-\r
- /* Follow Perl by returning the new array length. */\r
- length += args.length;\r
- ScriptRuntime.setProp(thisObj, "length",\r
- new Double(length), thisObj);\r
- }\r
- return new Long((long)length);\r
- }\r
-\r
- private static Object jsFunction_splice(Context cx, Scriptable scope,\r
- Scriptable thisObj, Object[] args)\r
- {\r
- /* create an empty Array to return. */\r
- scope = getTopLevelScope(scope);\r
- Object result = ScriptRuntime.newObject(cx, scope, "Array", null);\r
- int argc = args.length;\r
- if (argc == 0)\r
- return result;\r
- double length = getLengthProperty(thisObj);\r
-\r
- /* Convert the first argument into a starting index. */\r
- double begin = ScriptRuntime.toInteger(args[0]);\r
- double end;\r
- double delta;\r
- double count;\r
-\r
- if (begin < 0) {\r
- begin += length;\r
- if (begin < 0)\r
- begin = 0;\r
- } else if (begin > length) {\r
- begin = length;\r
- }\r
- argc--;\r
-\r
- /* Convert the second argument from a count into a fencepost index. */\r
- delta = length - begin;\r
-\r
- if (args.length == 1) {\r
- count = delta;\r
- end = length;\r
- } else {\r
- count = ScriptRuntime.toInteger(args[1]);\r
- if (count < 0)\r
- count = 0;\r
- else if (count > delta)\r
- count = delta;\r
- end = begin + count;\r
-\r
- argc--;\r
- }\r
-\r
- long lbegin = (long)begin;\r
- long lend = (long)end;\r
-\r
- /* If there are elements to remove, put them into the return value. */\r
- if (count > 0) {\r
- if (count == 1\r
- && (cx.getLanguageVersion() == Context.VERSION_1_2))\r
- {\r
- /*\r
- * JS lacks "list context", whereby in Perl one turns the\r
- * single scalar that's spliced out into an array just by\r
- * assigning it to @single instead of $single, or by using it\r
- * as Perl push's first argument, for instance.\r
- *\r
- * JS1.2 emulated Perl too closely and returned a non-Array for\r
- * the single-splice-out case, requiring callers to test and\r
- * wrap in [] if necessary. So JS1.3, default, and other\r
- * versions all return an array of length 1 for uniformity.\r
- */\r
- result = getElem(thisObj, lbegin);\r
- } else {\r
- for (long last = lbegin; last < lend; last++) {\r
- Scriptable resultArray = (Scriptable)result;\r
- Object temp = getElem(thisObj, last);\r
- setElem(resultArray, last - lbegin, temp);\r
- }\r
- }\r
- } else if (count == 0\r
- && cx.getLanguageVersion() == Context.VERSION_1_2)\r
- {\r
- /* Emulate C JS1.2; if no elements are removed, return undefined. */\r
- result = Context.getUndefinedValue();\r
- }\r
-\r
- /* Find the direction (up or down) to copy and make way for argv. */\r
- delta = argc - count;\r
-\r
- if (delta > 0) {\r
- for (long last = (long)length - 1; last >= lend; last--) {\r
- Object temp = getElem(thisObj, last);\r
- setElem(thisObj, last + (long)delta, temp);\r
- }\r
- } else if (delta < 0) {\r
- for (long last = lend; last < length; last++) {\r
- Object temp = getElem(thisObj, last);\r
- setElem(thisObj, last + (long)delta, temp);\r
- }\r
- }\r
-\r
- /* Copy from argv into the hole to complete the splice. */\r
- int argoffset = args.length - argc;\r
- for (int i = 0; i < argc; i++) {\r
- setElem(thisObj, lbegin + i, args[i + argoffset]);\r
- }\r
-\r
- /* Update length in case we deleted elements from the end. */\r
- ScriptRuntime.setProp(thisObj, "length",\r
- new Double(length + delta), thisObj);\r
- return result;\r
- }\r
-\r
- /*\r
- * Python-esque sequence operations.\r
- */\r
- private static Scriptable jsFunction_concat(Context cx, Scriptable scope, \r
- Scriptable thisObj,\r
- Object[] args)\r
- {\r
- /* Concat tries to keep the definition of an array as general\r
- * as possible; if it finds that an object has a numeric\r
- * 'length' property, then it treats that object as an array.\r
- * This treats string atoms and string objects differently; as\r
- * string objects have a length property and are accessible by\r
- * index, they get exploded into arrays when added, while\r
- * atomic strings are just added as strings.\r
- */\r
-\r
- // create an empty Array to return.\r
- scope = getTopLevelScope(scope);\r
- Scriptable result = ScriptRuntime.newObject(cx, scope, "Array", null);\r
- double length;\r
- long slot = 0;\r
-\r
- /* Put the target in the result array; only add it as an array\r
- * if it looks like one.\r
- */\r
- if (hasLengthProperty(thisObj)) {\r
- length = getLengthProperty(thisObj);\r
-\r
- // Copy from the target object into the result\r
- for (slot = 0; slot < length; slot++) {\r
- Object temp = getElem(thisObj, slot);\r
- setElem(result, slot, temp);\r
- }\r
- } else {\r
- setElem(result, slot++, thisObj);\r
- }\r
-\r
- /* Copy from the arguments into the result. If any argument\r
- * has a numeric length property, treat it as an array and add\r
- * elements separately; otherwise, just copy the argument.\r
- */\r
- for (int i = 0; i < args.length; i++) {\r
- if (hasLengthProperty(args[i])) {\r
- // hasLengthProperty => instanceOf Scriptable.\r
- Scriptable arg = (Scriptable)args[i];\r
- length = getLengthProperty(arg);\r
- for (long j = 0; j < length; j++, slot++) {\r
- Object temp = getElem(arg, j);\r
- setElem(result, slot, temp);\r
- }\r
- } else {\r
- setElem(result, slot++, args[i]);\r
- }\r
- }\r
- return result;\r
- }\r
-\r
- private Scriptable jsFunction_slice(Context cx, Scriptable thisObj,\r
- Object[] args)\r
- {\r
- Scriptable scope = getTopLevelScope(this);\r
- Scriptable result = ScriptRuntime.newObject(cx, scope, "Array", null);\r
- double length = getLengthProperty(thisObj);\r
-\r
- double begin = 0;\r
- double end = length;\r
-\r
- if (args.length > 0) {\r
- begin = ScriptRuntime.toInteger(args[0]);\r
- if (begin < 0) {\r
- begin += length;\r
- if (begin < 0)\r
- begin = 0;\r
- } else if (begin > length) {\r
- begin = length;\r
- }\r
-\r
- if (args.length > 1) {\r
- end = ScriptRuntime.toInteger(args[1]);\r
- if (end < 0) {\r
- end += length;\r
- if (end < 0)\r
- end = 0;\r
- } else if (end > length) {\r
- end = length;\r
- }\r
- }\r
- }\r
-\r
- long lbegin = (long)begin;\r
- long lend = (long)end;\r
- for (long slot = lbegin; slot < lend; slot++) {\r
- Object temp = getElem(thisObj, slot);\r
- setElem(result, slot - lbegin, temp);\r
- }\r
-\r
- return result;\r
- }\r
-\r
- protected int maxInstanceId() { return MAX_INSTANCE_ID; }\r
-\r
- protected String getIdName(int id) {\r
- if (id == Id_length) { return "length"; }\r
-\r
- if (prototypeFlag) {\r
- switch (id) {\r
- case Id_constructor: return "constructor";\r
- case Id_toString: return "toString";\r
- case Id_toLocaleString: return "toLocaleString";\r
- case Id_join: return "join";\r
- case Id_reverse: return "reverse";\r
- case Id_sort: return "sort";\r
- case Id_push: return "push";\r
- case Id_pop: return "pop";\r
- case Id_shift: return "shift";\r
- case Id_unshift: return "unshift";\r
- case Id_splice: return "splice";\r
- case Id_concat: return "concat";\r
- case Id_slice: return "slice";\r
- }\r
- }\r
- return null;\r
- }\r
-\r
- private static final int\r
- Id_length = 1,\r
- MAX_INSTANCE_ID = 1;\r
-\r
- protected int mapNameToId(String s) {\r
- if (s.equals("length")) { return Id_length; }\r
- else if (prototypeFlag) { \r
- return toPrototypeId(s); \r
- }\r
- return 0;\r
- }\r
-\r
-// #string_id_map#\r
-\r
- private static int toPrototypeId(String s) {\r
- int id;\r
-// #generated# Last update: 2001-04-23 11:46:01 GMT+02:00\r
- L0: { id = 0; String X = null; int c;\r
- L: switch (s.length()) {\r
- case 3: X="pop";id=Id_pop; break L;\r
- case 4: c=s.charAt(0);\r
- if (c=='j') { X="join";id=Id_join; }\r
- else if (c=='p') { X="push";id=Id_push; }\r
- else if (c=='s') { X="sort";id=Id_sort; }\r
- break L;\r
- case 5: c=s.charAt(1);\r
- if (c=='h') { X="shift";id=Id_shift; }\r
- else if (c=='l') { X="slice";id=Id_slice; }\r
- break L;\r
- case 6: c=s.charAt(0);\r
- if (c=='c') { X="concat";id=Id_concat; }\r
- else if (c=='l') { X="length";id=Id_length; }\r
- else if (c=='s') { X="splice";id=Id_splice; }\r
- break L;\r
- case 7: c=s.charAt(0);\r
- if (c=='r') { X="reverse";id=Id_reverse; }\r
- else if (c=='u') { X="unshift";id=Id_unshift; }\r
- break L;\r
- case 8: X="toString";id=Id_toString; break L;\r
- case 11: X="constructor";id=Id_constructor; break L;\r
- case 14: X="toLocaleString";id=Id_toLocaleString; break L;\r
- }\r
- if (X!=null && X!=s && !X.equals(s)) id = 0;\r
- }\r
-// #/generated#\r
- return id;\r
- }\r
-\r
- private static final int\r
- Id_constructor = MAX_INSTANCE_ID + 1,\r
- Id_toString = MAX_INSTANCE_ID + 2,\r
- Id_toLocaleString = MAX_INSTANCE_ID + 3,\r
- Id_join = MAX_INSTANCE_ID + 4,\r
- Id_reverse = MAX_INSTANCE_ID + 5,\r
- Id_sort = MAX_INSTANCE_ID + 6,\r
- Id_push = MAX_INSTANCE_ID + 7,\r
- Id_pop = MAX_INSTANCE_ID + 8,\r
- Id_shift = MAX_INSTANCE_ID + 9,\r
- Id_unshift = MAX_INSTANCE_ID + 10,\r
- Id_splice = MAX_INSTANCE_ID + 11,\r
- Id_concat = MAX_INSTANCE_ID + 12,\r
- Id_slice = MAX_INSTANCE_ID + 13,\r
-\r
- MAX_PROTOTYPE_ID = MAX_INSTANCE_ID + 13;\r
-\r
-// #/string_id_map#\r
-\r
- private long length;\r
- private Object[] dense;\r
- private static final int maximumDenseLength = 10000;\r
- \r
- private boolean prototypeFlag;\r
-}\r