1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
\r
3 * The contents of this file are subject to the Netscape Public
\r
4 * License Version 1.1 (the "License"); you may not use this file
\r
5 * except in compliance with the License. You may obtain a copy of
\r
6 * the License at http://www.mozilla.org/NPL/
\r
8 * Software distributed under the License is distributed on an "AS
\r
9 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
\r
10 * implied. See the License for the specific language governing
\r
11 * rights and limitations under the License.
\r
13 * The Original Code is Rhino code, released
\r
16 * The Initial Developer of the Original Code is Netscape
\r
17 * Communications Corporation. Portions created by Netscape are
\r
18 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
\r
26 * Alternatively, the contents of this file may be used under the
\r
27 * terms of the GNU Public License (the "GPL"), in which case the
\r
28 * provisions of the GPL are applicable instead of those above.
\r
29 * If you wish to allow use of your version of this file only
\r
30 * under the terms of the GPL and not to allow others to use your
\r
31 * version of this file under the NPL, indicate your decision by
\r
32 * deleting the provisions above and replace them with the notice
\r
33 * and other provisions required by the GPL. If you do not delete
\r
34 * the provisions above, a recipient may use your version of this
\r
35 * file under either the NPL or the GPL.
\r
38 package org.mozilla.javascript;
\r
39 import java.util.Hashtable;
\r
42 * This class implements the Array native object.
\r
43 * @author Norris Boyd
\r
44 * @author Mike McCabe
\r
46 public class NativeArray extends IdScriptable {
\r
49 * Optimization possibilities and open issues:
\r
50 * - Long vs. double schizophrenia. I suspect it might be better
\r
51 * to use double throughout.
\r
53 * - Most array operations go through getElem or setElem (defined
\r
54 * in this file) to handle the full 2^32 range; it might be faster
\r
55 * to have versions of most of the loops in this file for the
\r
56 * (infinitely more common) case of indices < 2^31.
\r
58 * - Functions that need a new Array call "new Array" in the
\r
59 * current scope rather than using a hardwired constructor;
\r
60 * "Array" could be redefined. It turns out that js calls the
\r
61 * equivalent of "new Array" in the current scope, except that it
\r
62 * always gets at least an object back, even when Array == null.
\r
65 public static void init(Context cx, Scriptable scope, boolean sealed) {
\r
66 NativeArray obj = new NativeArray();
\r
67 obj.prototypeFlag = true;
\r
68 obj.addAsPrototype(MAX_PROTOTYPE_ID, cx, scope, sealed);
\r
72 * Zero-parameter constructor: just used to create Array.prototype
\r
74 public NativeArray() {
\r
79 public NativeArray(long length) {
\r
80 int intLength = (int) length;
\r
81 if (intLength == length && intLength > 0) {
\r
82 if (intLength > maximumDenseLength)
\r
83 intLength = maximumDenseLength;
\r
84 dense = new Object[intLength];
\r
85 for (int i=0; i < intLength; i++)
\r
86 dense[i] = NOT_FOUND;
\r
88 this.length = length;
\r
91 public NativeArray(Object[] array) {
\r
93 this.length = array.length;
\r
96 public String getClassName() {
\r
100 protected int getIdDefaultAttributes(int id) {
\r
101 if (id == Id_length) {
\r
102 return DONTENUM | PERMANENT;
\r
104 return super.getIdDefaultAttributes(id);
\r
107 protected Object getIdValue(int id) {
\r
108 if (id == Id_length) {
\r
109 return wrap_double(length);
\r
111 return super.getIdValue(id);
\r
114 protected void setIdValue(int id, Object value) {
\r
115 if (id == Id_length) {
\r
116 jsSet_length(value); return;
\r
118 super.setIdValue(id, value);
\r
121 public int methodArity(int methodId) {
\r
122 if (prototypeFlag) {
\r
123 switch (methodId) {
\r
124 case Id_constructor: return 1;
\r
125 case Id_toString: return 0;
\r
126 case Id_toLocaleString: return 1;
\r
127 case Id_join: return 1;
\r
128 case Id_reverse: return 0;
\r
129 case Id_sort: return 1;
\r
130 case Id_push: return 1;
\r
131 case Id_pop: return 1;
\r
132 case Id_shift: return 1;
\r
133 case Id_unshift: return 1;
\r
134 case Id_splice: return 1;
\r
135 case Id_concat: return 1;
\r
136 case Id_slice: return 1;
\r
139 return super.methodArity(methodId);
\r
142 public Object execMethod
\r
143 (int methodId, IdFunction f,
\r
144 Context cx, Scriptable scope, Scriptable thisObj, Object[] args)
\r
145 throws JavaScriptException
\r
147 if (prototypeFlag) {
\r
148 switch (methodId) {
\r
149 case Id_constructor:
\r
150 return jsConstructor(cx, scope, args, f, thisObj == null);
\r
153 return jsFunction_toString(cx, thisObj, args);
\r
155 case Id_toLocaleString:
\r
156 return jsFunction_toLocaleString(cx, thisObj, args);
\r
159 return jsFunction_join(cx, thisObj, args);
\r
162 return jsFunction_reverse(cx, thisObj, args);
\r
165 return jsFunction_sort(cx, scope, thisObj, args);
\r
168 return jsFunction_push(cx, thisObj, args);
\r
171 return jsFunction_pop(cx, thisObj, args);
\r
174 return jsFunction_shift(cx, thisObj, args);
\r
177 return jsFunction_unshift(cx, thisObj, args);
\r
180 return jsFunction_splice(cx, scope, thisObj, args);
\r
183 return jsFunction_concat(cx, scope, thisObj, args);
\r
186 return jsFunction_slice(cx, thisObj, args);
\r
189 return super.execMethod(methodId, f, cx, scope, thisObj, args);
\r
192 public Object get(int index, Scriptable start) {
\r
193 if (dense != null && 0 <= index && index < dense.length)
\r
194 return dense[index];
\r
195 return super.get(index, start);
\r
198 public boolean has(int index, Scriptable start) {
\r
199 if (dense != null && 0 <= index && index < dense.length)
\r
200 return dense[index] != NOT_FOUND;
\r
201 return super.has(index, start);
\r
204 public void put(String id, Scriptable start, Object value) {
\r
205 if (start == this) {
\r
206 // only set the array length if given an array index (ECMA 15.4.0)
\r
208 // try to get an array index from id
\r
209 double d = ScriptRuntime.toNumber(id);
\r
211 if (ScriptRuntime.toUint32(d) == d &&
\r
212 ScriptRuntime.numberToString(d, 10).equals(id) &&
\r
213 this.length <= d && d != 4294967295.0)
\r
215 this.length = (long)d + 1;
\r
218 super.put(id, start, value);
\r
221 public void put(int index, Scriptable start, Object value) {
\r
222 if (start == this) {
\r
223 // only set the array length if given an array index (ECMA 15.4.0)
\r
224 if (this.length <= index) {
\r
225 // avoid overflowing index!
\r
226 this.length = (long)index + 1;
\r
229 if (dense != null && 0 <= index && index < dense.length) {
\r
230 dense[index] = value;
\r
234 super.put(index, start, value);
\r
237 public void delete(int index) {
\r
239 if (dense != null && 0 <= index && index < dense.length) {
\r
240 dense[index] = NOT_FOUND;
\r
244 super.delete(index);
\r
247 public Object[] getIds() {
\r
248 Object[] superIds = super.getIds();
\r
252 int last = dense.length;
\r
254 last = (int) length;
\r
255 for (int i=last-1; i >= 0; i--) {
\r
256 if (dense[i] != NOT_FOUND)
\r
259 count += superIds.length;
\r
260 Object[] result = new Object[count];
\r
261 System.arraycopy(superIds, 0, result, 0, superIds.length);
\r
262 for (int i=last-1; i >= 0; i--) {
\r
263 if (dense[i] != NOT_FOUND)
\r
264 result[--count] = new Integer(i);
\r
269 public Object getDefaultValue(Class hint) {
\r
270 if (hint == ScriptRuntime.NumberClass) {
\r
271 Context cx = Context.getContext();
\r
272 if (cx.getLanguageVersion() == Context.VERSION_1_2)
\r
273 return new Long(length);
\r
275 return super.getDefaultValue(hint);
\r
279 * See ECMA 15.4.1,2
\r
281 private static Object jsConstructor(Context cx, Scriptable scope,
\r
282 Object[] args, IdFunction ctorObj,
\r
284 throws JavaScriptException
\r
287 // FunctionObject.construct will set up parent, proto
\r
288 return ctorObj.construct(cx, scope, args);
\r
290 if (args.length == 0)
\r
291 return new NativeArray();
\r
293 // Only use 1 arg as first element for version 1.2; for
\r
294 // any other version (including 1.3) follow ECMA and use it as
\r
296 if (cx.getLanguageVersion() == cx.VERSION_1_2) {
\r
297 return new NativeArray(args);
\r
300 if ((args.length > 1) || (!(args[0] instanceof Number)))
\r
301 return new NativeArray(args);
\r
303 long len = ScriptRuntime.toUint32(args[0]);
\r
304 if (len != (((Number)(args[0])).doubleValue()))
\r
305 throw Context.reportRuntimeError0("msg.arraylength.bad");
\r
306 return new NativeArray(len);
\r
311 public long jsGet_length() {
\r
315 private void jsSet_length(Object val) {
\r
316 /* XXX do we satisfy this?
\r
317 * 15.4.5.1 [[Put]](P, V):
\r
318 * 1. Call the [[CanPut]] method of A with name P.
\r
319 * 2. If Result(1) is false, return.
\r
323 if (!(val instanceof Number))
\r
324 throw Context.reportRuntimeError0("msg.arraylength.bad");
\r
326 long longVal = ScriptRuntime.toUint32(val);
\r
327 if (longVal != (((Number)val).doubleValue()))
\r
328 throw Context.reportRuntimeError0("msg.arraylength.bad");
\r
330 if (longVal < length) {
\r
331 // remove all properties between longVal and length
\r
332 if (length - longVal > 0x1000) {
\r
333 // assume that the representation is sparse
\r
334 Object[] e = getIds(); // will only find in object itself
\r
335 for (int i=0; i < e.length; i++) {
\r
336 if (e[i] instanceof String) {
\r
337 // > MAXINT will appear as string
\r
338 String id = (String) e[i];
\r
339 double d = ScriptRuntime.toNumber(id);
\r
340 if (d == d && d < length)
\r
344 int index = ((Number) e[i]).intValue();
\r
345 if (index >= longVal)
\r
349 // assume a dense representation
\r
350 for (long i=longVal; i < length; i++) {
\r
351 // only delete if defined in the object itself
\r
352 if (hasElem(this, i))
\r
353 ScriptRuntime.delete(this, new Long(i));
\r
360 /* Support for generic Array-ish objects. Most of the Array
\r
361 * functions try to be generic; anything that has a length
\r
362 * property is assumed to be an array. hasLengthProperty is
\r
363 * needed in addition to getLengthProperty, because
\r
364 * getLengthProperty always succeeds - tries to convert strings, etc.
\r
366 static double getLengthProperty(Scriptable obj) {
\r
367 // These will both give numeric lengths within Uint32 range.
\r
368 if (obj instanceof NativeString)
\r
369 return (double)((NativeString)obj).jsGet_length();
\r
370 if (obj instanceof NativeArray)
\r
371 return (double)((NativeArray)obj).jsGet_length();
\r
372 return ScriptRuntime.toUint32(ScriptRuntime
\r
373 .getProp(obj, "length", obj));
\r
376 static boolean hasLengthProperty(Object obj) {
\r
377 if (!(obj instanceof Scriptable) || obj == Context.getUndefinedValue())
\r
379 if (obj instanceof NativeString || obj instanceof NativeArray)
\r
381 Scriptable sobj = (Scriptable)obj;
\r
383 // XXX some confusion as to whether or not to walk to get the length
\r
384 // property. Pending review of js/[new ecma submission] treatment
\r
387 Object property = ScriptRuntime.getProp(sobj, "length", sobj);
\r
388 return property instanceof Number;
\r
391 /* Utility functions to encapsulate index > Integer.MAX_VALUE
\r
392 * handling. Also avoids unnecessary object creation that would
\r
393 * be necessary to use the general ScriptRuntime.get/setElem
\r
394 * functions... though this is probably premature optimization.
\r
396 private static boolean hasElem(Scriptable target, long index) {
\r
397 return index > Integer.MAX_VALUE
\r
398 ? target.has(Long.toString(index), target)
\r
399 : target.has((int)index, target);
\r
402 private static Object getElem(Scriptable target, long index) {
\r
403 if (index > Integer.MAX_VALUE) {
\r
404 String id = Long.toString(index);
\r
405 return ScriptRuntime.getElem(target, id, target);
\r
407 return ScriptRuntime.getElem(target, (int)index);
\r
411 private static void setElem(Scriptable target, long index, Object value) {
\r
412 if (index > Integer.MAX_VALUE) {
\r
413 String id = Long.toString(index);
\r
414 ScriptRuntime.setElem(target, id, value, target);
\r
416 ScriptRuntime.setElem(target, (int)index, value);
\r
420 private static String jsFunction_toString(Context cx, Scriptable thisObj,
\r
422 throws JavaScriptException
\r
424 return toStringHelper(cx, thisObj,
\r
425 cx.getLanguageVersion() == cx.VERSION_1_2,
\r
429 private static String jsFunction_toLocaleString(Context cx,
\r
430 Scriptable thisObj,
\r
432 throws JavaScriptException
\r
434 return toStringHelper(cx, thisObj, false, true);
\r
437 private static String toStringHelper(Context cx, Scriptable thisObj,
\r
438 boolean toSource, boolean toLocale)
\r
439 throws JavaScriptException
\r
441 /* It's probably redundant to handle long lengths in this
\r
442 * function; StringBuffers are limited to 2^31 in java.
\r
445 long length = (long)getLengthProperty(thisObj);
\r
447 StringBuffer result = new StringBuffer();
\r
449 if (cx.iterating == null)
\r
450 cx.iterating = new Hashtable(31);
\r
451 boolean iterating = cx.iterating.get(thisObj) == Boolean.TRUE;
\r
453 // whether to return '4,unquoted,5' or '[4, "quoted", 5]'
\r
457 result.append('[');
\r
463 boolean haslast = false;
\r
467 for (i = 0; i < length; i++) {
\r
469 result.append(separator);
\r
470 Object elem = getElem(thisObj, i);
\r
471 if (elem == null || elem == Undefined.instance) {
\r
477 if (elem instanceof String) {
\r
479 result.append('\"');
\r
480 result.append(ScriptRuntime.escapeString
\r
481 (ScriptRuntime.toString(elem)));
\r
482 result.append('\"');
\r
484 result.append(ScriptRuntime.toString(elem));
\r
487 /* wrap changes to cx.iterating in a try/finally
\r
488 * so that the reference always gets removed, and
\r
489 * we don't leak memory. Good place for weak
\r
490 * references, if we had them. */
\r
493 cx.iterating.put(thisObj, Boolean.TRUE);
\r
494 if (toLocale && elem != Undefined.instance &&
\r
497 Scriptable obj = cx.toObject(elem, thisObj);
\r
498 Object tls = ScriptRuntime.getProp(obj,
\r
499 "toLocaleString", thisObj);
\r
500 elem = ScriptRuntime.call(cx, tls, elem,
\r
501 ScriptRuntime.emptyArgs);
\r
503 result.append(ScriptRuntime.toString(elem));
\r
505 cx.iterating.remove(thisObj);
\r
512 //for [,,].length behavior; we want toString to be symmetric.
\r
513 if (!haslast && i > 0)
\r
514 result.append(", ]");
\r
516 result.append(']');
\r
518 return result.toString();
\r
522 * See ECMA 15.4.4.3
\r
524 private static String jsFunction_join(Context cx, Scriptable thisObj,
\r
527 StringBuffer result = new StringBuffer();
\r
530 double length = getLengthProperty(thisObj);
\r
532 // if no args, use "," as separator
\r
533 if (args.length < 1) {
\r
536 separator = ScriptRuntime.toString(args[0]);
\r
538 for (long i=0; i < length; i++) {
\r
540 result.append(separator);
\r
541 Object temp = getElem(thisObj, i);
\r
542 if (temp == null || temp == Undefined.instance)
\r
544 result.append(ScriptRuntime.toString(temp));
\r
546 return result.toString();
\r
550 * See ECMA 15.4.4.4
\r
552 private static Scriptable jsFunction_reverse(Context cx,
\r
553 Scriptable thisObj,
\r
556 long len = (long)getLengthProperty(thisObj);
\r
558 long half = len / 2;
\r
559 for(long i=0; i < half; i++) {
\r
560 long j = len - i - 1;
\r
561 Object temp1 = getElem(thisObj, i);
\r
562 Object temp2 = getElem(thisObj, j);
\r
563 setElem(thisObj, i, temp2);
\r
564 setElem(thisObj, j, temp1);
\r
570 * See ECMA 15.4.4.5
\r
572 private static Scriptable jsFunction_sort(Context cx, Scriptable scope,
\r
573 Scriptable thisObj,
\r
575 throws JavaScriptException
\r
577 long length = (long)getLengthProperty(thisObj);
\r
580 if (args.length > 0 && Undefined.instance != args[0])
\r
581 // sort with given compare function
\r
584 // sort with default compare
\r
588 // OPT: Would it make sense to use the extended sort for very small
\r
591 // Should we use the extended sort function, or the faster one?
\r
592 if (length >= Integer.MAX_VALUE) {
\r
593 qsort_extended(cx, compare, thisObj, 0, length - 1);
\r
595 // copy the JS array into a working array, so it can be
\r
597 Object[] working = new Object[(int)length];
\r
598 for (int i=0; i<length; i++) {
\r
599 working[i] = getElem(thisObj, i);
\r
602 qsort(cx, compare, working, 0, (int)length - 1, scope);
\r
604 // copy the working array back into thisObj
\r
605 for (int i=0; i<length; i++) {
\r
606 setElem(thisObj, i, working[i]);
\r
612 private static double qsortCompare(Context cx, Object jsCompare, Object x,
\r
613 Object y, Scriptable scope)
\r
614 throws JavaScriptException
\r
616 Object undef = Undefined.instance;
\r
618 // sort undefined to end
\r
619 if (undef == x || undef == y) {
\r
627 if (jsCompare == null) {
\r
628 // if no compare function supplied, sort lexicographically
\r
629 String a = ScriptRuntime.toString(x);
\r
630 String b = ScriptRuntime.toString(y);
\r
632 return a.compareTo(b);
\r
634 // assemble args and call supplied JS compare function
\r
635 // OPT: put this argument creation in the caller and reuse it.
\r
636 // XXX what to do when compare function returns NaN? ECMA states
\r
637 // that it's then not a 'consistent compararison function'... but
\r
638 // then what do we do? Back out and start over with the generic
\r
639 // compare function when we see a NaN? Throw an error?
\r
640 Object[] args = {x, y};
\r
641 // return ScriptRuntime.toNumber(ScriptRuntime.call(jsCompare, null,
\r
643 // for now, just ignore it:
\r
644 double d = ScriptRuntime.
\r
645 toNumber(ScriptRuntime.call(cx, jsCompare, null, args, scope));
\r
647 return (d == d) ? d : 0;
\r
651 private static void qsort(Context cx, Object jsCompare, Object[] working,
\r
652 int lo, int hi, Scriptable scope)
\r
653 throws JavaScriptException
\r
663 pivot = working[a];
\r
667 if (qsortCompare(cx, jsCompare, working[j], pivot,
\r
672 working[a] = working[b];
\r
673 while (i < j && qsortCompare(cx, jsCompare, working[a],
\r
674 pivot, scope) <= 0)
\r
679 working[b] = working[a];
\r
681 working[a] = pivot;
\r
682 if (i - lo < hi - i) {
\r
683 qsort(cx, jsCompare, working, lo, i - 1, scope);
\r
686 qsort(cx, jsCompare, working, i + 1, hi, scope);
\r
692 // A version that knows about long indices and doesn't use
\r
693 // a working array. Probably will never be used.
\r
694 private static void qsort_extended(Context cx, Object jsCompare,
\r
695 Scriptable target, long lo, long hi)
\r
696 throws JavaScriptException
\r
706 pivot = getElem(target, a);
\r
710 if (qsortCompare(cx, jsCompare, getElem(target, j),
\r
711 pivot, target) <= 0)
\r
715 setElem(target, a, getElem(target, b));
\r
716 while (i < j && qsortCompare(cx, jsCompare,
\r
717 getElem(target, a),
\r
718 pivot, target) <= 0)
\r
723 setElem(target, b, getElem(target, a));
\r
725 setElem(target, a, pivot);
\r
726 if (i - lo < hi - i) {
\r
727 qsort_extended(cx, jsCompare, target, lo, i - 1);
\r
730 qsort_extended(cx, jsCompare, target, i + 1, hi);
\r
737 * Non-ECMA methods.
\r
740 private static Object jsFunction_push(Context cx, Scriptable thisObj,
\r
743 double length = getLengthProperty(thisObj);
\r
744 for (int i = 0; i < args.length; i++) {
\r
745 setElem(thisObj, (long)length + i, args[i]);
\r
748 length += args.length;
\r
749 ScriptRuntime.setProp(thisObj, "length", new Double(length), thisObj);
\r
752 * If JS1.2, follow Perl4 by returning the last thing pushed.
\r
753 * Otherwise, return the new array length.
\r
755 if (cx.getLanguageVersion() == Context.VERSION_1_2)
\r
756 // if JS1.2 && no arguments, return undefined.
\r
757 return args.length == 0
\r
758 ? Context.getUndefinedValue()
\r
759 : args[args.length - 1];
\r
762 return new Long((long)length);
\r
765 private static Object jsFunction_pop(Context cx, Scriptable thisObj,
\r
768 double length = getLengthProperty(thisObj);
\r
772 // Get the to-be-deleted property's value.
\r
773 result = getElem(thisObj, (long)length);
\r
775 // We don't need to delete the last property, because
\r
776 // setLength does that for us.
\r
778 result = Context.getUndefinedValue();
\r
780 // necessary to match js even when length < 0; js pop will give a
\r
781 // length property to any target it is called on.
\r
782 ScriptRuntime.setProp(thisObj, "length", new Double(length), thisObj);
\r
787 private static Object jsFunction_shift(Context cx, Scriptable thisObj,
\r
791 double length = getLengthProperty(thisObj);
\r
796 // Get the to-be-deleted property's value.
\r
797 result = getElem(thisObj, i);
\r
800 * Slide down the array above the first element. Leave i
\r
801 * set to point to the last element.
\r
804 for (i = 1; i <= length; i++) {
\r
805 Object temp = getElem(thisObj, i);
\r
806 setElem(thisObj, i - 1, temp);
\r
809 // We don't need to delete the last property, because
\r
810 // setLength does that for us.
\r
812 result = Context.getUndefinedValue();
\r
814 ScriptRuntime.setProp(thisObj, "length", new Double(length), thisObj);
\r
818 private static Object jsFunction_unshift(Context cx, Scriptable thisObj,
\r
822 double length = (double)getLengthProperty(thisObj);
\r
823 int argc = args.length;
\r
825 if (args.length > 0) {
\r
826 /* Slide up the array to make room for args at the bottom */
\r
828 for (long last = (long)length - 1; last >= 0; last--) {
\r
829 Object temp = getElem(thisObj, last);
\r
830 setElem(thisObj, last + argc, temp);
\r
834 /* Copy from argv to the bottom of the array. */
\r
835 for (int i = 0; i < args.length; i++) {
\r
836 setElem(thisObj, i, args[i]);
\r
839 /* Follow Perl by returning the new array length. */
\r
840 length += args.length;
\r
841 ScriptRuntime.setProp(thisObj, "length",
\r
842 new Double(length), thisObj);
\r
844 return new Long((long)length);
\r
847 private static Object jsFunction_splice(Context cx, Scriptable scope,
\r
848 Scriptable thisObj, Object[] args)
\r
850 /* create an empty Array to return. */
\r
851 scope = getTopLevelScope(scope);
\r
852 Object result = ScriptRuntime.newObject(cx, scope, "Array", null);
\r
853 int argc = args.length;
\r
856 double length = getLengthProperty(thisObj);
\r
858 /* Convert the first argument into a starting index. */
\r
859 double begin = ScriptRuntime.toInteger(args[0]);
\r
868 } else if (begin > length) {
\r
873 /* Convert the second argument from a count into a fencepost index. */
\r
874 delta = length - begin;
\r
876 if (args.length == 1) {
\r
880 count = ScriptRuntime.toInteger(args[1]);
\r
883 else if (count > delta)
\r
885 end = begin + count;
\r
890 long lbegin = (long)begin;
\r
891 long lend = (long)end;
\r
893 /* If there are elements to remove, put them into the return value. */
\r
896 && (cx.getLanguageVersion() == Context.VERSION_1_2))
\r
899 * JS lacks "list context", whereby in Perl one turns the
\r
900 * single scalar that's spliced out into an array just by
\r
901 * assigning it to @single instead of $single, or by using it
\r
902 * as Perl push's first argument, for instance.
\r
904 * JS1.2 emulated Perl too closely and returned a non-Array for
\r
905 * the single-splice-out case, requiring callers to test and
\r
906 * wrap in [] if necessary. So JS1.3, default, and other
\r
907 * versions all return an array of length 1 for uniformity.
\r
909 result = getElem(thisObj, lbegin);
\r
911 for (long last = lbegin; last < lend; last++) {
\r
912 Scriptable resultArray = (Scriptable)result;
\r
913 Object temp = getElem(thisObj, last);
\r
914 setElem(resultArray, last - lbegin, temp);
\r
917 } else if (count == 0
\r
918 && cx.getLanguageVersion() == Context.VERSION_1_2)
\r
920 /* Emulate C JS1.2; if no elements are removed, return undefined. */
\r
921 result = Context.getUndefinedValue();
\r
924 /* Find the direction (up or down) to copy and make way for argv. */
\r
925 delta = argc - count;
\r
928 for (long last = (long)length - 1; last >= lend; last--) {
\r
929 Object temp = getElem(thisObj, last);
\r
930 setElem(thisObj, last + (long)delta, temp);
\r
932 } else if (delta < 0) {
\r
933 for (long last = lend; last < length; last++) {
\r
934 Object temp = getElem(thisObj, last);
\r
935 setElem(thisObj, last + (long)delta, temp);
\r
939 /* Copy from argv into the hole to complete the splice. */
\r
940 int argoffset = args.length - argc;
\r
941 for (int i = 0; i < argc; i++) {
\r
942 setElem(thisObj, lbegin + i, args[i + argoffset]);
\r
945 /* Update length in case we deleted elements from the end. */
\r
946 ScriptRuntime.setProp(thisObj, "length",
\r
947 new Double(length + delta), thisObj);
\r
952 * Python-esque sequence operations.
\r
954 private static Scriptable jsFunction_concat(Context cx, Scriptable scope,
\r
955 Scriptable thisObj,
\r
958 /* Concat tries to keep the definition of an array as general
\r
959 * as possible; if it finds that an object has a numeric
\r
960 * 'length' property, then it treats that object as an array.
\r
961 * This treats string atoms and string objects differently; as
\r
962 * string objects have a length property and are accessible by
\r
963 * index, they get exploded into arrays when added, while
\r
964 * atomic strings are just added as strings.
\r
967 // create an empty Array to return.
\r
968 scope = getTopLevelScope(scope);
\r
969 Scriptable result = ScriptRuntime.newObject(cx, scope, "Array", null);
\r
973 /* Put the target in the result array; only add it as an array
\r
974 * if it looks like one.
\r
976 if (hasLengthProperty(thisObj)) {
\r
977 length = getLengthProperty(thisObj);
\r
979 // Copy from the target object into the result
\r
980 for (slot = 0; slot < length; slot++) {
\r
981 Object temp = getElem(thisObj, slot);
\r
982 setElem(result, slot, temp);
\r
985 setElem(result, slot++, thisObj);
\r
988 /* Copy from the arguments into the result. If any argument
\r
989 * has a numeric length property, treat it as an array and add
\r
990 * elements separately; otherwise, just copy the argument.
\r
992 for (int i = 0; i < args.length; i++) {
\r
993 if (hasLengthProperty(args[i])) {
\r
994 // hasLengthProperty => instanceOf Scriptable.
\r
995 Scriptable arg = (Scriptable)args[i];
\r
996 length = getLengthProperty(arg);
\r
997 for (long j = 0; j < length; j++, slot++) {
\r
998 Object temp = getElem(arg, j);
\r
999 setElem(result, slot, temp);
\r
1002 setElem(result, slot++, args[i]);
\r
1008 private Scriptable jsFunction_slice(Context cx, Scriptable thisObj,
\r
1011 Scriptable scope = getTopLevelScope(this);
\r
1012 Scriptable result = ScriptRuntime.newObject(cx, scope, "Array", null);
\r
1013 double length = getLengthProperty(thisObj);
\r
1016 double end = length;
\r
1018 if (args.length > 0) {
\r
1019 begin = ScriptRuntime.toInteger(args[0]);
\r
1024 } else if (begin > length) {
\r
1028 if (args.length > 1) {
\r
1029 end = ScriptRuntime.toInteger(args[1]);
\r
1034 } else if (end > length) {
\r
1040 long lbegin = (long)begin;
\r
1041 long lend = (long)end;
\r
1042 for (long slot = lbegin; slot < lend; slot++) {
\r
1043 Object temp = getElem(thisObj, slot);
\r
1044 setElem(result, slot - lbegin, temp);
\r
1050 protected int maxInstanceId() { return MAX_INSTANCE_ID; }
\r
1052 protected String getIdName(int id) {
\r
1053 if (id == Id_length) { return "length"; }
\r
1055 if (prototypeFlag) {
\r
1057 case Id_constructor: return "constructor";
\r
1058 case Id_toString: return "toString";
\r
1059 case Id_toLocaleString: return "toLocaleString";
\r
1060 case Id_join: return "join";
\r
1061 case Id_reverse: return "reverse";
\r
1062 case Id_sort: return "sort";
\r
1063 case Id_push: return "push";
\r
1064 case Id_pop: return "pop";
\r
1065 case Id_shift: return "shift";
\r
1066 case Id_unshift: return "unshift";
\r
1067 case Id_splice: return "splice";
\r
1068 case Id_concat: return "concat";
\r
1069 case Id_slice: return "slice";
\r
1075 private static final int
\r
1077 MAX_INSTANCE_ID = 1;
\r
1079 protected int mapNameToId(String s) {
\r
1080 if (s.equals("length")) { return Id_length; }
\r
1081 else if (prototypeFlag) {
\r
1082 return toPrototypeId(s);
\r
1087 // #string_id_map#
\r
1089 private static int toPrototypeId(String s) {
\r
1091 // #generated# Last update: 2001-04-23 11:46:01 GMT+02:00
\r
1092 L0: { id = 0; String X = null; int c;
\r
1093 L: switch (s.length()) {
\r
1094 case 3: X="pop";id=Id_pop; break L;
\r
1095 case 4: c=s.charAt(0);
\r
1096 if (c=='j') { X="join";id=Id_join; }
\r
1097 else if (c=='p') { X="push";id=Id_push; }
\r
1098 else if (c=='s') { X="sort";id=Id_sort; }
\r
1100 case 5: c=s.charAt(1);
\r
1101 if (c=='h') { X="shift";id=Id_shift; }
\r
1102 else if (c=='l') { X="slice";id=Id_slice; }
\r
1104 case 6: c=s.charAt(0);
\r
1105 if (c=='c') { X="concat";id=Id_concat; }
\r
1106 else if (c=='l') { X="length";id=Id_length; }
\r
1107 else if (c=='s') { X="splice";id=Id_splice; }
\r
1109 case 7: c=s.charAt(0);
\r
1110 if (c=='r') { X="reverse";id=Id_reverse; }
\r
1111 else if (c=='u') { X="unshift";id=Id_unshift; }
\r
1113 case 8: X="toString";id=Id_toString; break L;
\r
1114 case 11: X="constructor";id=Id_constructor; break L;
\r
1115 case 14: X="toLocaleString";id=Id_toLocaleString; break L;
\r
1117 if (X!=null && X!=s && !X.equals(s)) id = 0;
\r
1123 private static final int
\r
1124 Id_constructor = MAX_INSTANCE_ID + 1,
\r
1125 Id_toString = MAX_INSTANCE_ID + 2,
\r
1126 Id_toLocaleString = MAX_INSTANCE_ID + 3,
\r
1127 Id_join = MAX_INSTANCE_ID + 4,
\r
1128 Id_reverse = MAX_INSTANCE_ID + 5,
\r
1129 Id_sort = MAX_INSTANCE_ID + 6,
\r
1130 Id_push = MAX_INSTANCE_ID + 7,
\r
1131 Id_pop = MAX_INSTANCE_ID + 8,
\r
1132 Id_shift = MAX_INSTANCE_ID + 9,
\r
1133 Id_unshift = MAX_INSTANCE_ID + 10,
\r
1134 Id_splice = MAX_INSTANCE_ID + 11,
\r
1135 Id_concat = MAX_INSTANCE_ID + 12,
\r
1136 Id_slice = MAX_INSTANCE_ID + 13,
\r
1138 MAX_PROTOTYPE_ID = MAX_INSTANCE_ID + 13;
\r
1140 // #/string_id_map#
\r
1142 private long length;
\r
1143 private Object[] dense;
\r
1144 private static final int maximumDenseLength = 10000;
\r
1146 private boolean prototypeFlag;
\r