0001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
0002: *
0003: * ***** BEGIN LICENSE BLOCK *****
0004: * Version: MPL 1.1/GPL 2.0
0005: *
0006: * The contents of this file are subject to the Mozilla Public License Version
0007: * 1.1 (the "License"); you may not use this file except in compliance with
0008: * the License. You may obtain a copy of the License at
0009: * http://www.mozilla.org/MPL/
0010: *
0011: * Software distributed under the License is distributed on an "AS IS" basis,
0012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0013: * for the specific language governing rights and limitations under the
0014: * License.
0015: *
0016: * The Original Code is Rhino code, released
0017: * May 6, 1999.
0018: *
0019: * The Initial Developer of the Original Code is
0020: * Netscape Communications Corporation.
0021: * Portions created by the Initial Developer are Copyright (C) 1997-1999
0022: * the Initial Developer. All Rights Reserved.
0023: *
0024: * Contributor(s):
0025: * Norris Boyd
0026: * Mike McCabe
0027: * Igor Bukanov
0028: *
0029: * Alternatively, the contents of this file may be used under the terms of
0030: * the GNU General Public License Version 2 or later (the "GPL"), in which
0031: * case the provisions of the GPL are applicable instead of those above. If
0032: * you wish to allow use of your version of this file only under the terms of
0033: * the GPL and not to allow others to use your version of this file under the
0034: * MPL, indicate your decision by deleting the provisions above and replacing
0035: * them with the notice and other provisions required by the GPL. If you do
0036: * not delete the provisions above, a recipient may use your version of this
0037: * file under either the MPL or the GPL.
0038: *
0039: * ***** END LICENSE BLOCK ***** */
0040:
0041: package org.mozilla.javascript;
0042:
0043: import java.util.Arrays;
0044:
0045: /**
0046: * This class implements the Array native object.
0047: * @author Norris Boyd
0048: * @author Mike McCabe
0049: */
0050: public class NativeArray extends IdScriptableObject {
0051: static final long serialVersionUID = 7331366857676127338L;
0052:
0053: /*
0054: * Optimization possibilities and open issues:
0055: * - Long vs. double schizophrenia. I suspect it might be better
0056: * to use double throughout.
0057: *
0058: * - Functions that need a new Array call "new Array" in the
0059: * current scope rather than using a hardwired constructor;
0060: * "Array" could be redefined. It turns out that js calls the
0061: * equivalent of "new Array" in the current scope, except that it
0062: * always gets at least an object back, even when Array == null.
0063: */
0064:
0065: private static final Object ARRAY_TAG = new Object();
0066: private static final Integer NEGATIVE_ONE = new Integer(-1);
0067:
0068: static void init(Scriptable scope, boolean sealed) {
0069: NativeArray obj = new NativeArray(0);
0070: obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
0071: }
0072:
0073: static int getMaximumInitialCapacity() {
0074: return maximumInitialCapacity;
0075: }
0076:
0077: static void setMaximumInitialCapacity(int maximumInitialCapacity) {
0078: NativeArray.maximumInitialCapacity = maximumInitialCapacity;
0079: }
0080:
0081: public NativeArray(long lengthArg) {
0082: denseOnly = lengthArg <= maximumInitialCapacity;
0083: if (denseOnly) {
0084: int intLength = (int) lengthArg;
0085: if (intLength < DEFAULT_INITIAL_CAPACITY)
0086: intLength = DEFAULT_INITIAL_CAPACITY;
0087: dense = new Object[intLength];
0088: Arrays.fill(dense, Scriptable.NOT_FOUND);
0089: }
0090: length = lengthArg;
0091: }
0092:
0093: public NativeArray(Object[] array) {
0094: denseOnly = true;
0095: dense = array;
0096: length = array.length;
0097: }
0098:
0099: public String getClassName() {
0100: return "Array";
0101: }
0102:
0103: private static final int Id_length = 1, MAX_INSTANCE_ID = 1;
0104:
0105: protected int getMaxInstanceId() {
0106: return MAX_INSTANCE_ID;
0107: }
0108:
0109: protected int findInstanceIdInfo(String s) {
0110: if (s.equals("length")) {
0111: return instanceIdInfo(DONTENUM | PERMANENT, Id_length);
0112: }
0113: return super .findInstanceIdInfo(s);
0114: }
0115:
0116: protected String getInstanceIdName(int id) {
0117: if (id == Id_length) {
0118: return "length";
0119: }
0120: return super .getInstanceIdName(id);
0121: }
0122:
0123: protected Object getInstanceIdValue(int id) {
0124: if (id == Id_length) {
0125: return ScriptRuntime.wrapNumber(length);
0126: }
0127: return super .getInstanceIdValue(id);
0128: }
0129:
0130: protected void setInstanceIdValue(int id, Object value) {
0131: if (id == Id_length) {
0132: setLength(value);
0133: return;
0134: }
0135: super .setInstanceIdValue(id, value);
0136: }
0137:
0138: protected void fillConstructorProperties(IdFunctionObject ctor) {
0139: addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_join,
0140: "join", 2);
0141: addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_reverse,
0142: "reverse", 1);
0143: addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_sort,
0144: "sort", 2);
0145: addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_push,
0146: "push", 2);
0147: addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_pop,
0148: "pop", 2);
0149: addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_shift,
0150: "shift", 2);
0151: addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_unshift,
0152: "unshift", 2);
0153: addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_splice,
0154: "splice", 2);
0155: addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_concat,
0156: "concat", 2);
0157: addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_slice,
0158: "slice", 2);
0159: addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_indexOf,
0160: "indexOf", 2);
0161: addIdFunctionProperty(ctor, ARRAY_TAG,
0162: ConstructorId_lastIndexOf, "lastIndexOf", 2);
0163: addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_every,
0164: "every", 2);
0165: addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_filter,
0166: "filter", 2);
0167: addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_forEach,
0168: "forEach", 2);
0169: addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_map,
0170: "map", 2);
0171: addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_some,
0172: "some", 2);
0173: super .fillConstructorProperties(ctor);
0174: }
0175:
0176: protected void initPrototypeId(int id) {
0177: String s;
0178: int arity;
0179: switch (id) {
0180: case Id_constructor:
0181: arity = 1;
0182: s = "constructor";
0183: break;
0184: case Id_toString:
0185: arity = 0;
0186: s = "toString";
0187: break;
0188: case Id_toLocaleString:
0189: arity = 1;
0190: s = "toLocaleString";
0191: break;
0192: case Id_toSource:
0193: arity = 0;
0194: s = "toSource";
0195: break;
0196: case Id_join:
0197: arity = 1;
0198: s = "join";
0199: break;
0200: case Id_reverse:
0201: arity = 0;
0202: s = "reverse";
0203: break;
0204: case Id_sort:
0205: arity = 1;
0206: s = "sort";
0207: break;
0208: case Id_push:
0209: arity = 1;
0210: s = "push";
0211: break;
0212: case Id_pop:
0213: arity = 1;
0214: s = "pop";
0215: break;
0216: case Id_shift:
0217: arity = 1;
0218: s = "shift";
0219: break;
0220: case Id_unshift:
0221: arity = 1;
0222: s = "unshift";
0223: break;
0224: case Id_splice:
0225: arity = 1;
0226: s = "splice";
0227: break;
0228: case Id_concat:
0229: arity = 1;
0230: s = "concat";
0231: break;
0232: case Id_slice:
0233: arity = 1;
0234: s = "slice";
0235: break;
0236: case Id_indexOf:
0237: arity = 1;
0238: s = "indexOf";
0239: break;
0240: case Id_lastIndexOf:
0241: arity = 1;
0242: s = "lastIndexOf";
0243: break;
0244: case Id_every:
0245: arity = 1;
0246: s = "every";
0247: break;
0248: case Id_filter:
0249: arity = 1;
0250: s = "filter";
0251: break;
0252: case Id_forEach:
0253: arity = 1;
0254: s = "forEach";
0255: break;
0256: case Id_map:
0257: arity = 1;
0258: s = "map";
0259: break;
0260: case Id_some:
0261: arity = 1;
0262: s = "some";
0263: break;
0264: default:
0265: throw new IllegalArgumentException(String.valueOf(id));
0266: }
0267: initPrototypeMethod(ARRAY_TAG, id, s, arity);
0268: }
0269:
0270: public Object execIdCall(IdFunctionObject f, Context cx,
0271: Scriptable scope, Scriptable this Obj, Object[] args) {
0272: if (!f.hasTag(ARRAY_TAG)) {
0273: return super .execIdCall(f, cx, scope, this Obj, args);
0274: }
0275: int id = f.methodId();
0276: again: for (;;) {
0277: switch (id) {
0278: case ConstructorId_join:
0279: case ConstructorId_reverse:
0280: case ConstructorId_sort:
0281: case ConstructorId_push:
0282: case ConstructorId_pop:
0283: case ConstructorId_shift:
0284: case ConstructorId_unshift:
0285: case ConstructorId_splice:
0286: case ConstructorId_concat:
0287: case ConstructorId_slice:
0288: case ConstructorId_indexOf:
0289: case ConstructorId_lastIndexOf:
0290: case ConstructorId_every:
0291: case ConstructorId_filter:
0292: case ConstructorId_forEach:
0293: case ConstructorId_map:
0294: case ConstructorId_some: {
0295: this Obj = ScriptRuntime.toObject(scope, args[0]);
0296: Object[] newArgs = new Object[args.length - 1];
0297: for (int i = 0; i < newArgs.length; i++)
0298: newArgs[i] = args[i + 1];
0299: args = newArgs;
0300: id = -id;
0301: continue again;
0302: }
0303:
0304: case Id_constructor: {
0305: boolean inNewExpr = (this Obj == null);
0306: if (!inNewExpr) {
0307: // IdFunctionObject.construct will set up parent, proto
0308: return f.construct(cx, scope, args);
0309: }
0310: return jsConstructor(cx, scope, args);
0311: }
0312:
0313: case Id_toString:
0314: return toStringHelper(
0315: cx,
0316: scope,
0317: this Obj,
0318: cx
0319: .hasFeature(Context.FEATURE_TO_STRING_AS_SOURCE),
0320: false);
0321:
0322: case Id_toLocaleString:
0323: return toStringHelper(cx, scope, this Obj, false, true);
0324:
0325: case Id_toSource:
0326: return toStringHelper(cx, scope, this Obj, true, false);
0327:
0328: case Id_join:
0329: return js_join(cx, this Obj, args);
0330:
0331: case Id_reverse:
0332: return js_reverse(cx, this Obj, args);
0333:
0334: case Id_sort:
0335: return js_sort(cx, scope, this Obj, args);
0336:
0337: case Id_push:
0338: return js_push(cx, this Obj, args);
0339:
0340: case Id_pop:
0341: return js_pop(cx, this Obj, args);
0342:
0343: case Id_shift:
0344: return js_shift(cx, this Obj, args);
0345:
0346: case Id_unshift:
0347: return js_unshift(cx, this Obj, args);
0348:
0349: case Id_splice:
0350: return js_splice(cx, scope, this Obj, args);
0351:
0352: case Id_concat:
0353: return js_concat(cx, scope, this Obj, args);
0354:
0355: case Id_slice:
0356: return js_slice(cx, this Obj, args);
0357:
0358: case Id_indexOf:
0359: return indexOfHelper(cx, this Obj, args, false);
0360:
0361: case Id_lastIndexOf:
0362: return indexOfHelper(cx, this Obj, args, true);
0363:
0364: case Id_every:
0365: case Id_filter:
0366: case Id_forEach:
0367: case Id_map:
0368: case Id_some:
0369: return iterativeMethod(cx, id, scope, this Obj, args);
0370: }
0371: throw new IllegalArgumentException(String.valueOf(id));
0372: }
0373: }
0374:
0375: public Object get(int index, Scriptable start) {
0376: if (!denseOnly && isGetterOrSetter(null, index, false))
0377: return super .get(index, start);
0378: if (dense != null && 0 <= index && index < dense.length)
0379: return dense[index];
0380: return super .get(index, start);
0381: }
0382:
0383: public boolean has(int index, Scriptable start) {
0384: if (!denseOnly && isGetterOrSetter(null, index, false))
0385: return super .has(index, start);
0386: if (dense != null && 0 <= index && index < dense.length)
0387: return dense[index] != NOT_FOUND;
0388: return super .has(index, start);
0389: }
0390:
0391: // if id is an array index (ECMA 15.4.0), return the number,
0392: // otherwise return -1L
0393: private static long toArrayIndex(String id) {
0394: double d = ScriptRuntime.toNumber(id);
0395: if (d == d) {
0396: long index = ScriptRuntime.toUint32(d);
0397: if (index == d && index != 4294967295L) {
0398: // Assume that ScriptRuntime.toString(index) is the same
0399: // as java.lang.Long.toString(index) for long
0400: if (Long.toString(index).equals(id)) {
0401: return index;
0402: }
0403: }
0404: }
0405: return -1;
0406: }
0407:
0408: public void put(String id, Scriptable start, Object value) {
0409: super .put(id, start, value);
0410: if (start == this ) {
0411: // If the object is sealed, super will throw exception
0412: long index = toArrayIndex(id);
0413: if (index >= length) {
0414: length = index + 1;
0415: denseOnly = false;
0416: }
0417: }
0418: }
0419:
0420: private boolean ensureCapacity(int capacity) {
0421: if (capacity > dense.length) {
0422: if (capacity > MAX_PRE_GROW_SIZE) {
0423: denseOnly = false;
0424: return false;
0425: }
0426: capacity = Math.max(capacity,
0427: (int) (dense.length * GROW_FACTOR));
0428: Object[] newDense = new Object[capacity];
0429: System.arraycopy(dense, 0, newDense, 0, dense.length);
0430: Arrays.fill(newDense, dense.length, newDense.length,
0431: Scriptable.NOT_FOUND);
0432: dense = newDense;
0433: }
0434: return true;
0435: }
0436:
0437: public void put(int index, Scriptable start, Object value) {
0438: if (start == this && !isSealed() && dense != null && 0 <= index
0439: && (denseOnly || !isGetterOrSetter(null, index, true))) {
0440: if (index < dense.length) {
0441: dense[index] = value;
0442: if (this .length <= index)
0443: this .length = (long) index + 1;
0444: return;
0445: } else if (denseOnly && index < dense.length * GROW_FACTOR
0446: && ensureCapacity(index + 1)) {
0447: dense[index] = value;
0448: this .length = (long) index + 1;
0449: return;
0450: } else {
0451: denseOnly = false;
0452: }
0453: }
0454: super .put(index, start, value);
0455: if (start == this ) {
0456: // only set the array length if given an array index (ECMA 15.4.0)
0457: if (this .length <= index) {
0458: // avoid overflowing index!
0459: this .length = (long) index + 1;
0460: }
0461: }
0462: }
0463:
0464: public void delete(int index) {
0465: if (dense != null && 0 <= index && index < dense.length
0466: && !isSealed()
0467: && (denseOnly || !isGetterOrSetter(null, index, true))) {
0468: dense[index] = NOT_FOUND;
0469: } else {
0470: super .delete(index);
0471: }
0472: }
0473:
0474: public Object[] getIds() {
0475: Object[] super Ids = super .getIds();
0476: if (dense == null) {
0477: return super Ids;
0478: }
0479: int N = dense.length;
0480: long currentLength = length;
0481: if (N > currentLength) {
0482: N = (int) currentLength;
0483: }
0484: if (N == 0) {
0485: return super Ids;
0486: }
0487: int super Length = super Ids.length;
0488: Object[] ids = new Object[N + super Length];
0489:
0490: int presentCount = 0;
0491: for (int i = 0; i != N; ++i) {
0492: // Replace existing elements by their indexes
0493: if (dense[i] != NOT_FOUND) {
0494: ids[presentCount] = new Integer(i);
0495: ++presentCount;
0496: }
0497: }
0498: if (presentCount != N) {
0499: // dense contains deleted elems, need to shrink the result
0500: Object[] tmp = new Object[presentCount + super Length];
0501: System.arraycopy(ids, 0, tmp, 0, presentCount);
0502: ids = tmp;
0503: }
0504: System.arraycopy(super Ids, 0, ids, presentCount, super Length);
0505: return ids;
0506: }
0507:
0508: public Object getDefaultValue(Class hint) {
0509: if (hint == ScriptRuntime.NumberClass) {
0510: Context cx = Context.getContext();
0511: if (cx.getLanguageVersion() == Context.VERSION_1_2)
0512: return new Long(length);
0513: }
0514: return super .getDefaultValue(hint);
0515: }
0516:
0517: /**
0518: * See ECMA 15.4.1,2
0519: */
0520: private static Object jsConstructor(Context cx, Scriptable scope,
0521: Object[] args) {
0522: if (args.length == 0)
0523: return new NativeArray(0);
0524:
0525: // Only use 1 arg as first element for version 1.2; for
0526: // any other version (including 1.3) follow ECMA and use it as
0527: // a length.
0528: if (cx.getLanguageVersion() == Context.VERSION_1_2) {
0529: return new NativeArray(args);
0530: } else {
0531: Object arg0 = args[0];
0532: if (args.length > 1 || !(arg0 instanceof Number)) {
0533: return new NativeArray(args);
0534: } else {
0535: long len = ScriptRuntime.toUint32(arg0);
0536: if (len != ((Number) arg0).doubleValue())
0537: throw Context
0538: .reportRuntimeError0("msg.arraylength.bad");
0539: return new NativeArray(len);
0540: }
0541: }
0542: }
0543:
0544: public long getLength() {
0545: return length;
0546: }
0547:
0548: /** @deprecated Use {@link #getLength()} instead. */
0549: public long jsGet_length() {
0550: return getLength();
0551: }
0552:
0553: /**
0554: * Change the value of the internal flag that determines whether all
0555: * storage is handed by a dense backing array rather than an associative
0556: * store.
0557: * @param denseOnly new value for denseOnly flag
0558: * @throws IllegalArgumentException if an attempt is made to enable
0559: * denseOnly after it was disabled; NativeArray code is not written
0560: * to handle switching back to a dense representation
0561: */
0562: void setDenseOnly(boolean denseOnly) {
0563: if (denseOnly && !this .denseOnly)
0564: throw new IllegalArgumentException();
0565: this .denseOnly = denseOnly;
0566: }
0567:
0568: private void setLength(Object val) {
0569: /* XXX do we satisfy this?
0570: * 15.4.5.1 [[Put]](P, V):
0571: * 1. Call the [[CanPut]] method of A with name P.
0572: * 2. If Result(1) is false, return.
0573: * ?
0574: */
0575:
0576: double d = ScriptRuntime.toNumber(val);
0577: long longVal = ScriptRuntime.toUint32(d);
0578: if (longVal != d)
0579: throw Context.reportRuntimeError0("msg.arraylength.bad");
0580:
0581: if (denseOnly) {
0582: if (longVal < length) {
0583: // downcast okay because denseOnly
0584: Arrays.fill(dense, (int) longVal, dense.length,
0585: NOT_FOUND);
0586: length = longVal;
0587: return;
0588: } else if (longVal < MAX_PRE_GROW_SIZE
0589: && longVal < (length * GROW_FACTOR)
0590: && ensureCapacity((int) longVal)) {
0591: length = longVal;
0592: return;
0593: } else {
0594: denseOnly = false;
0595: }
0596: }
0597: if (longVal < length) {
0598: // remove all properties between longVal and length
0599: if (length - longVal > 0x1000) {
0600: // assume that the representation is sparse
0601: Object[] e = getIds(); // will only find in object itself
0602: for (int i = 0; i < e.length; i++) {
0603: Object id = e[i];
0604: if (id instanceof String) {
0605: // > MAXINT will appear as string
0606: String strId = (String) id;
0607: long index = toArrayIndex(strId);
0608: if (index >= longVal)
0609: delete(strId);
0610: } else {
0611: int index = ((Integer) id).intValue();
0612: if (index >= longVal)
0613: delete(index);
0614: }
0615: }
0616: } else {
0617: // assume a dense representation
0618: for (long i = longVal; i < length; i++) {
0619: deleteElem(this , i);
0620: }
0621: }
0622: }
0623: length = longVal;
0624: }
0625:
0626: /* Support for generic Array-ish objects. Most of the Array
0627: * functions try to be generic; anything that has a length
0628: * property is assumed to be an array.
0629: * getLengthProperty returns 0 if obj does not have the length property
0630: * or its value is not convertible to a number.
0631: */
0632: static long getLengthProperty(Context cx, Scriptable obj) {
0633: // These will both give numeric lengths within Uint32 range.
0634: if (obj instanceof NativeString) {
0635: return ((NativeString) obj).getLength();
0636: } else if (obj instanceof NativeArray) {
0637: return ((NativeArray) obj).getLength();
0638: }
0639: return ScriptRuntime.toUint32(ScriptRuntime.getObjectProp(obj,
0640: "length", cx));
0641: }
0642:
0643: private static Object setLengthProperty(Context cx,
0644: Scriptable target, long length) {
0645: return ScriptRuntime.setObjectProp(target, "length",
0646: ScriptRuntime.wrapNumber(length), cx);
0647: }
0648:
0649: /* Utility functions to encapsulate index > Integer.MAX_VALUE
0650: * handling. Also avoids unnecessary object creation that would
0651: * be necessary to use the general ScriptRuntime.get/setElem
0652: * functions... though this is probably premature optimization.
0653: */
0654: private static void deleteElem(Scriptable target, long index) {
0655: int i = (int) index;
0656: if (i == index) {
0657: target.delete(i);
0658: } else {
0659: target.delete(Long.toString(index));
0660: }
0661: }
0662:
0663: private static Object getElem(Context cx, Scriptable target,
0664: long index) {
0665: if (index > Integer.MAX_VALUE) {
0666: String id = Long.toString(index);
0667: return ScriptRuntime.getObjectProp(target, id, cx);
0668: } else {
0669: return ScriptRuntime
0670: .getObjectIndex(target, (int) index, cx);
0671: }
0672: }
0673:
0674: private static void setElem(Context cx, Scriptable target,
0675: long index, Object value) {
0676: if (index > Integer.MAX_VALUE) {
0677: String id = Long.toString(index);
0678: ScriptRuntime.setObjectProp(target, id, value, cx);
0679: } else {
0680: ScriptRuntime
0681: .setObjectIndex(target, (int) index, value, cx);
0682: }
0683: }
0684:
0685: private static String toStringHelper(Context cx, Scriptable scope,
0686: Scriptable this Obj, boolean toSource, boolean toLocale) {
0687: /* It's probably redundant to handle long lengths in this
0688: * function; StringBuffers are limited to 2^31 in java.
0689: */
0690:
0691: long length = getLengthProperty(cx, this Obj);
0692:
0693: StringBuffer result = new StringBuffer(256);
0694:
0695: // whether to return '4,unquoted,5' or '[4, "quoted", 5]'
0696: String separator;
0697:
0698: if (toSource) {
0699: result.append('[');
0700: separator = ", ";
0701: } else {
0702: separator = ",";
0703: }
0704:
0705: boolean haslast = false;
0706: long i = 0;
0707:
0708: boolean toplevel, iterating;
0709: if (cx.iterating == null) {
0710: toplevel = true;
0711: iterating = false;
0712: cx.iterating = new ObjToIntMap(31);
0713: } else {
0714: toplevel = false;
0715: iterating = cx.iterating.has(this Obj);
0716: }
0717:
0718: // Make sure cx.iterating is set to null when done
0719: // so we don't leak memory
0720: try {
0721: if (!iterating) {
0722: cx.iterating.put(this Obj, 0); // stop recursion.
0723: for (i = 0; i < length; i++) {
0724: if (i > 0)
0725: result.append(separator);
0726: Object elem = getElem(cx, this Obj, i);
0727: if (elem == null || elem == Undefined.instance) {
0728: haslast = false;
0729: continue;
0730: }
0731: haslast = true;
0732:
0733: if (toSource) {
0734: result.append(ScriptRuntime.uneval(cx, scope,
0735: elem));
0736:
0737: } else if (elem instanceof String) {
0738: String s = (String) elem;
0739: if (toSource) {
0740: result.append('\"');
0741: result
0742: .append(ScriptRuntime
0743: .escapeString(s));
0744: result.append('\"');
0745: } else {
0746: result.append(s);
0747: }
0748:
0749: } else {
0750: if (toLocale) {
0751: Callable fun;
0752: Scriptable funThis;
0753: fun = ScriptRuntime.getPropFunctionAndThis(
0754: elem, "toLocaleString", cx);
0755: funThis = ScriptRuntime
0756: .lastStoredScriptable(cx);
0757: elem = fun.call(cx, scope, funThis,
0758: ScriptRuntime.emptyArgs);
0759: }
0760: result.append(ScriptRuntime.toString(elem));
0761: }
0762: }
0763: }
0764: } finally {
0765: if (toplevel) {
0766: cx.iterating = null;
0767: }
0768: }
0769:
0770: if (toSource) {
0771: //for [,,].length behavior; we want toString to be symmetric.
0772: if (!haslast && i > 0)
0773: result.append(", ]");
0774: else
0775: result.append(']');
0776: }
0777: return result.toString();
0778: }
0779:
0780: /**
0781: * See ECMA 15.4.4.3
0782: */
0783: private static String js_join(Context cx, Scriptable this Obj,
0784: Object[] args) {
0785: long llength = getLengthProperty(cx, this Obj);
0786: int length = (int) llength;
0787: if (llength != length) {
0788: throw Context.reportRuntimeError1(
0789: "msg.arraylength.too.big", String.valueOf(llength));
0790: }
0791: // if no args, use "," as separator
0792: String separator = (args.length < 1 || args[0] == Undefined.instance) ? ","
0793: : ScriptRuntime.toString(args[0]);
0794: if (this Obj instanceof NativeArray) {
0795: NativeArray na = (NativeArray) this Obj;
0796: if (na.denseOnly) {
0797: StringBuffer sb = new StringBuffer();
0798: for (int i = 0; i < length; i++) {
0799: if (i != 0) {
0800: sb.append(separator);
0801: }
0802: if (i < na.dense.length) {
0803: Object temp = na.dense[i];
0804: if (temp != null && temp != Undefined.instance
0805: && temp != Scriptable.NOT_FOUND) {
0806: sb.append(ScriptRuntime.toString(temp));
0807: }
0808: }
0809: }
0810: return sb.toString();
0811: }
0812: }
0813: if (length == 0) {
0814: return "";
0815: }
0816: String[] buf = new String[length];
0817: int total_size = 0;
0818: for (int i = 0; i != length; i++) {
0819: Object temp = getElem(cx, this Obj, i);
0820: if (temp != null && temp != Undefined.instance) {
0821: String str = ScriptRuntime.toString(temp);
0822: total_size += str.length();
0823: buf[i] = str;
0824: }
0825: }
0826: total_size += (length - 1) * separator.length();
0827: StringBuffer sb = new StringBuffer(total_size);
0828: for (int i = 0; i != length; i++) {
0829: if (i != 0) {
0830: sb.append(separator);
0831: }
0832: String str = buf[i];
0833: if (str != null) {
0834: // str == null for undefined or null
0835: sb.append(str);
0836: }
0837: }
0838: return sb.toString();
0839: }
0840:
0841: /**
0842: * See ECMA 15.4.4.4
0843: */
0844: private static Scriptable js_reverse(Context cx,
0845: Scriptable this Obj, Object[] args) {
0846: if (this Obj instanceof NativeArray) {
0847: NativeArray na = (NativeArray) this Obj;
0848: if (na.denseOnly) {
0849: for (int i = 0, j = ((int) na.length) - 1; i < j; i++, j--) {
0850: Object temp = na.dense[i];
0851: na.dense[i] = na.dense[j];
0852: na.dense[j] = temp;
0853: }
0854: return this Obj;
0855: }
0856: }
0857: long len = getLengthProperty(cx, this Obj);
0858:
0859: long half = len / 2;
0860: for (long i = 0; i < half; i++) {
0861: long j = len - i - 1;
0862: Object temp1 = getElem(cx, this Obj, i);
0863: Object temp2 = getElem(cx, this Obj, j);
0864: setElem(cx, this Obj, i, temp2);
0865: setElem(cx, this Obj, j, temp1);
0866: }
0867: return this Obj;
0868: }
0869:
0870: /**
0871: * See ECMA 15.4.4.5
0872: */
0873: private static Scriptable js_sort(Context cx, Scriptable scope,
0874: Scriptable this Obj, Object[] args) {
0875: long length = getLengthProperty(cx, this Obj);
0876:
0877: if (length <= 1) {
0878: return this Obj;
0879: }
0880:
0881: Object compare;
0882: Object[] cmpBuf;
0883:
0884: if (args.length > 0 && Undefined.instance != args[0]) {
0885: // sort with given compare function
0886: compare = args[0];
0887: cmpBuf = new Object[2]; // Buffer for cmp arguments
0888: } else {
0889: // sort with default compare
0890: compare = null;
0891: cmpBuf = null;
0892: }
0893: if (this Obj instanceof NativeArray) {
0894: NativeArray na = (NativeArray) this Obj;
0895: if (na.denseOnly) {
0896: int ilength = (int) length;
0897: heapsort(cx, scope, na.dense, ilength, compare, cmpBuf);
0898: return this Obj;
0899: }
0900: }
0901:
0902: // Should we use the extended sort function, or the faster one?
0903: if (length >= Integer.MAX_VALUE) {
0904: heapsort_extended(cx, scope, this Obj, length, compare,
0905: cmpBuf);
0906: } else {
0907: int ilength = (int) length;
0908: // copy the JS array into a working array, so it can be
0909: // sorted cheaply.
0910: Object[] working = new Object[ilength];
0911: for (int i = 0; i != ilength; ++i) {
0912: working[i] = getElem(cx, this Obj, i);
0913: }
0914:
0915: heapsort(cx, scope, working, ilength, compare, cmpBuf);
0916:
0917: // copy the working array back into thisObj
0918: for (int i = 0; i != ilength; ++i) {
0919: setElem(cx, this Obj, i, working[i]);
0920: }
0921: }
0922: return this Obj;
0923: }
0924:
0925: // Return true only if x > y
0926: private static boolean isBigger(Context cx, Scriptable scope,
0927: Object x, Object y, Object cmp, Object[] cmpBuf) {
0928: if (cmp == null) {
0929: if (cmpBuf != null)
0930: Kit.codeBug();
0931: } else {
0932: if (cmpBuf == null || cmpBuf.length != 2)
0933: Kit.codeBug();
0934: }
0935:
0936: Object undef = Undefined.instance;
0937: Object notfound = Scriptable.NOT_FOUND;
0938:
0939: // sort undefined to end
0940: if (y == undef || y == notfound) {
0941: return false; // x can not be bigger then undef
0942: } else if (x == undef || x == notfound) {
0943: return true; // y != undef here, so x > y
0944: }
0945:
0946: if (cmp == null) {
0947: // if no cmp function supplied, sort lexicographically
0948: String a = ScriptRuntime.toString(x);
0949: String b = ScriptRuntime.toString(y);
0950: return a.compareTo(b) > 0;
0951: } else {
0952: // assemble args and call supplied JS cmp function
0953: cmpBuf[0] = x;
0954: cmpBuf[1] = y;
0955: Callable fun = ScriptRuntime.getValueFunctionAndThis(cmp,
0956: cx);
0957: Scriptable funThis = ScriptRuntime.lastStoredScriptable(cx);
0958:
0959: Object ret = fun.call(cx, scope, funThis, cmpBuf);
0960: double d = ScriptRuntime.toNumber(ret);
0961:
0962: // XXX what to do when cmp function returns NaN? ECMA states
0963: // that it's then not a 'consistent comparison function'... but
0964: // then what do we do? Back out and start over with the generic
0965: // cmp function when we see a NaN? Throw an error?
0966:
0967: // for now, just ignore it:
0968:
0969: return d > 0;
0970: }
0971: }
0972:
0973: /** Heapsort implementation.
0974: * See "Introduction to Algorithms" by Cormen, Leiserson, Rivest for details.
0975: * Adjusted for zero based indexes.
0976: */
0977: private static void heapsort(Context cx, Scriptable scope,
0978: Object[] array, int length, Object cmp, Object[] cmpBuf) {
0979: if (length <= 1)
0980: Kit.codeBug();
0981:
0982: // Build heap
0983: for (int i = length / 2; i != 0;) {
0984: --i;
0985: Object pivot = array[i];
0986: heapify(cx, scope, pivot, array, i, length, cmp, cmpBuf);
0987: }
0988:
0989: // Sort heap
0990: for (int i = length; i != 1;) {
0991: --i;
0992: Object pivot = array[i];
0993: array[i] = array[0];
0994: heapify(cx, scope, pivot, array, 0, i, cmp, cmpBuf);
0995: }
0996: }
0997:
0998: /** pivot and child heaps of i should be made into heap starting at i,
0999: * original array[i] is never used to have less array access during sorting.
1000: */
1001: private static void heapify(Context cx, Scriptable scope,
1002: Object pivot, Object[] array, int i, int end, Object cmp,
1003: Object[] cmpBuf) {
1004: for (;;) {
1005: int child = i * 2 + 1;
1006: if (child >= end) {
1007: break;
1008: }
1009: Object childVal = array[child];
1010: if (child + 1 < end) {
1011: Object nextVal = array[child + 1];
1012: if (isBigger(cx, scope, nextVal, childVal, cmp, cmpBuf)) {
1013: ++child;
1014: childVal = nextVal;
1015: }
1016: }
1017: if (!isBigger(cx, scope, childVal, pivot, cmp, cmpBuf)) {
1018: break;
1019: }
1020: array[i] = childVal;
1021: i = child;
1022: }
1023: array[i] = pivot;
1024: }
1025:
1026: /** Version of heapsort that call getElem/setElem on target to query/assign
1027: * array elements instead of Java array access
1028: */
1029: private static void heapsort_extended(Context cx, Scriptable scope,
1030: Scriptable target, long length, Object cmp, Object[] cmpBuf) {
1031: if (length <= 1)
1032: Kit.codeBug();
1033:
1034: // Build heap
1035: for (long i = length / 2; i != 0;) {
1036: --i;
1037: Object pivot = getElem(cx, target, i);
1038: heapify_extended(cx, scope, pivot, target, i, length, cmp,
1039: cmpBuf);
1040: }
1041:
1042: // Sort heap
1043: for (long i = length; i != 1;) {
1044: --i;
1045: Object pivot = getElem(cx, target, i);
1046: setElem(cx, target, i, getElem(cx, target, 0));
1047: heapify_extended(cx, scope, pivot, target, 0, i, cmp,
1048: cmpBuf);
1049: }
1050: }
1051:
1052: private static void heapify_extended(Context cx, Scriptable scope,
1053: Object pivot, Scriptable target, long i, long end,
1054: Object cmp, Object[] cmpBuf) {
1055: for (;;) {
1056: long child = i * 2 + 1;
1057: if (child >= end) {
1058: break;
1059: }
1060: Object childVal = getElem(cx, target, child);
1061: if (child + 1 < end) {
1062: Object nextVal = getElem(cx, target, child + 1);
1063: if (isBigger(cx, scope, nextVal, childVal, cmp, cmpBuf)) {
1064: ++child;
1065: childVal = nextVal;
1066: }
1067: }
1068: if (!isBigger(cx, scope, childVal, pivot, cmp, cmpBuf)) {
1069: break;
1070: }
1071: setElem(cx, target, i, childVal);
1072: i = child;
1073: }
1074: setElem(cx, target, i, pivot);
1075: }
1076:
1077: /**
1078: * Non-ECMA methods.
1079: */
1080:
1081: private static Object js_push(Context cx, Scriptable this Obj,
1082: Object[] args) {
1083: if (this Obj instanceof NativeArray) {
1084: NativeArray na = (NativeArray) this Obj;
1085: if (na.denseOnly
1086: && na.ensureCapacity((int) na.length + args.length)) {
1087: for (int i = 0; i < args.length; i++) {
1088: na.dense[(int) na.length++] = args[i];
1089: }
1090: return ScriptRuntime.wrapNumber(na.length);
1091: }
1092: }
1093: long length = getLengthProperty(cx, this Obj);
1094: for (int i = 0; i < args.length; i++) {
1095: setElem(cx, this Obj, length + i, args[i]);
1096: }
1097:
1098: length += args.length;
1099: Object lengthObj = setLengthProperty(cx, this Obj, length);
1100:
1101: /*
1102: * If JS1.2, follow Perl4 by returning the last thing pushed.
1103: * Otherwise, return the new array length.
1104: */
1105: if (cx.getLanguageVersion() == Context.VERSION_1_2)
1106: // if JS1.2 && no arguments, return undefined.
1107: return args.length == 0 ? Undefined.instance
1108: : args[args.length - 1];
1109:
1110: else
1111: return lengthObj;
1112: }
1113:
1114: private static Object js_pop(Context cx, Scriptable this Obj,
1115: Object[] args) {
1116: Object result;
1117: if (this Obj instanceof NativeArray) {
1118: NativeArray na = (NativeArray) this Obj;
1119: if (na.denseOnly && na.length > 0) {
1120: na.length--;
1121: result = na.dense[(int) na.length];
1122: na.dense[(int) na.length] = NOT_FOUND;
1123: return result;
1124: }
1125: }
1126: long length = getLengthProperty(cx, this Obj);
1127: if (length > 0) {
1128: length--;
1129:
1130: // Get the to-be-deleted property's value.
1131: result = getElem(cx, this Obj, length);
1132:
1133: // We don't need to delete the last property, because
1134: // setLength does that for us.
1135: } else {
1136: result = Undefined.instance;
1137: }
1138: // necessary to match js even when length < 0; js pop will give a
1139: // length property to any target it is called on.
1140: setLengthProperty(cx, this Obj, length);
1141:
1142: return result;
1143: }
1144:
1145: private static Object js_shift(Context cx, Scriptable this Obj,
1146: Object[] args) {
1147: if (this Obj instanceof NativeArray) {
1148: NativeArray na = (NativeArray) this Obj;
1149: if (na.denseOnly && na.length > 0) {
1150: na.length--;
1151: Object result = na.dense[0];
1152: System.arraycopy(na.dense, 1, na.dense, 0,
1153: (int) na.length);
1154: na.dense[(int) na.length] = NOT_FOUND;
1155: return result;
1156: }
1157: }
1158: Object result;
1159: long length = getLengthProperty(cx, this Obj);
1160: if (length > 0) {
1161: long i = 0;
1162: length--;
1163:
1164: // Get the to-be-deleted property's value.
1165: result = getElem(cx, this Obj, i);
1166:
1167: /*
1168: * Slide down the array above the first element. Leave i
1169: * set to point to the last element.
1170: */
1171: if (length > 0) {
1172: for (i = 1; i <= length; i++) {
1173: Object temp = getElem(cx, this Obj, i);
1174: setElem(cx, this Obj, i - 1, temp);
1175: }
1176: }
1177: // We don't need to delete the last property, because
1178: // setLength does that for us.
1179: } else {
1180: result = Undefined.instance;
1181: }
1182: setLengthProperty(cx, this Obj, length);
1183: return result;
1184: }
1185:
1186: private static Object js_unshift(Context cx, Scriptable this Obj,
1187: Object[] args) {
1188: if (this Obj instanceof NativeArray) {
1189: NativeArray na = (NativeArray) this Obj;
1190: if (na.denseOnly
1191: && na.ensureCapacity((int) na.length + args.length)) {
1192: System.arraycopy(na.dense, 0, na.dense, args.length,
1193: (int) na.length);
1194: for (int i = 0; i < args.length; i++) {
1195: na.dense[i] = args[i];
1196: }
1197: na.length += args.length;
1198: return ScriptRuntime.wrapNumber(na.length);
1199: }
1200: }
1201: long length = getLengthProperty(cx, this Obj);
1202: int argc = args.length;
1203:
1204: if (args.length > 0) {
1205: /* Slide up the array to make room for args at the bottom */
1206: if (length > 0) {
1207: for (long last = length - 1; last >= 0; last--) {
1208: Object temp = getElem(cx, this Obj, last);
1209: setElem(cx, this Obj, last + argc, temp);
1210: }
1211: }
1212:
1213: /* Copy from argv to the bottom of the array. */
1214: for (int i = 0; i < args.length; i++) {
1215: setElem(cx, this Obj, i, args[i]);
1216: }
1217:
1218: /* Follow Perl by returning the new array length. */
1219: length += args.length;
1220: return setLengthProperty(cx, this Obj, length);
1221: }
1222: return ScriptRuntime.wrapNumber(length);
1223: }
1224:
1225: private static Object js_splice(Context cx, Scriptable scope,
1226: Scriptable this Obj, Object[] args) {
1227: NativeArray na = null;
1228: boolean denseMode = false;
1229: if (this Obj instanceof NativeArray) {
1230: na = (NativeArray) this Obj;
1231: denseMode = na.denseOnly;
1232: }
1233:
1234: /* create an empty Array to return. */
1235: scope = getTopLevelScope(scope);
1236: int argc = args.length;
1237: if (argc == 0)
1238: return ScriptRuntime.newObject(cx, scope, "Array", null);
1239: long length = getLengthProperty(cx, this Obj);
1240:
1241: /* Convert the first argument into a starting index. */
1242: long begin = toSliceIndex(ScriptRuntime.toInteger(args[0]),
1243: length);
1244: argc--;
1245:
1246: /* Convert the second argument into count */
1247: long count;
1248: if (args.length == 1) {
1249: count = length - begin;
1250: } else {
1251: double dcount = ScriptRuntime.toInteger(args[1]);
1252: if (dcount < 0) {
1253: count = 0;
1254: } else if (dcount > (length - begin)) {
1255: count = length - begin;
1256: } else {
1257: count = (long) dcount;
1258: }
1259: argc--;
1260: }
1261:
1262: long end = begin + count;
1263:
1264: /* If there are elements to remove, put them into the return value. */
1265: Object result;
1266: if (count != 0) {
1267: if (count == 1
1268: && (cx.getLanguageVersion() == Context.VERSION_1_2)) {
1269: /*
1270: * JS lacks "list context", whereby in Perl one turns the
1271: * single scalar that's spliced out into an array just by
1272: * assigning it to @single instead of $single, or by using it
1273: * as Perl push's first argument, for instance.
1274: *
1275: * JS1.2 emulated Perl too closely and returned a non-Array for
1276: * the single-splice-out case, requiring callers to test and
1277: * wrap in [] if necessary. So JS1.3, default, and other
1278: * versions all return an array of length 1 for uniformity.
1279: */
1280: result = getElem(cx, this Obj, begin);
1281: } else {
1282: if (denseMode) {
1283: int intLen = (int) (end - begin);
1284: Object[] copy = new Object[intLen];
1285: System.arraycopy(na.dense, (int) begin, copy, 0,
1286: intLen);
1287: result = cx.newArray(scope, copy);
1288: } else {
1289: Scriptable resultArray = ScriptRuntime.newObject(
1290: cx, scope, "Array", null);
1291: for (long last = begin; last != end; last++) {
1292: Object temp = getElem(cx, this Obj, last);
1293: setElem(cx, resultArray, last - begin, temp);
1294: }
1295: result = resultArray;
1296: }
1297: }
1298: } else { // (count == 0)
1299: if (cx.getLanguageVersion() == Context.VERSION_1_2) {
1300: /* Emulate C JS1.2; if no elements are removed, return undefined. */
1301: result = Undefined.instance;
1302: } else {
1303: result = ScriptRuntime.newObject(cx, scope, "Array",
1304: null);
1305: }
1306: }
1307:
1308: /* Find the direction (up or down) to copy and make way for argv. */
1309: long delta = argc - count;
1310: if (denseMode && length + delta < Integer.MAX_VALUE
1311: && na.ensureCapacity((int) (length + delta))) {
1312: System.arraycopy(na.dense, (int) end, na.dense,
1313: (int) (begin + argc), (int) (length - end));
1314: if (argc > 0) {
1315: System.arraycopy(args, 2, na.dense, (int) begin, argc);
1316: }
1317: if (delta < 0) {
1318: Arrays.fill(na.dense, (int) (length + delta),
1319: (int) length, NOT_FOUND);
1320: }
1321: na.length = length + delta;
1322: return result;
1323: }
1324:
1325: if (delta > 0) {
1326: for (long last = length - 1; last >= end; last--) {
1327: Object temp = getElem(cx, this Obj, last);
1328: setElem(cx, this Obj, last + delta, temp);
1329: }
1330: } else if (delta < 0) {
1331: for (long last = end; last < length; last++) {
1332: Object temp = getElem(cx, this Obj, last);
1333: setElem(cx, this Obj, last + delta, temp);
1334: }
1335: }
1336:
1337: /* Copy from argv into the hole to complete the splice. */
1338: int argoffset = args.length - argc;
1339: for (int i = 0; i < argc; i++) {
1340: setElem(cx, this Obj, begin + i, args[i + argoffset]);
1341: }
1342:
1343: /* Update length in case we deleted elements from the end. */
1344: setLengthProperty(cx, this Obj, length + delta);
1345: return result;
1346: }
1347:
1348: /*
1349: * See Ecma 262v3 15.4.4.4
1350: */
1351: private static Scriptable js_concat(Context cx, Scriptable scope,
1352: Scriptable this Obj, Object[] args) {
1353: // create an empty Array to return.
1354: scope = getTopLevelScope(scope);
1355: Function ctor = ScriptRuntime.getExistingCtor(cx, scope,
1356: "Array");
1357: Scriptable result = ctor.construct(cx, scope,
1358: ScriptRuntime.emptyArgs);
1359: if (this Obj instanceof NativeArray
1360: && result instanceof NativeArray) {
1361: NativeArray denseThis = (NativeArray) this Obj;
1362: NativeArray denseResult = (NativeArray) result;
1363: if (denseThis.denseOnly && denseResult.denseOnly) {
1364: // First calculate length of resulting array
1365: boolean canUseDense = true;
1366: int length = (int) denseThis.length;
1367: for (int i = 0; i < args.length && canUseDense; i++) {
1368: if (ScriptRuntime.instanceOf(args[i], ctor, cx)) {
1369: // only try to use dense approach for Array-like
1370: // objects that are actually NativeArrays
1371: canUseDense = args[i] instanceof NativeArray;
1372: length += ((NativeArray) args[i]).length;
1373: } else {
1374: length++;
1375: }
1376: }
1377: if (canUseDense && denseResult.ensureCapacity(length)) {
1378: System.arraycopy(denseThis.dense, 0,
1379: denseResult.dense, 0,
1380: (int) denseThis.length);
1381: int cursor = (int) denseThis.length;
1382: for (int i = 0; i < args.length && canUseDense; i++) {
1383: if (args[i] instanceof NativeArray) {
1384: NativeArray arg = (NativeArray) args[i];
1385: System.arraycopy(arg.dense, 0,
1386: denseResult.dense, cursor,
1387: (int) arg.length);
1388: cursor += (int) arg.length;
1389: } else {
1390: denseResult.dense[cursor++] = args[i];
1391: }
1392: }
1393: denseResult.length = length;
1394: return result;
1395: }
1396: }
1397: }
1398:
1399: long length;
1400: long slot = 0;
1401:
1402: /* Put the target in the result array; only add it as an array
1403: * if it looks like one.
1404: */
1405: if (ScriptRuntime.instanceOf(this Obj, ctor, cx)) {
1406: length = getLengthProperty(cx, this Obj);
1407:
1408: // Copy from the target object into the result
1409: for (slot = 0; slot < length; slot++) {
1410: Object temp = getElem(cx, this Obj, slot);
1411: setElem(cx, result, slot, temp);
1412: }
1413: } else {
1414: setElem(cx, result, slot++, this Obj);
1415: }
1416:
1417: /* Copy from the arguments into the result. If any argument
1418: * has a numeric length property, treat it as an array and add
1419: * elements separately; otherwise, just copy the argument.
1420: */
1421: for (int i = 0; i < args.length; i++) {
1422: if (ScriptRuntime.instanceOf(args[i], ctor, cx)) {
1423: // ScriptRuntime.instanceOf => instanceof Scriptable
1424: Scriptable arg = (Scriptable) args[i];
1425: length = getLengthProperty(cx, arg);
1426: for (long j = 0; j < length; j++, slot++) {
1427: Object temp = getElem(cx, arg, j);
1428: setElem(cx, result, slot, temp);
1429: }
1430: } else {
1431: setElem(cx, result, slot++, args[i]);
1432: }
1433: }
1434: return result;
1435: }
1436:
1437: private Scriptable js_slice(Context cx, Scriptable this Obj,
1438: Object[] args) {
1439: Scriptable scope = getTopLevelScope(this );
1440: Scriptable result = ScriptRuntime.newObject(cx, scope, "Array",
1441: null);
1442: long length = getLengthProperty(cx, this Obj);
1443:
1444: long begin, end;
1445: if (args.length == 0) {
1446: begin = 0;
1447: end = length;
1448: } else {
1449: begin = toSliceIndex(ScriptRuntime.toInteger(args[0]),
1450: length);
1451: if (args.length == 1) {
1452: end = length;
1453: } else {
1454: end = toSliceIndex(ScriptRuntime.toInteger(args[1]),
1455: length);
1456: }
1457: }
1458:
1459: for (long slot = begin; slot < end; slot++) {
1460: Object temp = getElem(cx, this Obj, slot);
1461: setElem(cx, result, slot - begin, temp);
1462: }
1463:
1464: return result;
1465: }
1466:
1467: private static long toSliceIndex(double value, long length) {
1468: long result;
1469: if (value < 0.0) {
1470: if (value + length < 0.0) {
1471: result = 0;
1472: } else {
1473: result = (long) (value + length);
1474: }
1475: } else if (value > length) {
1476: result = length;
1477: } else {
1478: result = (long) value;
1479: }
1480: return result;
1481: }
1482:
1483: /**
1484: * Implements the methods "indexOf" and "lastIndexOf".
1485: */
1486: private Object indexOfHelper(Context cx, Scriptable this Obj,
1487: Object[] args, boolean isLast) {
1488: Object compareTo = args.length > 0 ? args[0]
1489: : Undefined.instance;
1490: long length = getLengthProperty(cx, this Obj);
1491: long start;
1492: if (isLast) {
1493: // lastIndexOf
1494: /*
1495: * From http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:lastIndexOf
1496: * The index at which to start searching backwards. Defaults to the
1497: * array's length, i.e. the whole array will be searched. If the
1498: * index is greater than or equal to the length of the array, the
1499: * whole array will be searched. If negative, it is taken as the
1500: * offset from the end of the array. Note that even when the index
1501: * is negative, the array is still searched from back to front. If
1502: * the calculated index is less than 0, -1 is returned, i.e. the
1503: * array will not be searched.
1504: */
1505: if (args.length < 2) {
1506: // default
1507: start = length - 1;
1508: } else {
1509: start = ScriptRuntime.toInt32(ScriptRuntime
1510: .toNumber(args[1]));
1511: if (start >= length)
1512: start = length - 1;
1513: else if (start < 0)
1514: start += length;
1515: // Note that start may be negative, but that's okay
1516: // as the result of -1 will fall out from the code below
1517: }
1518: } else {
1519: // indexOf
1520: /*
1521: * From http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:indexOf
1522: * The index at which to begin the search. Defaults to 0, i.e. the
1523: * whole array will be searched. If the index is greater than or
1524: * equal to the length of the array, -1 is returned, i.e. the array
1525: * will not be searched. If negative, it is taken as the offset from
1526: * the end of the array. Note that even when the index is negative,
1527: * the array is still searched from front to back. If the calculated
1528: * index is less than 0, the whole array will be searched.
1529: */
1530: if (args.length < 2) {
1531: // default
1532: start = 0;
1533: } else {
1534: start = ScriptRuntime.toInt32(ScriptRuntime
1535: .toNumber(args[1]));
1536: if (start < 0) {
1537: start += length;
1538: if (start < 0)
1539: start = 0;
1540: }
1541: // Note that start may be > length-1, but that's okay
1542: // as the result of -1 will fall out from the code below
1543: }
1544: }
1545: if (this Obj instanceof NativeArray) {
1546: NativeArray na = (NativeArray) this Obj;
1547: if (na.denseOnly) {
1548: if (isLast) {
1549: for (int i = (int) start; i >= 0; i--) {
1550: if (na.dense[i] != Scriptable.NOT_FOUND
1551: && ScriptRuntime.shallowEq(na.dense[i],
1552: compareTo)) {
1553: return new Long(i);
1554: }
1555: }
1556: } else {
1557: for (int i = (int) start; i < length; i++) {
1558: if (na.dense[i] != Scriptable.NOT_FOUND
1559: && ScriptRuntime.shallowEq(na.dense[i],
1560: compareTo)) {
1561: return new Long(i);
1562: }
1563: }
1564: }
1565: return NEGATIVE_ONE;
1566: }
1567: }
1568: if (isLast) {
1569: for (long i = start; i >= 0; i--) {
1570: if (ScriptRuntime.shallowEq(getElem(cx, this Obj, i),
1571: compareTo)) {
1572: return new Long(i);
1573: }
1574: }
1575: } else {
1576: for (long i = start; i < length; i++) {
1577: if (ScriptRuntime.shallowEq(getElem(cx, this Obj, i),
1578: compareTo)) {
1579: return new Long(i);
1580: }
1581: }
1582: }
1583: return NEGATIVE_ONE;
1584: }
1585:
1586: /**
1587: * Implements the methods "every", "filter", "forEach", "map", and "some".
1588: */
1589: private Object iterativeMethod(Context cx, int id,
1590: Scriptable scope, Scriptable this Obj, Object[] args) {
1591: Object callbackArg = args.length > 0 ? args[0]
1592: : Undefined.instance;
1593: if (callbackArg == null || !(callbackArg instanceof Function)) {
1594: throw ScriptRuntime.notFunctionError(ScriptRuntime
1595: .toString(callbackArg));
1596: }
1597: Function f = (Function) callbackArg;
1598: Scriptable parent = ScriptableObject.getTopLevelScope(f);
1599: Scriptable this Arg;
1600: if (args.length < 2 || args[1] == null
1601: || args[1] == Undefined.instance) {
1602: this Arg = parent;
1603: } else {
1604: this Arg = ScriptRuntime.toObject(cx, scope, args[1]);
1605: }
1606: long length = getLengthProperty(cx, this Obj);
1607: Scriptable array = ScriptRuntime.newObject(cx, scope, "Array",
1608: null);
1609: long j = 0;
1610: for (long i = 0; i < length; i++) {
1611: Object[] innerArgs = new Object[3];
1612: Object elem = (i > Integer.MAX_VALUE) ? ScriptableObject
1613: .getProperty(this Obj, Long.toString(i))
1614: : ScriptableObject.getProperty(this Obj, (int) i);
1615: if (elem == Scriptable.NOT_FOUND) {
1616: continue;
1617: }
1618: innerArgs[0] = elem;
1619: innerArgs[1] = new Long(i);
1620: innerArgs[2] = this Obj;
1621: Object result = f.call(cx, parent, this Arg, innerArgs);
1622: switch (id) {
1623: case Id_every:
1624: if (!ScriptRuntime.toBoolean(result))
1625: return Boolean.FALSE;
1626: break;
1627: case Id_filter:
1628: if (ScriptRuntime.toBoolean(result))
1629: setElem(cx, array, j++, innerArgs[0]);
1630: break;
1631: case Id_forEach:
1632: break;
1633: case Id_map:
1634: setElem(cx, array, i, result);
1635: break;
1636: case Id_some:
1637: if (ScriptRuntime.toBoolean(result))
1638: return Boolean.TRUE;
1639: break;
1640: }
1641: }
1642: switch (id) {
1643: case Id_every:
1644: return Boolean.TRUE;
1645: case Id_filter:
1646: case Id_map:
1647: return array;
1648: case Id_some:
1649: return Boolean.FALSE;
1650: case Id_forEach:
1651: default:
1652: return Undefined.instance;
1653: }
1654: }
1655:
1656: // #string_id_map#
1657:
1658: protected int findPrototypeId(String s) {
1659: int id;
1660: // #generated# Last update: 2005-09-26 15:47:42 EDT
1661: L0: {
1662: id = 0;
1663: String X = null;
1664: int c;
1665: L: switch (s.length()) {
1666: case 3:
1667: c = s.charAt(0);
1668: if (c == 'm') {
1669: if (s.charAt(2) == 'p' && s.charAt(1) == 'a') {
1670: id = Id_map;
1671: break L0;
1672: }
1673: } else if (c == 'p') {
1674: if (s.charAt(2) == 'p' && s.charAt(1) == 'o') {
1675: id = Id_pop;
1676: break L0;
1677: }
1678: }
1679: break L;
1680: case 4:
1681: switch (s.charAt(2)) {
1682: case 'i':
1683: X = "join";
1684: id = Id_join;
1685: break L;
1686: case 'm':
1687: X = "some";
1688: id = Id_some;
1689: break L;
1690: case 'r':
1691: X = "sort";
1692: id = Id_sort;
1693: break L;
1694: case 's':
1695: X = "push";
1696: id = Id_push;
1697: break L;
1698: }
1699: break L;
1700: case 5:
1701: c = s.charAt(1);
1702: if (c == 'h') {
1703: X = "shift";
1704: id = Id_shift;
1705: } else if (c == 'l') {
1706: X = "slice";
1707: id = Id_slice;
1708: } else if (c == 'v') {
1709: X = "every";
1710: id = Id_every;
1711: }
1712: break L;
1713: case 6:
1714: c = s.charAt(0);
1715: if (c == 'c') {
1716: X = "concat";
1717: id = Id_concat;
1718: } else if (c == 'f') {
1719: X = "filter";
1720: id = Id_filter;
1721: } else if (c == 's') {
1722: X = "splice";
1723: id = Id_splice;
1724: }
1725: break L;
1726: case 7:
1727: switch (s.charAt(0)) {
1728: case 'f':
1729: X = "forEach";
1730: id = Id_forEach;
1731: break L;
1732: case 'i':
1733: X = "indexOf";
1734: id = Id_indexOf;
1735: break L;
1736: case 'r':
1737: X = "reverse";
1738: id = Id_reverse;
1739: break L;
1740: case 'u':
1741: X = "unshift";
1742: id = Id_unshift;
1743: break L;
1744: }
1745: break L;
1746: case 8:
1747: c = s.charAt(3);
1748: if (c == 'o') {
1749: X = "toSource";
1750: id = Id_toSource;
1751: } else if (c == 't') {
1752: X = "toString";
1753: id = Id_toString;
1754: }
1755: break L;
1756: case 11:
1757: c = s.charAt(0);
1758: if (c == 'c') {
1759: X = "constructor";
1760: id = Id_constructor;
1761: } else if (c == 'l') {
1762: X = "lastIndexOf";
1763: id = Id_lastIndexOf;
1764: }
1765: break L;
1766: case 14:
1767: X = "toLocaleString";
1768: id = Id_toLocaleString;
1769: break L;
1770: }
1771: if (X != null && X != s && !X.equals(s))
1772: id = 0;
1773: }
1774: // #/generated#
1775: return id;
1776: }
1777:
1778: private static final int Id_constructor = 1, Id_toString = 2,
1779: Id_toLocaleString = 3, Id_toSource = 4, Id_join = 5,
1780: Id_reverse = 6, Id_sort = 7, Id_push = 8, Id_pop = 9,
1781: Id_shift = 10, Id_unshift = 11, Id_splice = 12,
1782: Id_concat = 13, Id_slice = 14, Id_indexOf = 15,
1783: Id_lastIndexOf = 16, Id_every = 17, Id_filter = 18,
1784: Id_forEach = 19, Id_map = 20, Id_some = 21,
1785:
1786: MAX_PROTOTYPE_ID = 21;
1787:
1788: // #/string_id_map#
1789:
1790: private static final int ConstructorId_join = -Id_join,
1791: ConstructorId_reverse = -Id_reverse,
1792: ConstructorId_sort = -Id_sort,
1793: ConstructorId_push = -Id_push, ConstructorId_pop = -Id_pop,
1794: ConstructorId_shift = -Id_shift,
1795: ConstructorId_unshift = -Id_unshift,
1796: ConstructorId_splice = -Id_splice,
1797: ConstructorId_concat = -Id_concat,
1798: ConstructorId_slice = -Id_slice,
1799: ConstructorId_indexOf = -Id_indexOf,
1800: ConstructorId_lastIndexOf = -Id_lastIndexOf,
1801: ConstructorId_every = -Id_every,
1802: ConstructorId_filter = -Id_filter,
1803: ConstructorId_forEach = -Id_forEach,
1804: ConstructorId_map = -Id_map, ConstructorId_some = -Id_some;
1805:
1806: /**
1807: * Internal representation of the JavaScript array's length property.
1808: */
1809: private long length;
1810:
1811: /**
1812: * Fast storage for dense arrays. Sparse arrays will use the superclass's
1813: * hashtable storage scheme.
1814: */
1815: private Object[] dense;
1816:
1817: /**
1818: * True if all numeric properties are stored in <code>dense</code>.
1819: */
1820: private boolean denseOnly;
1821:
1822: /**
1823: * The maximum size of <code>dense</code> that will be allocated initially.
1824: */
1825: private static int maximumInitialCapacity = 10000;
1826:
1827: /**
1828: * The default capacity for <code>dense</code>.
1829: */
1830: private static final int DEFAULT_INITIAL_CAPACITY = 10;
1831:
1832: /**
1833: * The factor to grow <code>dense</code> by.
1834: */
1835: private static final double GROW_FACTOR = 1.5;
1836: private static final int MAX_PRE_GROW_SIZE = (int) (Integer.MAX_VALUE / GROW_FACTOR);
1837: }
|