1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set sw=4 ts=8 et tw=78:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla Communicator client code, released
18 : * March 31, 1998.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Netscape Communications Corporation.
22 : * Portions created by the Initial Developer are Copyright (C) 1998
23 : * the Initial Developer. All Rights Reserved.
24 : *
25 : * Contributor(s):
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /*
42 : * JS array class.
43 : *
44 : * Array objects begin as "dense" arrays, optimized for index-only property
45 : * access over a vector of slots with high load factor. Array methods
46 : * optimize for denseness by testing that the object's class is
47 : * &ArrayClass, and can then directly manipulate the slots for efficiency.
48 : *
49 : * We track these pieces of metadata for arrays in dense mode:
50 : * - The array's length property as a uint32, accessible with
51 : * getArrayLength(), setArrayLength().
52 : * - The number of element slots (capacity), gettable with
53 : * getDenseArrayCapacity().
54 : * - The array's initialized length, accessible with
55 : * getDenseArrayInitializedLength().
56 : *
57 : * In dense mode, holes in the array are represented by
58 : * MagicValue(JS_ARRAY_HOLE) invalid values.
59 : *
60 : * NB: the capacity and length of a dense array are entirely unrelated! The
61 : * length may be greater than, less than, or equal to the capacity. The first
62 : * case may occur when the user writes "new Array(100)", in which case the
63 : * length is 100 while the capacity remains 0 (indices below length and above
64 : * capacity must be treated as holes). See array_length_setter for another
65 : * explanation of how the first case may occur.
66 : *
67 : * The initialized length of a dense array specifies the number of elements
68 : * that have been initialized. All elements above the initialized length are
69 : * holes in the array, and the memory for all elements between the initialized
70 : * length and capacity is left uninitialized. When type inference is disabled,
71 : * the initialized length always equals the array's capacity. When inference is
72 : * enabled, the initialized length is some value less than or equal to both the
73 : * array's length and the array's capacity.
74 : *
75 : * With inference enabled, there is flexibility in exactly the value the
76 : * initialized length must hold, e.g. if an array has length 5, capacity 10,
77 : * completely empty, it is valid for the initialized length to be any value
78 : * between zero and 5, as long as the in memory values below the initialized
79 : * length have been initialized with a hole value. However, in such cases we
80 : * want to keep the initialized length as small as possible: if the array is
81 : * known to have no hole values below its initialized length, then it is a
82 : * "packed" array and can be accessed much faster by JIT code.
83 : *
84 : * Arrays are converted to use SlowArrayClass when any of these conditions
85 : * are met:
86 : * - there are more than MIN_SPARSE_INDEX slots total and the load factor
87 : * (COUNT / capacity) is less than 0.25
88 : * - a property is set that is not indexed (and not "length")
89 : * - a property is defined that has non-default property attributes.
90 : *
91 : * Dense arrays do not track property creation order, so unlike other native
92 : * objects and slow arrays, enumerating an array does not necessarily visit the
93 : * properties in the order they were created. We could instead maintain the
94 : * scope to track property enumeration order, but still use the fast slot
95 : * access. That would have the same memory cost as just using a
96 : * SlowArrayClass, but have the same performance characteristics as a dense
97 : * array for slot accesses, at some cost in code complexity.
98 : */
99 : #include <limits.h>
100 : #include <stdlib.h>
101 : #include <string.h>
102 :
103 : #include "mozilla/RangedPtr.h"
104 :
105 : #include "jstypes.h"
106 : #include "jsutil.h"
107 :
108 : #include "jsapi.h"
109 : #include "jsarray.h"
110 : #include "jsatom.h"
111 : #include "jsbool.h"
112 : #include "jscntxt.h"
113 : #include "jsversion.h"
114 : #include "jsfun.h"
115 : #include "jsgc.h"
116 : #include "jsgcmark.h"
117 : #include "jsinterp.h"
118 : #include "jsiter.h"
119 : #include "jslock.h"
120 : #include "jsnum.h"
121 : #include "jsobj.h"
122 : #include "jsscope.h"
123 : #include "jswrapper.h"
124 : #include "methodjit/MethodJIT.h"
125 : #include "methodjit/StubCalls.h"
126 : #include "methodjit/StubCalls-inl.h"
127 :
128 : #include "vm/ArgumentsObject.h"
129 : #include "vm/MethodGuard.h"
130 : #include "vm/StringBuffer.h"
131 :
132 : #include "ds/Sort.h"
133 :
134 : #include "jsarrayinlines.h"
135 : #include "jsatominlines.h"
136 : #include "jscntxtinlines.h"
137 : #include "jsobjinlines.h"
138 : #include "jsscopeinlines.h"
139 : #include "jsstrinlines.h"
140 :
141 : #include "vm/ArgumentsObject-inl.h"
142 : #include "vm/ObjectImpl-inl.h"
143 : #include "vm/Stack-inl.h"
144 :
145 : using namespace mozilla;
146 : using namespace js;
147 : using namespace js::gc;
148 : using namespace js::types;
149 :
150 : JSBool
151 607379 : js_GetLengthProperty(JSContext *cx, JSObject *obj, uint32_t *lengthp)
152 : {
153 607379 : if (obj->isArray()) {
154 392534 : *lengthp = obj->getArrayLength();
155 392534 : return true;
156 : }
157 :
158 214845 : if (obj->isArguments()) {
159 214323 : ArgumentsObject &argsobj = obj->asArguments();
160 214323 : if (!argsobj.hasOverriddenLength()) {
161 213819 : *lengthp = argsobj.initialLength();
162 213819 : return true;
163 : }
164 : }
165 :
166 2052 : AutoValueRooter tvr(cx);
167 1026 : if (!obj->getProperty(cx, cx->runtime->atomState.lengthAtom, tvr.addr()))
168 18 : return false;
169 :
170 1008 : if (tvr.value().isInt32()) {
171 972 : *lengthp = uint32_t(tvr.value().toInt32()); /* uint32_t cast does ToUint32_t */
172 972 : return true;
173 : }
174 :
175 :
176 36 : return ToUint32(cx, tvr.value(), (uint32_t *)lengthp);
177 : }
178 :
179 : namespace js {
180 :
181 : /*
182 : * Determine if the id represents an array index or an XML property index.
183 : *
184 : * An id is an array index according to ECMA by (15.4):
185 : *
186 : * "Array objects give special treatment to a certain class of property names.
187 : * A property name P (in the form of a string value) is an array index if and
188 : * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
189 : * to 2^32-1."
190 : *
191 : * This means the largest allowed index is actually 2^32-2 (4294967294).
192 : *
193 : * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id)
194 : * except that by using signed 31-bit integers we miss the top half of the
195 : * valid range. This function checks the string representation itself; note
196 : * that calling a standard conversion routine might allow strings such as
197 : * "08" or "4.0" as array indices, which they are not.
198 : *
199 : */
200 : JS_FRIEND_API(bool)
201 31942868 : StringIsArrayIndex(JSLinearString *str, uint32_t *indexp)
202 : {
203 31942868 : const jschar *s = str->chars();
204 31942868 : uint32_t length = str->length();
205 31942868 : const jschar *end = s + length;
206 :
207 31942868 : if (length == 0 || length > (sizeof("4294967294") - 1) || !JS7_ISDEC(*s))
208 31827944 : return false;
209 :
210 114924 : uint32_t c = 0, previous = 0;
211 114924 : uint32_t index = JS7_UNDEC(*s++);
212 :
213 : /* Don't allow leading zeros. */
214 114924 : if (index == 0 && s != end)
215 45 : return false;
216 :
217 266349 : for (; s < end; s++) {
218 203376 : if (!JS7_ISDEC(*s))
219 51906 : return false;
220 :
221 151470 : previous = index;
222 151470 : c = JS7_UNDEC(*s);
223 151470 : index = 10 * index + c;
224 : }
225 :
226 : /* Make sure we didn't overflow. */
227 62973 : if (previous < (MAX_ARRAY_INDEX / 10) || (previous == (MAX_ARRAY_INDEX / 10) &&
228 : c <= (MAX_ARRAY_INDEX % 10))) {
229 62932 : JS_ASSERT(index <= MAX_ARRAY_INDEX);
230 62932 : *indexp = index;
231 62932 : return true;
232 : }
233 :
234 41 : return false;
235 : }
236 :
237 : }
238 :
239 : static JSBool
240 0 : BigIndexToId(JSContext *cx, JSObject *obj, uint32_t index, JSBool createAtom,
241 : jsid *idp)
242 : {
243 :
244 0 : JS_ASSERT(index > JSID_INT_MAX);
245 :
246 : jschar buf[10];
247 0 : jschar *start = ArrayEnd(buf);
248 0 : do {
249 0 : --start;
250 0 : *start = (jschar)('0' + index % 10);
251 0 : index /= 10;
252 : } while (index != 0);
253 :
254 : /*
255 : * Skip the atomization if the class is known to store atoms corresponding
256 : * to big indexes together with elements. In such case we know that the
257 : * array does not have an element at the given index if its atom does not
258 : * exist. Dense arrays don't use atoms for any indexes, though it would be
259 : * rare to see them have a big index in any case.
260 : */
261 : JSAtom *atom;
262 0 : if (!createAtom && (obj->isSlowArray() || obj->isArguments() || obj->isObject())) {
263 0 : atom = js_GetExistingStringAtom(cx, start, ArrayEnd(buf) - start);
264 0 : if (!atom) {
265 0 : *idp = JSID_VOID;
266 0 : return JS_TRUE;
267 : }
268 : } else {
269 0 : atom = js_AtomizeChars(cx, start, ArrayEnd(buf) - start);
270 0 : if (!atom)
271 0 : return JS_FALSE;
272 : }
273 :
274 0 : *idp = ATOM_TO_JSID(atom);
275 0 : return JS_TRUE;
276 : }
277 :
278 : bool
279 4866 : JSObject::willBeSparseDenseArray(unsigned requiredCapacity, unsigned newElementsHint)
280 : {
281 4866 : JS_ASSERT(isDenseArray());
282 4866 : JS_ASSERT(requiredCapacity > MIN_SPARSE_INDEX);
283 :
284 4866 : unsigned cap = getDenseArrayCapacity();
285 4866 : JS_ASSERT(requiredCapacity >= cap);
286 :
287 4866 : if (requiredCapacity >= JSObject::NELEMENTS_LIMIT)
288 9 : return true;
289 :
290 4857 : unsigned minimalDenseCount = requiredCapacity / 4;
291 4857 : if (newElementsHint >= minimalDenseCount)
292 0 : return false;
293 4857 : minimalDenseCount -= newElementsHint;
294 :
295 4857 : if (minimalDenseCount > cap)
296 3060 : return true;
297 :
298 1797 : unsigned len = getDenseArrayInitializedLength();
299 1797 : const Value *elems = getDenseArrayElements();
300 4132033 : for (unsigned i = 0; i < len; i++) {
301 4132033 : if (!elems[i].isMagic(JS_ARRAY_HOLE) && !--minimalDenseCount)
302 1797 : return false;
303 : }
304 0 : return true;
305 : }
306 :
307 : static bool
308 0 : ReallyBigIndexToId(JSContext* cx, double index, jsid* idp)
309 : {
310 0 : return js_ValueToStringId(cx, DoubleValue(index), idp);
311 : }
312 :
313 : static bool
314 209920 : IndexToId(JSContext* cx, JSObject* obj, double index, JSBool* hole, jsid* idp,
315 : JSBool createAtom = JS_FALSE)
316 : {
317 209920 : if (index <= JSID_INT_MAX) {
318 209920 : *idp = INT_TO_JSID(int(index));
319 209920 : return JS_TRUE;
320 : }
321 :
322 0 : if (index <= uint32_t(-1)) {
323 0 : if (!BigIndexToId(cx, obj, uint32_t(index), createAtom, idp))
324 0 : return JS_FALSE;
325 0 : if (hole && JSID_IS_VOID(*idp))
326 0 : *hole = JS_TRUE;
327 0 : return JS_TRUE;
328 : }
329 :
330 0 : return ReallyBigIndexToId(cx, index, idp);
331 : }
332 :
333 : bool
334 360 : JSObject::arrayGetOwnDataElement(JSContext *cx, size_t i, Value *vp)
335 : {
336 360 : JS_ASSERT(isArray());
337 :
338 360 : if (isDenseArray()) {
339 360 : if (i >= getArrayLength())
340 288 : vp->setMagic(JS_ARRAY_HOLE);
341 : else
342 72 : *vp = getDenseArrayElement(uint32_t(i));
343 360 : return true;
344 : }
345 :
346 : JSBool hole;
347 : jsid id;
348 0 : if (!IndexToId(cx, this, i, &hole, &id))
349 0 : return false;
350 :
351 0 : const Shape *shape = nativeLookup(cx, id);
352 0 : if (!shape || !shape->isDataDescriptor())
353 0 : vp->setMagic(JS_ARRAY_HOLE);
354 : else
355 0 : *vp = getSlot(shape->slot());
356 0 : return true;
357 : }
358 :
359 : /*
360 : * If the property at the given index exists, get its value into location
361 : * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp
362 : * to JSVAL_VOID. This function assumes that the location pointed by vp is
363 : * properly rooted and can be used as GC-protected storage for temporaries.
364 : */
365 : static inline JSBool
366 205933 : DoGetElement(JSContext *cx, JSObject *obj, double index, JSBool *hole, Value *vp)
367 : {
368 411866 : AutoIdRooter idr(cx);
369 :
370 205933 : *hole = JS_FALSE;
371 205933 : if (!IndexToId(cx, obj, index, hole, idr.addr()))
372 0 : return JS_FALSE;
373 205933 : if (*hole) {
374 0 : vp->setUndefined();
375 0 : return JS_TRUE;
376 : }
377 :
378 : JSObject *obj2;
379 : JSProperty *prop;
380 205933 : if (!obj->lookupGeneric(cx, idr.id(), &obj2, &prop))
381 9 : return JS_FALSE;
382 205924 : if (!prop) {
383 205447 : vp->setUndefined();
384 205447 : *hole = JS_TRUE;
385 : } else {
386 477 : if (!obj->getGeneric(cx, idr.id(), vp))
387 9 : return JS_FALSE;
388 468 : *hole = JS_FALSE;
389 : }
390 205915 : return JS_TRUE;
391 : }
392 :
393 : static inline JSBool
394 505294 : DoGetElement(JSContext *cx, JSObject *obj, uint32_t index, JSBool *hole, Value *vp)
395 : {
396 : bool present;
397 505294 : if (!obj->getElementIfPresent(cx, obj, index, vp, &present))
398 36 : return false;
399 :
400 505258 : *hole = !present;
401 505258 : if (*hole)
402 503143 : vp->setUndefined();
403 :
404 505258 : return true;
405 : }
406 :
407 : template<typename IndexType>
408 : static void
409 206320 : AssertGreaterThanZero(IndexType index)
410 : {
411 206320 : JS_ASSERT(index >= 0);
412 206320 : }
413 :
414 : template<>
415 : void
416 1365978 : AssertGreaterThanZero(uint32_t index)
417 : {
418 1365978 : }
419 :
420 : template<typename IndexType>
421 : static JSBool
422 1572298 : GetElement(JSContext *cx, JSObject *obj, IndexType index, JSBool *hole, Value *vp)
423 : {
424 1572298 : AssertGreaterThanZero(index);
425 1572298 : if (obj->isDenseArray() && index < obj->getDenseArrayInitializedLength() &&
426 : !(*vp = obj->getDenseArrayElement(uint32_t(index))).isMagic(JS_ARRAY_HOLE)) {
427 859550 : *hole = JS_FALSE;
428 859550 : return JS_TRUE;
429 : }
430 712748 : if (obj->isArguments()) {
431 1521 : if (obj->asArguments().getElement(uint32_t(index), vp)) {
432 1521 : *hole = JS_FALSE;
433 1521 : return true;
434 : }
435 : }
436 :
437 711227 : return DoGetElement(cx, obj, index, hole, vp);
438 : }
439 :
440 : namespace js {
441 :
442 : static bool
443 4190 : GetElementsSlow(JSContext *cx, JSObject *aobj, uint32_t length, Value *vp)
444 : {
445 366310 : for (uint32_t i = 0; i < length; i++) {
446 362120 : if (!aobj->getElement(cx, i, &vp[i]))
447 0 : return false;
448 : }
449 :
450 4190 : return true;
451 : }
452 :
453 : bool
454 394688 : GetElements(JSContext *cx, JSObject *aobj, uint32_t length, Value *vp)
455 : {
456 572366 : if (aobj->isDenseArray() && length <= aobj->getDenseArrayInitializedLength() &&
457 177678 : !js_PrototypeHasIndexedProperties(cx, aobj)) {
458 : /* The prototype does not have indexed properties so hole = undefined */
459 177588 : const Value *srcbeg = aobj->getDenseArrayElements();
460 177588 : const Value *srcend = srcbeg + length;
461 177588 : const Value *src = srcbeg;
462 527310 : for (Value *dst = vp; src < srcend; ++dst, ++src)
463 349722 : *dst = src->isMagic(JS_ARRAY_HOLE) ? UndefinedValue() : *src;
464 177588 : return true;
465 : }
466 :
467 217100 : if (aobj->isArguments()) {
468 213396 : ArgumentsObject &argsobj = aobj->asArguments();
469 213396 : if (!argsobj.hasOverriddenLength()) {
470 212955 : if (argsobj.getElements(0, length, vp))
471 212910 : return true;
472 : }
473 : }
474 :
475 4190 : return GetElementsSlow(cx, aobj, length, vp);
476 : }
477 :
478 : }
479 :
480 : /*
481 : * Set the value of the property at the given index to v assuming v is rooted.
482 : */
483 : static JSBool
484 14889 : SetArrayElement(JSContext *cx, JSObject *obj, double index, const Value &v)
485 : {
486 14889 : JS_ASSERT(index >= 0);
487 :
488 14889 : if (obj->isDenseArray()) {
489 : /* Predicted/prefetched code should favor the remains-dense case. */
490 11244 : JSObject::EnsureDenseResult result = JSObject::ED_SPARSE;
491 : do {
492 11244 : if (index > uint32_t(-1))
493 0 : break;
494 11244 : uint32_t idx = uint32_t(index);
495 11244 : result = obj->ensureDenseArrayElements(cx, idx, 1);
496 11244 : if (result != JSObject::ED_OK)
497 342 : break;
498 10902 : if (idx >= obj->getArrayLength())
499 4134 : obj->setDenseArrayLength(idx + 1);
500 10902 : obj->setDenseArrayElementWithType(cx, idx, v);
501 10902 : return true;
502 : } while (false);
503 :
504 342 : if (result == JSObject::ED_FAILED)
505 0 : return false;
506 342 : JS_ASSERT(result == JSObject::ED_SPARSE);
507 342 : if (!obj->makeDenseArraySlow(cx))
508 0 : return JS_FALSE;
509 : }
510 :
511 7974 : AutoIdRooter idr(cx);
512 :
513 3987 : if (!IndexToId(cx, obj, index, NULL, idr.addr(), JS_TRUE))
514 0 : return JS_FALSE;
515 3987 : JS_ASSERT(!JSID_IS_VOID(idr.id()));
516 :
517 3987 : Value tmp = v;
518 3987 : return obj->setGeneric(cx, idr.id(), &tmp, true);
519 : }
520 :
521 : /*
522 : * Delete the element |index| from |obj|. If |strict|, do a strict
523 : * deletion: throw if the property is not configurable.
524 : *
525 : * - Return 1 if the deletion succeeds (that is, ES5's [[Delete]] would
526 : * return true)
527 : *
528 : * - Return 0 if the deletion fails because the property is not
529 : * configurable (that is, [[Delete]] would return false). Note that if
530 : * |strict| is true we will throw, not return zero.
531 : *
532 : * - Return -1 if an exception occurs (that is, [[Delete]] would throw).
533 : */
534 : static int
535 305966 : DeleteArrayElement(JSContext *cx, JSObject *obj, double index, bool strict)
536 : {
537 305966 : JS_ASSERT(index >= 0);
538 305966 : JS_ASSERT(floor(index) == index);
539 :
540 305966 : if (obj->isDenseArray()) {
541 113033 : if (index <= UINT32_MAX) {
542 113033 : uint32_t idx = uint32_t(index);
543 113033 : if (idx < obj->getDenseArrayInitializedLength()) {
544 16854 : obj->markDenseArrayNotPacked(cx);
545 16854 : obj->setDenseArrayElement(idx, MagicValue(JS_ARRAY_HOLE));
546 16854 : if (!js_SuppressDeletedElement(cx, obj, idx))
547 0 : return -1;
548 : }
549 : }
550 113033 : return 1;
551 : }
552 :
553 : Value v;
554 192933 : if (index <= UINT32_MAX) {
555 192933 : if (!obj->deleteElement(cx, uint32_t(index), &v, strict))
556 36 : return -1;
557 : } else {
558 0 : if (!obj->deleteByValue(cx, DoubleValue(index), &v, strict))
559 0 : return -1;
560 : }
561 :
562 192897 : return v.isTrue() ? 1 : 0;
563 : }
564 :
565 : /*
566 : * When hole is true, delete the property at the given index. Otherwise set
567 : * its value to v assuming v is rooted.
568 : */
569 : static JSBool
570 300820 : SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, double index,
571 : JSBool hole, const Value &v)
572 : {
573 300820 : if (hole) {
574 297832 : JS_ASSERT(v.isUndefined());
575 297832 : return DeleteArrayElement(cx, obj, index, true) >= 0;
576 : }
577 2988 : return SetArrayElement(cx, obj, index, v);
578 : }
579 :
580 : JSBool
581 13044 : js_SetLengthProperty(JSContext *cx, JSObject *obj, double length)
582 : {
583 13044 : Value v = NumberValue(length);
584 :
585 : /* We don't support read-only array length yet. */
586 13044 : return obj->setProperty(cx, cx->runtime->atomState.lengthAtom, &v, false);
587 : }
588 :
589 : /*
590 : * Since SpiderMonkey supports cross-class prototype-based delegation, we have
591 : * to be careful about the length getter and setter being called on an object
592 : * not of Array class. For the getter, we search obj's prototype chain for the
593 : * array that caused this getter to be invoked. In the setter case to overcome
594 : * the JSPROP_SHARED attribute, we must define a shadowing length property.
595 : */
596 : static JSBool
597 171 : array_length_getter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
598 : {
599 0 : do {
600 171 : if (obj->isArray()) {
601 171 : vp->setNumber(obj->getArrayLength());
602 171 : return JS_TRUE;
603 : }
604 0 : } while ((obj = obj->getProto()) != NULL);
605 0 : return JS_TRUE;
606 : }
607 :
608 : static JSBool
609 15006 : array_length_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
610 : {
611 15006 : if (!obj->isArray()) {
612 : return obj->defineProperty(cx, cx->runtime->atomState.lengthAtom, *vp,
613 0 : NULL, NULL, JSPROP_ENUMERATE);
614 : }
615 :
616 : uint32_t newlen;
617 15006 : if (!ToUint32(cx, *vp, &newlen))
618 0 : return false;
619 :
620 : double d;
621 15006 : if (!ToNumber(cx, *vp, &d))
622 0 : return false;
623 :
624 15006 : if (d != newlen) {
625 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
626 0 : return false;
627 : }
628 :
629 15006 : uint32_t oldlen = obj->getArrayLength();
630 15006 : if (oldlen == newlen)
631 11910 : return true;
632 :
633 3096 : vp->setNumber(newlen);
634 3096 : if (oldlen < newlen) {
635 2871 : obj->setArrayLength(cx, newlen);
636 2871 : return true;
637 : }
638 :
639 225 : if (obj->isDenseArray()) {
640 : /*
641 : * Don't reallocate if we're not actually shrinking our slots. If we do
642 : * shrink slots here, shrink the initialized length too. This permits us
643 : * us to disregard length when reading from arrays as long we are within
644 : * the initialized capacity.
645 : */
646 189 : uint32_t oldcap = obj->getDenseArrayCapacity();
647 189 : uint32_t oldinit = obj->getDenseArrayInitializedLength();
648 189 : if (oldinit > newlen)
649 123 : obj->setDenseArrayInitializedLength(newlen);
650 189 : if (oldcap > newlen)
651 175 : obj->shrinkElements(cx, newlen);
652 36 : } else if (oldlen - newlen < (1 << 24)) {
653 36 : do {
654 36 : --oldlen;
655 36 : if (!JS_CHECK_OPERATION_LIMIT(cx)) {
656 0 : obj->setArrayLength(cx, oldlen + 1);
657 0 : return false;
658 : }
659 36 : int deletion = DeleteArrayElement(cx, obj, oldlen, strict);
660 36 : if (deletion <= 0) {
661 0 : obj->setArrayLength(cx, oldlen + 1);
662 0 : return deletion >= 0;
663 : }
664 : } while (oldlen != newlen);
665 : } else {
666 : /*
667 : * We are going to remove a lot of indexes in a presumably sparse
668 : * array. So instead of looping through indexes between newlen and
669 : * oldlen, we iterate through all properties and remove those that
670 : * correspond to indexes in the half-open range [newlen, oldlen). See
671 : * bug 322135.
672 : */
673 0 : JSObject *iter = JS_NewPropertyIterator(cx, obj);
674 0 : if (!iter)
675 0 : return false;
676 :
677 0 : uint32_t gap = oldlen - newlen;
678 0 : for (;;) {
679 0 : if (!JS_CHECK_OPERATION_LIMIT(cx) || !JS_NextProperty(cx, iter, &id))
680 0 : return false;
681 0 : if (JSID_IS_VOID(id))
682 0 : break;
683 : uint32_t index;
684 : Value junk;
685 0 : if (js_IdIsIndex(id, &index) && index - newlen < gap &&
686 0 : !obj->deleteElement(cx, index, &junk, false)) {
687 0 : return false;
688 : }
689 : }
690 : }
691 :
692 225 : obj->setArrayLength(cx, newlen);
693 225 : return true;
694 : }
695 :
696 : /* Returns true if the dense array has an own property at the index. */
697 : static inline bool
698 706838 : IsDenseArrayIndex(JSObject *obj, uint32_t index)
699 : {
700 706838 : JS_ASSERT(obj->isDenseArray());
701 :
702 706838 : return index < obj->getDenseArrayInitializedLength() &&
703 706838 : !obj->getDenseArrayElement(index).isMagic(JS_ARRAY_HOLE);
704 : }
705 :
706 : /*
707 : * We have only indexed properties up to initialized length, plus the
708 : * length property. For all else, we delegate to the prototype.
709 : */
710 : static inline bool
711 709407 : IsDenseArrayId(JSContext *cx, JSObject *obj, jsid id)
712 : {
713 709407 : JS_ASSERT(obj->isDenseArray());
714 :
715 : uint32_t i;
716 709407 : return JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom) ||
717 709407 : (js_IdIsIndex(id, &i) && IsDenseArrayIndex(obj, i));
718 : }
719 :
720 : static JSBool
721 709407 : array_lookupGeneric(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
722 : JSProperty **propp)
723 : {
724 709407 : if (!obj->isDenseArray())
725 0 : return js_LookupProperty(cx, obj, id, objp, propp);
726 :
727 709407 : if (IsDenseArrayId(cx, obj, id)) {
728 24545 : *propp = (JSProperty *) 1; /* non-null to indicate found */
729 24545 : *objp = obj;
730 24545 : return JS_TRUE;
731 : }
732 :
733 684862 : JSObject *proto = obj->getProto();
734 684862 : if (!proto) {
735 0 : *objp = NULL;
736 0 : *propp = NULL;
737 0 : return JS_TRUE;
738 : }
739 684862 : return proto->lookupGeneric(cx, id, objp, propp);
740 : }
741 :
742 : static JSBool
743 0 : array_lookupProperty(JSContext *cx, JSObject *obj, PropertyName *name, JSObject **objp,
744 : JSProperty **propp)
745 : {
746 0 : return array_lookupGeneric(cx, obj, ATOM_TO_JSID(name), objp, propp);
747 : }
748 :
749 : static JSBool
750 0 : array_lookupElement(JSContext *cx, JSObject *obj, uint32_t index, JSObject **objp,
751 : JSProperty **propp)
752 : {
753 0 : if (!obj->isDenseArray())
754 0 : return js_LookupElement(cx, obj, index, objp, propp);
755 :
756 0 : if (IsDenseArrayIndex(obj, index)) {
757 0 : *propp = (JSProperty *) 1; /* non-null to indicate found */
758 0 : *objp = obj;
759 0 : return true;
760 : }
761 :
762 0 : if (JSObject *proto = obj->getProto())
763 0 : return proto->lookupElement(cx, index, objp, propp);
764 :
765 0 : *objp = NULL;
766 0 : *propp = NULL;
767 0 : return true;
768 : }
769 :
770 : static JSBool
771 0 : array_lookupSpecial(JSContext *cx, JSObject *obj, SpecialId sid, JSObject **objp,
772 : JSProperty **propp)
773 : {
774 0 : return array_lookupGeneric(cx, obj, SPECIALID_TO_JSID(sid), objp, propp);
775 : }
776 :
777 : JSBool
778 0 : js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, jsid id, Value *vp)
779 : {
780 0 : JS_ASSERT(obj->isDenseArray());
781 :
782 : uint32_t i;
783 0 : if (!js_IdIsIndex(id, &i)) {
784 0 : JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
785 0 : vp->setNumber(obj->getArrayLength());
786 0 : return JS_TRUE;
787 : }
788 0 : *vp = obj->getDenseArrayElement(i);
789 0 : return JS_TRUE;
790 : }
791 :
792 : static JSBool
793 2379714 : array_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name, Value *vp)
794 : {
795 2379714 : if (name == cx->runtime->atomState.lengthAtom) {
796 812 : vp->setNumber(obj->getArrayLength());
797 812 : return true;
798 : }
799 :
800 2378902 : if (name == cx->runtime->atomState.protoAtom) {
801 72 : vp->setObjectOrNull(obj->getProto());
802 72 : return true;
803 : }
804 :
805 2378830 : if (!obj->isDenseArray())
806 0 : return js_GetProperty(cx, obj, receiver, ATOM_TO_JSID(name), vp);
807 :
808 2378830 : JSObject *proto = obj->getProto();
809 2378830 : if (!proto) {
810 0 : vp->setUndefined();
811 0 : return true;
812 : }
813 :
814 2378830 : return proto->getProperty(cx, receiver, name, vp);
815 : }
816 :
817 : static JSBool
818 695873 : array_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp)
819 : {
820 695873 : if (!obj->isDenseArray())
821 0 : return js_GetElement(cx, obj, receiver, index, vp);
822 :
823 695873 : if (index < obj->getDenseArrayInitializedLength()) {
824 203288 : *vp = obj->getDenseArrayElement(index);
825 203288 : if (!vp->isMagic(JS_ARRAY_HOLE)) {
826 : /* Type information for dense array elements must be correct. */
827 403552 : JS_ASSERT_IF(!obj->hasSingletonType(),
828 403552 : js::types::TypeHasProperty(cx, obj->type(), JSID_VOID, *vp));
829 :
830 201776 : return true;
831 : }
832 : }
833 :
834 494097 : JSObject *proto = obj->getProto();
835 494097 : if (!proto) {
836 0 : vp->setUndefined();
837 0 : return true;
838 : }
839 :
840 494097 : return proto->getElement(cx, receiver, index, vp);
841 : }
842 :
843 : static JSBool
844 0 : array_getSpecial(JSContext *cx, JSObject *obj, JSObject *receiver, SpecialId sid, Value *vp)
845 : {
846 0 : if (obj->isDenseArray() && !obj->getProto()) {
847 0 : vp->setUndefined();
848 0 : return true;
849 : }
850 :
851 0 : return js_GetProperty(cx, obj, receiver, SPECIALID_TO_JSID(sid), vp);
852 : }
853 :
854 : static JSBool
855 2397752 : array_getGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
856 : {
857 2397752 : Value idval = IdToValue(id);
858 :
859 : uint32_t index;
860 2397752 : if (IsDefinitelyIndex(idval, &index))
861 17573 : return array_getElement(cx, obj, receiver, index, vp);
862 :
863 2380179 : SpecialId sid;
864 2380179 : if (ValueIsSpecial(obj, &idval, &sid, cx))
865 0 : return array_getSpecial(cx, obj, receiver, sid, vp);
866 :
867 : JSAtom *atom;
868 2380179 : if (!js_ValueToAtom(cx, idval, &atom))
869 0 : return false;
870 :
871 2380179 : if (atom->isIndex(&index))
872 465 : return array_getElement(cx, obj, receiver, index, vp);
873 :
874 2379714 : return array_getProperty(cx, obj, receiver, atom->asPropertyName(), vp);
875 : }
876 :
877 : static JSBool
878 1992603 : slowarray_addProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
879 : {
880 : uint32_t index, length;
881 :
882 1992603 : if (!js_IdIsIndex(id, &index))
883 867448 : return JS_TRUE;
884 1125155 : length = obj->getArrayLength();
885 1125155 : if (index >= length)
886 1121825 : obj->setArrayLength(cx, index + 1);
887 1125155 : return JS_TRUE;
888 : }
889 :
890 : static JSType
891 81 : array_typeOf(JSContext *cx, JSObject *obj)
892 : {
893 81 : return JSTYPE_OBJECT;
894 : }
895 :
896 : static JSBool
897 1720816 : array_setGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
898 : {
899 : uint32_t i;
900 :
901 1720816 : if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
902 14241 : return array_length_setter(cx, obj, id, strict, vp);
903 :
904 1706575 : if (!obj->isDenseArray())
905 0 : return js_SetPropertyHelper(cx, obj, id, 0, vp, strict);
906 :
907 : do {
908 1706575 : if (!js_IdIsIndex(id, &i))
909 1215 : break;
910 1705360 : if (js_PrototypeHasIndexedProperties(cx, obj))
911 963 : break;
912 :
913 1704397 : JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, i, 1);
914 1704397 : if (result != JSObject::ED_OK) {
915 1314 : if (result == JSObject::ED_FAILED)
916 0 : return false;
917 1314 : JS_ASSERT(result == JSObject::ED_SPARSE);
918 1314 : break;
919 : }
920 :
921 1703083 : if (i >= obj->getArrayLength())
922 522204 : obj->setDenseArrayLength(i + 1);
923 1703083 : obj->setDenseArrayElementWithType(cx, i, *vp);
924 1703083 : return true;
925 : } while (false);
926 :
927 3492 : if (!obj->makeDenseArraySlow(cx))
928 0 : return false;
929 3492 : return js_SetPropertyHelper(cx, obj, id, 0, vp, strict);
930 : }
931 :
932 : static JSBool
933 0 : array_setProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict)
934 : {
935 0 : return array_setGeneric(cx, obj, ATOM_TO_JSID(name), vp, strict);
936 : }
937 :
938 : static JSBool
939 22238 : array_setElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict)
940 : {
941 : jsid id;
942 22238 : if (!IndexToId(cx, index, &id))
943 0 : return false;
944 :
945 22238 : if (!obj->isDenseArray())
946 0 : return js_SetPropertyHelper(cx, obj, id, 0, vp, strict);
947 :
948 : do {
949 : /*
950 : * UINT32_MAX is not an array index and must not affect the length
951 : * property, so specifically reject it.
952 : */
953 22238 : if (index == UINT32_MAX)
954 0 : break;
955 22238 : if (js_PrototypeHasIndexedProperties(cx, obj))
956 0 : break;
957 :
958 22238 : JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, index, 1);
959 22238 : if (result != JSObject::ED_OK) {
960 0 : if (result == JSObject::ED_FAILED)
961 0 : return false;
962 0 : JS_ASSERT(result == JSObject::ED_SPARSE);
963 0 : break;
964 : }
965 :
966 22238 : if (index >= obj->getArrayLength())
967 3635 : obj->setDenseArrayLength(index + 1);
968 22238 : obj->setDenseArrayElementWithType(cx, index, *vp);
969 22238 : return true;
970 : } while (false);
971 :
972 0 : if (!obj->makeDenseArraySlow(cx))
973 0 : return false;
974 0 : return js_SetPropertyHelper(cx, obj, id, 0, vp, strict);
975 : }
976 :
977 : static JSBool
978 0 : array_setSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict)
979 : {
980 0 : return array_setGeneric(cx, obj, SPECIALID_TO_JSID(sid), vp, strict);
981 : }
982 :
983 : JSBool
984 2004834 : js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj)
985 : {
986 : /*
987 : * Walk up the prototype chain and see if this indexed element already
988 : * exists. If we hit the end of the prototype chain, it's safe to set the
989 : * element on the original object.
990 : */
991 8018118 : while ((obj = obj->getProto()) != NULL) {
992 : /*
993 : * If the prototype is a non-native object (possibly a dense array), or
994 : * a native object (possibly a slow array) that has indexed properties,
995 : * return true.
996 : */
997 4009507 : if (!obj->isNative())
998 0 : return JS_TRUE;
999 4009507 : if (obj->isIndexed())
1000 1057 : return JS_TRUE;
1001 : }
1002 2003777 : return JS_FALSE;
1003 : }
1004 :
1005 : static JSBool
1006 5804034 : array_defineGeneric(JSContext *cx, JSObject *obj, jsid id, const Value *value,
1007 : JSPropertyOp getter, StrictPropertyOp setter, unsigned attrs)
1008 : {
1009 5804034 : if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
1010 0 : return JS_TRUE;
1011 :
1012 5804034 : if (!obj->isDenseArray())
1013 0 : return js_DefineProperty(cx, obj, id, value, getter, setter, attrs);
1014 :
1015 : do {
1016 5804034 : uint32_t i = 0; // init to shut GCC up
1017 5804034 : bool isIndex = js_IdIsIndex(id, &i);
1018 5804034 : if (!isIndex || attrs != JSPROP_ENUMERATE)
1019 0 : break;
1020 :
1021 5804034 : JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, i, 1);
1022 5804034 : if (result != JSObject::ED_OK) {
1023 0 : if (result == JSObject::ED_FAILED)
1024 0 : return false;
1025 0 : JS_ASSERT(result == JSObject::ED_SPARSE);
1026 0 : break;
1027 : }
1028 :
1029 5804034 : if (i >= obj->getArrayLength())
1030 388 : obj->setDenseArrayLength(i + 1);
1031 5804034 : obj->setDenseArrayElementWithType(cx, i, *value);
1032 5804034 : return true;
1033 : } while (false);
1034 :
1035 0 : if (!obj->makeDenseArraySlow(cx))
1036 0 : return false;
1037 0 : return js_DefineProperty(cx, obj, id, value, getter, setter, attrs);
1038 : }
1039 :
1040 : static JSBool
1041 0 : array_defineProperty(JSContext *cx, JSObject *obj, PropertyName *name, const Value *value,
1042 : JSPropertyOp getter, StrictPropertyOp setter, unsigned attrs)
1043 : {
1044 0 : return array_defineGeneric(cx, obj, ATOM_TO_JSID(name), value, getter, setter, attrs);
1045 : }
1046 :
1047 : namespace js {
1048 :
1049 : /* non-static for direct definition of array elements within the engine */
1050 : JSBool
1051 2843 : array_defineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *value,
1052 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
1053 : {
1054 2843 : if (!obj->isDenseArray())
1055 0 : return js_DefineElement(cx, obj, index, value, getter, setter, attrs);
1056 :
1057 : jsid id;
1058 2843 : if (!IndexToId(cx, index, &id))
1059 0 : return false;
1060 :
1061 : do {
1062 : /*
1063 : * UINT32_MAX is not an array index and must not affect the length
1064 : * property, so specifically reject it.
1065 : */
1066 2843 : if (attrs != JSPROP_ENUMERATE || index == UINT32_MAX)
1067 0 : break;
1068 :
1069 2843 : JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, index, 1);
1070 2843 : if (result != JSObject::ED_OK) {
1071 0 : if (result == JSObject::ED_FAILED)
1072 0 : return false;
1073 0 : JS_ASSERT(result == JSObject::ED_SPARSE);
1074 0 : break;
1075 : }
1076 :
1077 2843 : if (index >= obj->getArrayLength())
1078 2187 : obj->setDenseArrayLength(index + 1);
1079 2843 : obj->setDenseArrayElementWithType(cx, index, *value);
1080 2843 : return true;
1081 : } while (false);
1082 :
1083 0 : if (!obj->makeDenseArraySlow(cx))
1084 0 : return false;
1085 0 : return js_DefineElement(cx, obj, index, value, getter, setter, attrs);
1086 : }
1087 :
1088 : } // namespace js
1089 :
1090 : static JSBool
1091 0 : array_defineSpecial(JSContext *cx, JSObject *obj, SpecialId sid, const Value *value,
1092 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
1093 : {
1094 0 : return array_defineGeneric(cx, obj, SPECIALID_TO_JSID(sid), value, getter, setter, attrs);
1095 : }
1096 :
1097 : static JSBool
1098 135 : array_getGenericAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
1099 : {
1100 135 : *attrsp = JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)
1101 135 : ? JSPROP_PERMANENT : JSPROP_ENUMERATE;
1102 135 : return true;
1103 : }
1104 :
1105 : static JSBool
1106 0 : array_getPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, unsigned *attrsp)
1107 : {
1108 : *attrsp = (name == cx->runtime->atomState.lengthAtom)
1109 : ? JSPROP_PERMANENT
1110 0 : : JSPROP_ENUMERATE;
1111 0 : return true;
1112 : }
1113 :
1114 : static JSBool
1115 0 : array_getElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
1116 : {
1117 0 : *attrsp = JSPROP_ENUMERATE;
1118 0 : return true;
1119 : }
1120 :
1121 : static JSBool
1122 0 : array_getSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, unsigned *attrsp)
1123 : {
1124 0 : *attrsp = JSPROP_ENUMERATE;
1125 0 : return true;
1126 : }
1127 :
1128 : static JSBool
1129 0 : array_setGenericAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
1130 : {
1131 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS);
1132 0 : return false;
1133 : }
1134 :
1135 : static JSBool
1136 0 : array_setPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, unsigned *attrsp)
1137 : {
1138 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS);
1139 0 : return false;
1140 : }
1141 :
1142 : static JSBool
1143 0 : array_setElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
1144 : {
1145 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS);
1146 0 : return false;
1147 : }
1148 :
1149 : static JSBool
1150 0 : array_setSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, unsigned *attrsp)
1151 : {
1152 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS);
1153 0 : return false;
1154 : }
1155 :
1156 : static JSBool
1157 99 : array_deleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict)
1158 : {
1159 99 : if (!obj->isDenseArray())
1160 0 : return js_DeleteProperty(cx, obj, name, rval, strict);
1161 :
1162 99 : if (name == cx->runtime->atomState.lengthAtom) {
1163 99 : rval->setBoolean(false);
1164 99 : return true;
1165 : }
1166 :
1167 0 : rval->setBoolean(true);
1168 0 : return true;
1169 : }
1170 :
1171 : namespace js {
1172 :
1173 : /* non-static for direct deletion of array elements within the engine */
1174 : JSBool
1175 1530 : array_deleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict)
1176 : {
1177 1530 : if (!obj->isDenseArray())
1178 0 : return js_DeleteElement(cx, obj, index, rval, strict);
1179 :
1180 1530 : if (index < obj->getDenseArrayInitializedLength()) {
1181 1530 : obj->markDenseArrayNotPacked(cx);
1182 1530 : obj->setDenseArrayElement(index, MagicValue(JS_ARRAY_HOLE));
1183 : }
1184 :
1185 1530 : if (!js_SuppressDeletedElement(cx, obj, index))
1186 0 : return false;
1187 :
1188 1530 : rval->setBoolean(true);
1189 1530 : return true;
1190 : }
1191 :
1192 : } // namespace js
1193 :
1194 : static JSBool
1195 0 : array_deleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict)
1196 : {
1197 0 : if (!obj->isDenseArray())
1198 0 : return js_DeleteSpecial(cx, obj, sid, rval, strict);
1199 :
1200 0 : rval->setBoolean(true);
1201 0 : return true;
1202 : }
1203 :
1204 : static void
1205 5528 : array_trace(JSTracer *trc, JSObject *obj)
1206 : {
1207 5528 : JS_ASSERT(obj->isDenseArray());
1208 :
1209 5528 : uint32_t initLength = obj->getDenseArrayInitializedLength();
1210 5528 : MarkArraySlots(trc, initLength, obj->getDenseArrayElements(), "element");
1211 5528 : }
1212 :
1213 : static JSBool
1214 55 : array_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props)
1215 : {
1216 55 : JS_ASSERT(obj->isDenseArray());
1217 :
1218 : /*
1219 : * We must slowify dense arrays; otherwise, we'd need to detect assignments to holes,
1220 : * since that is effectively adding a new property to the array.
1221 : */
1222 110 : if (!obj->makeDenseArraySlow(cx) ||
1223 55 : !GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, props))
1224 0 : return false;
1225 :
1226 55 : *success = true;
1227 55 : return true;
1228 : }
1229 :
1230 : Class js::ArrayClass = {
1231 : "Array",
1232 : Class::NON_NATIVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_FOR_OF_ITERATION,
1233 : JS_PropertyStub, /* addProperty */
1234 : JS_PropertyStub, /* delProperty */
1235 : JS_PropertyStub, /* getProperty */
1236 : JS_StrictPropertyStub, /* setProperty */
1237 : JS_EnumerateStub,
1238 : JS_ResolveStub,
1239 : JS_ConvertStub,
1240 : NULL,
1241 : NULL, /* checkAccess */
1242 : NULL, /* call */
1243 : NULL, /* construct */
1244 : NULL, /* hasInstance */
1245 : array_trace, /* trace */
1246 : {
1247 : NULL, /* equality */
1248 : NULL, /* outerObject */
1249 : NULL, /* innerObject */
1250 : JS_ElementIteratorStub,
1251 : NULL, /* unused */
1252 : false, /* isWrappedNative */
1253 : },
1254 : {
1255 : array_lookupGeneric,
1256 : array_lookupProperty,
1257 : array_lookupElement,
1258 : array_lookupSpecial,
1259 : array_defineGeneric,
1260 : array_defineProperty,
1261 : array_defineElement,
1262 : array_defineSpecial,
1263 : array_getGeneric,
1264 : array_getProperty,
1265 : array_getElement,
1266 : NULL, /* getElementIfPresent, because this is hard for now for
1267 : slow arrays */
1268 : array_getSpecial,
1269 : array_setGeneric,
1270 : array_setProperty,
1271 : array_setElement,
1272 : array_setSpecial,
1273 : array_getGenericAttributes,
1274 : array_getPropertyAttributes,
1275 : array_getElementAttributes,
1276 : array_getSpecialAttributes,
1277 : array_setGenericAttributes,
1278 : array_setPropertyAttributes,
1279 : array_setElementAttributes,
1280 : array_setSpecialAttributes,
1281 : array_deleteProperty,
1282 : array_deleteElement,
1283 : array_deleteSpecial,
1284 : NULL, /* enumerate */
1285 : array_typeOf,
1286 : array_fix,
1287 : NULL, /* thisObject */
1288 : NULL, /* clear */
1289 : }
1290 : };
1291 :
1292 : Class js::SlowArrayClass = {
1293 : "Array",
1294 : JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_FOR_OF_ITERATION,
1295 : slowarray_addProperty,
1296 : JS_PropertyStub, /* delProperty */
1297 : JS_PropertyStub, /* getProperty */
1298 : JS_StrictPropertyStub, /* setProperty */
1299 : JS_EnumerateStub,
1300 : JS_ResolveStub,
1301 : JS_ConvertStub,
1302 : NULL,
1303 : NULL, /* checkAccess */
1304 : NULL, /* call */
1305 : NULL, /* construct */
1306 : NULL, /* hasInstance */
1307 : NULL, /* trace */
1308 : {
1309 : NULL, /* equality */
1310 : NULL, /* outerObject */
1311 : NULL, /* innerObject */
1312 : JS_ElementIteratorStub,
1313 : NULL, /* unused */
1314 : false, /* isWrappedNative */
1315 : }
1316 : };
1317 :
1318 : bool
1319 234531 : JSObject::allocateSlowArrayElements(JSContext *cx)
1320 : {
1321 234531 : JS_ASSERT(hasClass(&js::SlowArrayClass));
1322 234531 : JS_ASSERT(elements == emptyObjectElements);
1323 :
1324 234531 : ObjectElements *header = cx->new_<ObjectElements>(0, 0);
1325 234531 : if (!header)
1326 0 : return false;
1327 :
1328 234531 : elements = header->elements();
1329 234531 : return true;
1330 : }
1331 :
1332 : static bool
1333 234531 : AddLengthProperty(JSContext *cx, JSObject *obj)
1334 : {
1335 : /*
1336 : * Add the 'length' property for a newly created or converted slow array,
1337 : * and update the elements to be an empty array owned by the object.
1338 : * The shared emptyObjectElements singleton cannot be used for slow arrays,
1339 : * as accesses to 'length' will use the elements header.
1340 : */
1341 :
1342 234531 : const jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
1343 234531 : JS_ASSERT(!obj->nativeLookup(cx, lengthId));
1344 :
1345 234531 : if (!obj->allocateSlowArrayElements(cx))
1346 0 : return false;
1347 :
1348 : return obj->addProperty(cx, lengthId, array_length_getter, array_length_setter,
1349 234531 : SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0);
1350 : }
1351 :
1352 : /*
1353 : * Convert an array object from fast-and-dense to slow-and-flexible.
1354 : */
1355 : JSBool
1356 4735 : JSObject::makeDenseArraySlow(JSContext *cx)
1357 : {
1358 4735 : JS_ASSERT(isDenseArray());
1359 :
1360 : MarkTypeObjectFlags(cx, this,
1361 : OBJECT_FLAG_NON_PACKED_ARRAY |
1362 4735 : OBJECT_FLAG_NON_DENSE_ARRAY);
1363 :
1364 4735 : uint32_t arrayCapacity = getDenseArrayCapacity();
1365 4735 : uint32_t arrayInitialized = getDenseArrayInitializedLength();
1366 :
1367 : /*
1368 : * Get an allocated array of the existing elements, evicting from the fixed
1369 : * slots if necessary.
1370 : */
1371 4735 : if (!hasDynamicElements()) {
1372 4330 : if (!growElements(cx, arrayCapacity))
1373 0 : return false;
1374 4330 : JS_ASSERT(hasDynamicElements());
1375 : }
1376 :
1377 : /*
1378 : * Save old map now, before calling InitScopeForObject. We'll have to undo
1379 : * on error. This is gross, but a better way is not obvious. Note: the
1380 : * exact contents of the array are not preserved on error.
1381 : */
1382 4735 : js::Shape *oldShape = lastProperty();
1383 :
1384 : /* Create a native scope. */
1385 4735 : gc::AllocKind kind = getAllocKind();
1386 : Shape *shape = EmptyShape::getInitialShape(cx, &SlowArrayClass, getProto(),
1387 4735 : oldShape->getObjectParent(), kind);
1388 4735 : if (!shape)
1389 0 : return false;
1390 4735 : this->shape_ = shape;
1391 :
1392 : /* Take ownership of the dense elements, reset to an empty dense array. */
1393 4735 : HeapSlot *elems = elements;
1394 4735 : elements = emptyObjectElements;
1395 :
1396 : /* Root all values in the array during conversion. */
1397 9470 : AutoValueArray autoArray(cx, (Value *) elems, arrayInitialized);
1398 :
1399 : /*
1400 : * Begin with the length property to share more of the property tree.
1401 : * The getter/setter here will directly access the object's private value.
1402 : */
1403 4735 : if (!AddLengthProperty(cx, this)) {
1404 0 : this->shape_ = oldShape;
1405 0 : if (elements != emptyObjectElements)
1406 0 : cx->free_(getElementsHeader());
1407 0 : elements = elems;
1408 0 : return false;
1409 : }
1410 :
1411 : /*
1412 : * Create new properties pointing to existing elements. Pack the array to
1413 : * remove holes, so that shapes use successive slots (as for other objects).
1414 : */
1415 4735 : uint32_t next = 0;
1416 22135 : for (uint32_t i = 0; i < arrayInitialized; i++) {
1417 : /* Dense array indexes can always fit in a jsid. */
1418 : jsid id;
1419 17400 : JS_ALWAYS_TRUE(ValueToId(cx, Int32Value(i), &id));
1420 :
1421 17400 : if (elems[i].isMagic(JS_ARRAY_HOLE))
1422 11962 : continue;
1423 :
1424 5438 : if (!addDataProperty(cx, id, next, JSPROP_ENUMERATE)) {
1425 0 : this->shape_ = oldShape;
1426 0 : cx->free_(getElementsHeader());
1427 0 : elements = elems;
1428 0 : return false;
1429 : }
1430 :
1431 5438 : initSlot(next, elems[i]);
1432 :
1433 5438 : next++;
1434 : }
1435 :
1436 4735 : ObjectElements *oldheader = ObjectElements::fromElements(elems);
1437 :
1438 4735 : getElementsHeader()->length = oldheader->length;
1439 4735 : cx->free_(oldheader);
1440 :
1441 4735 : return true;
1442 : }
1443 :
1444 : #if JS_HAS_TOSOURCE
1445 : class ArraySharpDetector
1446 : {
1447 : JSContext *cx;
1448 : bool success;
1449 : bool alreadySeen;
1450 : bool sharp;
1451 :
1452 : public:
1453 144 : ArraySharpDetector(JSContext *cx)
1454 : : cx(cx),
1455 : success(false),
1456 : alreadySeen(false),
1457 144 : sharp(false)
1458 144 : {}
1459 :
1460 144 : bool init(JSObject *obj) {
1461 144 : success = js_EnterSharpObject(cx, obj, NULL, &alreadySeen, &sharp);
1462 144 : if (!success)
1463 0 : return false;
1464 144 : return true;
1465 : }
1466 :
1467 144 : bool initiallySharp() const {
1468 144 : JS_ASSERT_IF(sharp, alreadySeen);
1469 144 : return sharp;
1470 : }
1471 :
1472 144 : ~ArraySharpDetector() {
1473 144 : if (success && !sharp)
1474 144 : js_LeaveSharpObject(cx, NULL);
1475 144 : }
1476 : };
1477 :
1478 : static JSBool
1479 207 : array_toSource(JSContext *cx, unsigned argc, Value *vp)
1480 : {
1481 207 : JS_CHECK_RECURSION(cx, return false);
1482 :
1483 207 : CallArgs args = CallArgsFromVp(argc, vp);
1484 207 : JSObject *obj = ToObject(cx, &args.thisv());
1485 207 : if (!obj)
1486 0 : return false;
1487 207 : if (!obj->isArray())
1488 63 : return HandleNonGenericMethodClassMismatch(cx, args, array_toSource, &ArrayClass);
1489 :
1490 288 : ArraySharpDetector detector(cx);
1491 144 : if (!detector.init(obj))
1492 0 : return false;
1493 :
1494 288 : StringBuffer sb(cx);
1495 :
1496 144 : if (detector.initiallySharp()) {
1497 0 : if (!sb.append("[]"))
1498 0 : return false;
1499 0 : goto make_string;
1500 : }
1501 :
1502 144 : if (!sb.append('['))
1503 0 : return false;
1504 :
1505 : uint32_t length;
1506 144 : if (!js_GetLengthProperty(cx, obj, &length))
1507 0 : return false;
1508 :
1509 882 : for (uint32_t index = 0; index < length; index++) {
1510 : JSBool hole;
1511 : Value elt;
1512 1476 : if (!JS_CHECK_OPERATION_LIMIT(cx) ||
1513 738 : !GetElement(cx, obj, index, &hole, &elt)) {
1514 0 : return false;
1515 : }
1516 :
1517 : /* Get element's character string. */
1518 : JSString *str;
1519 738 : if (hole) {
1520 0 : str = cx->runtime->emptyString;
1521 : } else {
1522 738 : str = js_ValueToSource(cx, elt);
1523 738 : if (!str)
1524 0 : return false;
1525 : }
1526 :
1527 : /* Append element to buffer. */
1528 738 : if (!sb.append(str))
1529 0 : return false;
1530 738 : if (index + 1 != length) {
1531 612 : if (!sb.append(", "))
1532 0 : return false;
1533 126 : } else if (hole) {
1534 0 : if (!sb.append(','))
1535 0 : return false;
1536 : }
1537 : }
1538 :
1539 : /* Finalize the buffer. */
1540 144 : if (!sb.append(']'))
1541 0 : return false;
1542 :
1543 : make_string:
1544 144 : JSString *str = sb.finishString();
1545 144 : if (!str)
1546 0 : return false;
1547 :
1548 144 : args.rval().setString(str);
1549 144 : return true;
1550 : }
1551 : #endif
1552 :
1553 : class AutoArrayCycleDetector
1554 : {
1555 : JSContext *cx;
1556 : JSObject *obj;
1557 : uint32_t genBefore;
1558 : BusyArraysSet::AddPtr hashPointer;
1559 : bool cycle;
1560 : JS_DECL_USE_GUARD_OBJECT_NOTIFIER
1561 :
1562 : public:
1563 125028 : AutoArrayCycleDetector(JSContext *cx, JSObject *obj JS_GUARD_OBJECT_NOTIFIER_PARAM)
1564 : : cx(cx),
1565 : obj(obj),
1566 125028 : cycle(true)
1567 : {
1568 125028 : JS_GUARD_OBJECT_NOTIFIER_INIT;
1569 125028 : }
1570 :
1571 125028 : bool init()
1572 : {
1573 125028 : BusyArraysSet &set = cx->busyArrays;
1574 125028 : hashPointer = set.lookupForAdd(obj);
1575 125028 : if (!hashPointer) {
1576 125028 : if (!set.add(hashPointer, obj))
1577 0 : return false;
1578 125028 : cycle = false;
1579 125028 : genBefore = set.generation();
1580 : }
1581 125028 : return true;
1582 : }
1583 :
1584 125028 : ~AutoArrayCycleDetector()
1585 125028 : {
1586 125028 : if (!cycle) {
1587 125028 : if (genBefore == cx->busyArrays.generation())
1588 124992 : cx->busyArrays.remove(hashPointer);
1589 : else
1590 36 : cx->busyArrays.remove(obj);
1591 : }
1592 125028 : }
1593 :
1594 125028 : bool foundCycle() { return cycle; }
1595 :
1596 : protected:
1597 : };
1598 :
1599 : static JSBool
1600 125028 : array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
1601 : JSString *sepstr, CallArgs &args)
1602 : {
1603 : static const jschar comma = ',';
1604 : const jschar *sep;
1605 : size_t seplen;
1606 125028 : if (sepstr) {
1607 22860 : seplen = sepstr->length();
1608 22860 : sep = sepstr->getChars(cx);
1609 22860 : if (!sep)
1610 0 : return false;
1611 : } else {
1612 102168 : sep = ,
1613 102168 : seplen = 1;
1614 : }
1615 :
1616 250056 : AutoArrayCycleDetector detector(cx, obj);
1617 125028 : if (!detector.init())
1618 0 : return false;
1619 :
1620 125028 : if (detector.foundCycle()) {
1621 0 : args.rval().setString(cx->runtime->atomState.emptyAtom);
1622 0 : return true;
1623 : }
1624 :
1625 : uint32_t length;
1626 125028 : if (!js_GetLengthProperty(cx, obj, &length))
1627 0 : return false;
1628 :
1629 250056 : StringBuffer sb(cx);
1630 :
1631 125028 : if (!locale && !seplen && obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj)) {
1632 17685 : const Value *start = obj->getDenseArrayElements();
1633 17685 : const Value *end = start + obj->getDenseArrayInitializedLength();
1634 : const Value *elem;
1635 626391 : for (elem = start; elem < end; elem++) {
1636 608715 : if (!JS_CHECK_OPERATION_LIMIT(cx))
1637 0 : return false;
1638 :
1639 : /*
1640 : * Object stringifying is slow; delegate it to a separate loop to
1641 : * keep this one tight.
1642 : */
1643 608715 : if (elem->isObject())
1644 9 : break;
1645 :
1646 608706 : if (!elem->isMagic(JS_ARRAY_HOLE) && !elem->isNullOrUndefined()) {
1647 608706 : if (!ValueToStringBuffer(cx, *elem, sb))
1648 0 : return false;
1649 : }
1650 : }
1651 :
1652 17775 : for (uint32_t i = uint32_t(PointerRangeSize(start, elem)); i < length; i++) {
1653 90 : if (!JS_CHECK_OPERATION_LIMIT(cx))
1654 0 : return false;
1655 :
1656 : JSBool hole;
1657 : Value v;
1658 90 : if (!GetElement(cx, obj, i, &hole, &v))
1659 0 : return false;
1660 90 : if (!hole && !v.isNullOrUndefined()) {
1661 90 : if (!ValueToStringBuffer(cx, v, sb))
1662 0 : return false;
1663 : }
1664 : }
1665 : } else {
1666 766521 : for (uint32_t index = 0; index < length; index++) {
1667 659187 : if (!JS_CHECK_OPERATION_LIMIT(cx))
1668 0 : return false;
1669 :
1670 : JSBool hole;
1671 : Value elt;
1672 659187 : if (!GetElement(cx, obj, index, &hole, &elt))
1673 0 : return false;
1674 :
1675 659187 : if (!hole && !elt.isNullOrUndefined()) {
1676 247149 : if (locale) {
1677 0 : JSObject *robj = ToObject(cx, &elt);
1678 0 : if (!robj)
1679 0 : return false;
1680 0 : jsid id = ATOM_TO_JSID(cx->runtime->atomState.toLocaleStringAtom);
1681 0 : if (!robj->callMethod(cx, id, 0, NULL, &elt))
1682 0 : return false;
1683 : }
1684 247149 : if (!ValueToStringBuffer(cx, elt, sb))
1685 9 : return false;
1686 : }
1687 :
1688 659178 : if (index + 1 != length) {
1689 565002 : if (!sb.append(sep, seplen))
1690 0 : return false;
1691 : }
1692 : }
1693 : }
1694 :
1695 125019 : JSString *str = sb.finishString();
1696 125019 : if (!str)
1697 0 : return false;
1698 125019 : args.rval().setString(str);
1699 125019 : return true;
1700 : }
1701 :
1702 : /* ES5 15.4.4.2. NB: The algorithm here differs from the one in ES3. */
1703 : static JSBool
1704 101997 : array_toString(JSContext *cx, unsigned argc, Value *vp)
1705 : {
1706 101997 : JS_CHECK_RECURSION(cx, return false);
1707 :
1708 101997 : CallArgs args = CallArgsFromVp(argc, vp);
1709 101997 : JSObject *obj = ToObject(cx, &args.thisv());
1710 101997 : if (!obj)
1711 0 : return false;
1712 :
1713 101997 : Value join = args.calleev();
1714 101997 : if (!obj->getProperty(cx, cx->runtime->atomState.joinAtom, &join))
1715 0 : return false;
1716 :
1717 101997 : if (!js_IsCallable(join)) {
1718 0 : JSString *str = obj_toStringHelper(cx, obj);
1719 0 : if (!str)
1720 0 : return false;
1721 0 : args.rval().setString(str);
1722 0 : return true;
1723 : }
1724 :
1725 203994 : InvokeArgsGuard ag;
1726 101997 : if (!cx->stack.pushInvokeArgs(cx, 0, &ag))
1727 0 : return false;
1728 :
1729 101997 : ag.calleev() = join;
1730 101997 : ag.thisv().setObject(*obj);
1731 :
1732 : /* Do the call. */
1733 101997 : if (!Invoke(cx, ag))
1734 9 : return false;
1735 101988 : args.rval() = ag.rval();
1736 101988 : return true;
1737 : }
1738 :
1739 : static JSBool
1740 0 : array_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
1741 : {
1742 0 : JS_CHECK_RECURSION(cx, return false);
1743 :
1744 0 : CallArgs args = CallArgsFromVp(argc, vp);
1745 0 : JSObject *obj = ToObject(cx, &args.thisv());
1746 0 : if (!obj)
1747 0 : return false;
1748 :
1749 : /*
1750 : * Passing comma here as the separator. Need a way to get a
1751 : * locale-specific version.
1752 : */
1753 0 : return array_toString_sub(cx, obj, JS_TRUE, NULL, args);
1754 : }
1755 :
1756 : static inline bool
1757 38403 : InitArrayTypes(JSContext *cx, TypeObject *type, const Value *vector, unsigned count)
1758 : {
1759 38403 : if (cx->typeInferenceEnabled() && !type->unknownProperties()) {
1760 22086 : AutoEnterTypeInference enter(cx);
1761 :
1762 11043 : TypeSet *types = type->getProperty(cx, JSID_VOID, true);
1763 11043 : if (!types)
1764 0 : return false;
1765 :
1766 22361 : for (unsigned i = 0; i < count; i++) {
1767 11318 : if (vector[i].isMagic(JS_ARRAY_HOLE))
1768 0 : continue;
1769 11318 : Type valtype = GetValueType(cx, vector[i]);
1770 11318 : types->addType(cx, valtype);
1771 : }
1772 : }
1773 38403 : return true;
1774 : }
1775 :
1776 : enum ShouldUpdateTypes
1777 : {
1778 : UpdateTypes = true,
1779 : DontUpdateTypes = false
1780 : };
1781 :
1782 : static bool
1783 43427 : InitArrayElements(JSContext *cx, JSObject *obj, uint32_t start, uint32_t count, const Value *vector, ShouldUpdateTypes updateTypes)
1784 : {
1785 43427 : JS_ASSERT(count <= MAX_ARRAY_INDEX);
1786 :
1787 43427 : if (count == 0)
1788 18 : return true;
1789 :
1790 43409 : if (updateTypes && !InitArrayTypes(cx, obj->getType(cx), vector, count))
1791 0 : return false;
1792 :
1793 : /*
1794 : * Optimize for dense arrays so long as adding the given set of elements
1795 : * wouldn't otherwise make the array slow.
1796 : */
1797 : do {
1798 43409 : if (!obj->isDenseArray())
1799 414 : break;
1800 42995 : if (js_PrototypeHasIndexedProperties(cx, obj))
1801 0 : break;
1802 :
1803 42995 : JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, start, count);
1804 42995 : if (result != JSObject::ED_OK) {
1805 0 : if (result == JSObject::ED_FAILED)
1806 0 : return false;
1807 0 : JS_ASSERT(result == JSObject::ED_SPARSE);
1808 0 : break;
1809 : }
1810 42995 : uint32_t newlen = start + count;
1811 42995 : if (newlen > obj->getArrayLength())
1812 10082 : obj->setDenseArrayLength(newlen);
1813 :
1814 42995 : JS_ASSERT(count < UINT32_MAX / sizeof(Value));
1815 42995 : obj->copyDenseArrayElements(start, vector, count);
1816 42995 : JS_ASSERT_IF(count != 0, !obj->getDenseArrayElement(newlen - 1).isMagic(JS_ARRAY_HOLE));
1817 42995 : return true;
1818 : } while (false);
1819 :
1820 414 : const Value* end = vector + count;
1821 1251 : while (vector < end && start <= MAX_ARRAY_INDEX) {
1822 846 : if (!JS_CHECK_OPERATION_LIMIT(cx) ||
1823 423 : !SetArrayElement(cx, obj, start++, *vector++)) {
1824 0 : return false;
1825 : }
1826 : }
1827 :
1828 414 : if (vector == end)
1829 414 : return true;
1830 :
1831 : /* Finish out any remaining elements past the max array index. */
1832 0 : if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
1833 0 : return false;
1834 :
1835 0 : JS_ASSERT(start == MAX_ARRAY_INDEX + 1);
1836 0 : AutoValueRooter tvr(cx);
1837 0 : AutoIdRooter idr(cx);
1838 0 : Value idval = DoubleValue(MAX_ARRAY_INDEX + 1);
1839 0 : do {
1840 0 : *tvr.addr() = *vector++;
1841 0 : if (!js_ValueToStringId(cx, idval, idr.addr()) ||
1842 0 : !obj->setGeneric(cx, idr.id(), tvr.addr(), true)) {
1843 0 : return false;
1844 : }
1845 0 : idval.getDoubleRef() += 1;
1846 : } while (vector != end);
1847 :
1848 0 : return true;
1849 : }
1850 :
1851 : #if 0
1852 : static JSBool
1853 : InitArrayObject(JSContext *cx, JSObject *obj, uint32_t length, const Value *vector)
1854 : {
1855 : JS_ASSERT(obj->isArray());
1856 :
1857 : JS_ASSERT(obj->isDenseArray());
1858 : obj->setArrayLength(cx, length);
1859 : if (!vector || !length)
1860 : return true;
1861 :
1862 : if (!InitArrayTypes(cx, obj->getType(cx), vector, length))
1863 : return false;
1864 :
1865 : /* Avoid ensureDenseArrayElements to skip sparse array checks there. */
1866 : if (!obj->ensureElements(cx, length))
1867 : return false;
1868 :
1869 : obj->setDenseArrayInitializedLength(length);
1870 :
1871 : bool hole = false;
1872 : for (uint32_t i = 0; i < length; i++) {
1873 : obj->setDenseArrayElement(i, vector[i]);
1874 : hole |= vector[i].isMagic(JS_ARRAY_HOLE);
1875 : }
1876 : if (hole)
1877 : obj->markDenseArrayNotPacked(cx);
1878 :
1879 : return true;
1880 : }
1881 : #endif
1882 :
1883 : /*
1884 : * Perl-inspired join, reverse, and sort.
1885 : */
1886 : static JSBool
1887 125028 : array_join(JSContext *cx, unsigned argc, Value *vp)
1888 : {
1889 125028 : JS_CHECK_RECURSION(cx, return false);
1890 :
1891 125028 : CallArgs args = CallArgsFromVp(argc, vp);
1892 : JSString *str;
1893 125028 : if (args.hasDefined(0)) {
1894 22860 : str = ToString(cx, args[0]);
1895 22860 : if (!str)
1896 0 : return JS_FALSE;
1897 22860 : args[0].setString(str);
1898 : } else {
1899 102168 : str = NULL;
1900 : }
1901 125028 : JSObject *obj = ToObject(cx, &args.thisv());
1902 125028 : if (!obj)
1903 0 : return false;
1904 125028 : return array_toString_sub(cx, obj, JS_FALSE, str, args);
1905 : }
1906 :
1907 : static JSBool
1908 468 : array_reverse(JSContext *cx, unsigned argc, Value *vp)
1909 : {
1910 468 : CallArgs args = CallArgsFromVp(argc, vp);
1911 468 : JSObject *obj = ToObject(cx, &args.thisv());
1912 468 : if (!obj)
1913 0 : return false;
1914 :
1915 : uint32_t len;
1916 468 : if (!js_GetLengthProperty(cx, obj, &len))
1917 0 : return false;
1918 :
1919 : do {
1920 468 : if (!obj->isDenseArray())
1921 0 : break;
1922 468 : if (js_PrototypeHasIndexedProperties(cx, obj))
1923 0 : break;
1924 :
1925 : /* An empty array or an array with no elements is already reversed. */
1926 468 : if (len == 0 || obj->getDenseArrayCapacity() == 0) {
1927 0 : args.rval().setObject(*obj);
1928 0 : return true;
1929 : }
1930 :
1931 : /*
1932 : * It's actually surprisingly complicated to reverse an array due to the
1933 : * orthogonality of array length and array capacity while handling
1934 : * leading and trailing holes correctly. Reversing seems less likely to
1935 : * be a common operation than other array mass-mutation methods, so for
1936 : * now just take a probably-small memory hit (in the absence of too many
1937 : * holes in the array at its start) and ensure that the capacity is
1938 : * sufficient to hold all the elements in the array if it were full.
1939 : */
1940 468 : JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, len, 0);
1941 468 : if (result != JSObject::ED_OK) {
1942 342 : if (result == JSObject::ED_FAILED)
1943 0 : return false;
1944 342 : JS_ASSERT(result == JSObject::ED_SPARSE);
1945 342 : break;
1946 : }
1947 :
1948 : /* Fill out the array's initialized length to its proper length. */
1949 126 : obj->ensureDenseArrayInitializedLength(cx, len, 0);
1950 :
1951 126 : uint32_t lo = 0, hi = len - 1;
1952 13653 : for (; lo < hi; lo++, hi--) {
1953 13527 : Value origlo = obj->getDenseArrayElement(lo);
1954 13527 : Value orighi = obj->getDenseArrayElement(hi);
1955 13527 : obj->setDenseArrayElement(lo, orighi);
1956 27027 : if (orighi.isMagic(JS_ARRAY_HOLE) &&
1957 13500 : !js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(lo))) {
1958 0 : return false;
1959 : }
1960 13527 : obj->setDenseArrayElement(hi, origlo);
1961 26496 : if (origlo.isMagic(JS_ARRAY_HOLE) &&
1962 12969 : !js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(hi))) {
1963 0 : return false;
1964 : }
1965 : }
1966 :
1967 : /*
1968 : * Per ECMA-262, don't update the length of the array, even if the new
1969 : * array has trailing holes (and thus the original array began with
1970 : * holes).
1971 : */
1972 126 : args.rval().setObject(*obj);
1973 126 : return true;
1974 : } while (false);
1975 :
1976 : Value lowval, hival;
1977 47367 : for (uint32_t i = 0, half = len / 2; i < half; i++) {
1978 : JSBool hole, hole2;
1979 235125 : if (!JS_CHECK_OPERATION_LIMIT(cx) ||
1980 47025 : !GetElement(cx, obj, i, &hole, &lowval) ||
1981 47025 : !GetElement(cx, obj, len - i - 1, &hole2, &hival) ||
1982 47025 : !SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, lowval) ||
1983 47025 : !SetOrDeleteArrayElement(cx, obj, i, hole2, hival)) {
1984 0 : return false;
1985 : }
1986 : }
1987 342 : args.rval().setObject(*obj);
1988 342 : return true;
1989 : }
1990 :
1991 : namespace {
1992 :
1993 : inline bool
1994 61488 : CompareStringValues(JSContext *cx, const Value &a, const Value &b, bool *lessOrEqualp)
1995 : {
1996 61488 : if (!JS_CHECK_OPERATION_LIMIT(cx))
1997 0 : return false;
1998 :
1999 61488 : JSString *astr = a.toString();
2000 61488 : JSString *bstr = b.toString();
2001 : int32_t result;
2002 61488 : if (!CompareStrings(cx, astr, bstr, &result))
2003 0 : return false;
2004 :
2005 61488 : *lessOrEqualp = (result <= 0);
2006 61488 : return true;
2007 : }
2008 :
2009 : static uint32_t const powersOf10[] = {
2010 : 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
2011 : };
2012 :
2013 : static inline unsigned
2014 6180 : NumDigitsBase10(uint32_t n)
2015 : {
2016 : /*
2017 : * This is just floor_log10(n) + 1
2018 : * Algorithm taken from
2019 : * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
2020 : */
2021 : uint32_t log2, t;
2022 6180 : JS_CEILING_LOG2(log2, n);
2023 6180 : t = log2 * 1233 >> 12;
2024 6180 : return t - (n < powersOf10[t]) + 1;
2025 : }
2026 :
2027 : static JS_ALWAYS_INLINE uint32_t
2028 1560 : NegateNegativeInt32(int32_t i)
2029 : {
2030 : /*
2031 : * We cannot simply return '-i' because this is undefined for INT32_MIN.
2032 : * 2s complement does actually give us what we want, however. That is,
2033 : * ~0x80000000 + 1 = 0x80000000 which is correct when interpreted as a
2034 : * uint32_t. To avoid undefined behavior, we write out 2s complement
2035 : * explicitly and rely on the peephole optimizer to generate 'neg'.
2036 : */
2037 1560 : return ~uint32_t(i) + 1;
2038 : }
2039 :
2040 : inline bool
2041 6125 : CompareLexicographicInt32(JSContext *cx, const Value &a, const Value &b, bool *lessOrEqualp)
2042 : {
2043 6125 : int32_t aint = a.toInt32();
2044 6125 : int32_t bint = b.toInt32();
2045 :
2046 : /*
2047 : * If both numbers are equal ... trivial
2048 : * If only one of both is negative --> arithmetic comparison as char code
2049 : * of '-' is always less than any other digit
2050 : * If both numbers are negative convert them to positive and continue
2051 : * handling ...
2052 : */
2053 6125 : if (aint == bint) {
2054 175 : *lessOrEqualp = true;
2055 5950 : } else if ((aint < 0) && (bint >= 0)) {
2056 1430 : *lessOrEqualp = true;
2057 4520 : } else if ((aint >= 0) && (bint < 0)) {
2058 1430 : *lessOrEqualp = false;
2059 : } else {
2060 : uint32_t auint, buint;
2061 3090 : if (aint >= 0) {
2062 2310 : auint = aint;
2063 2310 : buint = bint;
2064 : } else {
2065 780 : auint = NegateNegativeInt32(aint);
2066 780 : buint = NegateNegativeInt32(bint);
2067 : }
2068 :
2069 : /*
2070 : * ... get number of digits of both integers.
2071 : * If they have the same number of digits --> arithmetic comparison.
2072 : * If digits_a > digits_b: a < b*10e(digits_a - digits_b).
2073 : * If digits_b > digits_a: a*10e(digits_b - digits_a) <= b.
2074 : */
2075 3090 : unsigned digitsa = NumDigitsBase10(auint);
2076 3090 : unsigned digitsb = NumDigitsBase10(buint);
2077 3090 : if (digitsa == digitsb)
2078 970 : *lessOrEqualp = (auint <= buint);
2079 2120 : else if (digitsa > digitsb)
2080 1060 : *lessOrEqualp = (uint64_t(auint) < uint64_t(buint) * powersOf10[digitsa - digitsb]);
2081 : else /* if (digitsb > digitsa) */
2082 1060 : *lessOrEqualp = (uint64_t(auint) * powersOf10[digitsb - digitsa] <= uint64_t(buint));
2083 : }
2084 :
2085 6125 : return true;
2086 : }
2087 :
2088 : inline bool
2089 9769 : CompareSubStringValues(JSContext *cx, const jschar *s1, size_t l1,
2090 : const jschar *s2, size_t l2, bool *lessOrEqualp)
2091 : {
2092 9769 : if (!JS_CHECK_OPERATION_LIMIT(cx))
2093 0 : return false;
2094 :
2095 : int32_t result;
2096 9769 : if (!s1 || !s2 || !CompareChars(s1, l1, s2, l2, &result))
2097 0 : return false;
2098 :
2099 9769 : *lessOrEqualp = (result <= 0);
2100 9769 : return true;
2101 : }
2102 :
2103 : struct SortComparatorStrings
2104 : {
2105 : JSContext *const cx;
2106 :
2107 630 : SortComparatorStrings(JSContext *cx)
2108 630 : : cx(cx) {}
2109 :
2110 61488 : bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) {
2111 61488 : return CompareStringValues(cx, a, b, lessOrEqualp);
2112 : }
2113 : };
2114 :
2115 : struct SortComparatorLexicographicInt32
2116 : {
2117 : JSContext *const cx;
2118 :
2119 6134 : SortComparatorLexicographicInt32(JSContext *cx)
2120 6134 : : cx(cx) {}
2121 :
2122 6125 : bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) {
2123 6125 : return CompareLexicographicInt32(cx, a, b, lessOrEqualp);
2124 : }
2125 : };
2126 :
2127 : struct StringifiedElement
2128 39076 : {
2129 : size_t charsBegin;
2130 : size_t charsEnd;
2131 : size_t elementIndex;
2132 : };
2133 :
2134 : struct SortComparatorStringifiedElements
2135 : {
2136 : JSContext *const cx;
2137 : const StringBuffer &sb;
2138 :
2139 9769 : SortComparatorStringifiedElements(JSContext *cx, const StringBuffer &sb)
2140 9769 : : cx(cx), sb(sb) {}
2141 :
2142 9769 : bool operator()(const StringifiedElement &a, const StringifiedElement &b, bool *lessOrEqualp) {
2143 9769 : return CompareSubStringValues(cx, sb.begin() + a.charsBegin, a.charsEnd - a.charsBegin,
2144 9769 : sb.begin() + b.charsBegin, b.charsEnd - b.charsBegin,
2145 29307 : lessOrEqualp);
2146 : }
2147 : };
2148 :
2149 : struct SortComparatorFunction
2150 : {
2151 : JSContext *const cx;
2152 : const Value &fval;
2153 : InvokeArgsGuard &ag;
2154 :
2155 15939 : SortComparatorFunction(JSContext *cx, const Value &fval, InvokeArgsGuard &ag)
2156 15939 : : cx(cx), fval(fval), ag(ag) { }
2157 :
2158 : bool operator()(const Value &a, const Value &b, bool *lessOrEqualp);
2159 : };
2160 :
2161 : bool
2162 253701 : SortComparatorFunction::operator()(const Value &a, const Value &b, bool *lessOrEqualp)
2163 : {
2164 : /*
2165 : * array_sort deals with holes and undefs on its own and they should not
2166 : * come here.
2167 : */
2168 253701 : JS_ASSERT(!a.isMagic() && !a.isUndefined());
2169 253701 : JS_ASSERT(!a.isMagic() && !b.isUndefined());
2170 :
2171 253701 : if (!JS_CHECK_OPERATION_LIMIT(cx))
2172 0 : return false;
2173 :
2174 253701 : if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 2, &ag))
2175 0 : return false;
2176 :
2177 253701 : ag.setCallee(fval);
2178 253701 : ag.thisv() = UndefinedValue();
2179 253701 : ag[0] = a;
2180 253701 : ag[1] = b;
2181 :
2182 253701 : if (!Invoke(cx, ag))
2183 9 : return false;
2184 :
2185 : double cmp;
2186 253692 : if (!ToNumber(cx, ag.rval(), &cmp))
2187 9 : return false;
2188 :
2189 : /*
2190 : * XXX eport some kind of error here if cmp is NaN? ECMA talks about
2191 : * 'consistent compare functions' that don't return NaN, but is silent
2192 : * about what the result should be. So we currently ignore it.
2193 : */
2194 253683 : *lessOrEqualp = (JSDOUBLE_IS_NaN(cmp) || cmp <= 0);
2195 253683 : return true;
2196 : }
2197 :
2198 : } /* namespace anonymous */
2199 :
2200 : JSBool
2201 32607 : js::array_sort(JSContext *cx, unsigned argc, Value *vp)
2202 : {
2203 32607 : CallArgs args = CallArgsFromVp(argc, vp);
2204 : Value fval;
2205 32607 : if (args.hasDefined(0)) {
2206 15939 : if (args[0].isPrimitive()) {
2207 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SORT_ARG);
2208 0 : return false;
2209 : }
2210 15939 : fval = args[0]; /* non-default compare function */
2211 : } else {
2212 16668 : fval.setNull();
2213 : }
2214 :
2215 32607 : JSObject *obj = ToObject(cx, &args.thisv());
2216 32607 : if (!obj)
2217 0 : return false;
2218 :
2219 : uint32_t len;
2220 32607 : if (!js_GetLengthProperty(cx, obj, &len))
2221 0 : return false;
2222 32607 : if (len == 0) {
2223 126 : args.rval().setObject(*obj);
2224 126 : return true;
2225 : }
2226 :
2227 : /*
2228 : * We need a temporary array of 2 * len Value to hold the array elements
2229 : * and the scratch space for merge sort. Check that its size does not
2230 : * overflow size_t, which would allow for indexing beyond the end of the
2231 : * malloc'd vector.
2232 : */
2233 : #if JS_BITS_PER_WORD == 32
2234 32481 : if (size_t(len) > size_t(-1) / (2 * sizeof(Value))) {
2235 0 : js_ReportAllocationOverflow(cx);
2236 0 : return false;
2237 : }
2238 : #endif
2239 :
2240 : /*
2241 : * Initialize vec as a root. We will clear elements of vec one by
2242 : * one while increasing the rooted amount of vec when we know that the
2243 : * property at the corresponding index exists and its value must be rooted.
2244 : *
2245 : * In this way when sorting a huge mostly sparse array we will not
2246 : * access the tail of vec corresponding to properties that do not
2247 : * exist, allowing OS to avoiding committing RAM. See bug 330812.
2248 : */
2249 : size_t n, undefs;
2250 : {
2251 64962 : AutoValueVector vec(cx);
2252 32481 : if (!vec.reserve(2 * size_t(len)))
2253 0 : return false;
2254 :
2255 : /*
2256 : * By ECMA 262, 15.4.4.11, a property that does not exist (which we
2257 : * call a "hole") is always greater than an existing property with
2258 : * value undefined and that is always greater than any other property.
2259 : * Thus to sort holes and undefs we simply count them, sort the rest
2260 : * of elements, append undefs after them and then make holes after
2261 : * undefs.
2262 : */
2263 32481 : undefs = 0;
2264 32481 : bool allStrings = true;
2265 32481 : bool allInts = true;
2266 129852 : for (uint32_t i = 0; i < len; i++) {
2267 97371 : if (!JS_CHECK_OPERATION_LIMIT(cx))
2268 0 : return false;
2269 :
2270 : /* Clear vec[newlen] before including it in the rooted set. */
2271 : JSBool hole;
2272 : Value v;
2273 97371 : if (!GetElement(cx, obj, i, &hole, &v))
2274 0 : return false;
2275 97371 : if (hole)
2276 0 : continue;
2277 97371 : if (v.isUndefined()) {
2278 18 : ++undefs;
2279 18 : continue;
2280 : }
2281 97353 : vec.infallibleAppend(v);
2282 97353 : allStrings = allStrings && v.isString();
2283 97353 : allInts = allInts && v.isInt32();
2284 : }
2285 :
2286 32481 : n = vec.length();
2287 32481 : if (n == 0) {
2288 9 : args.rval().setObject(*obj);
2289 9 : return true; /* The array has only holes and undefs. */
2290 : }
2291 :
2292 32472 : JS_ALWAYS_TRUE(vec.resize(n * 2));
2293 :
2294 : /* Here len == n + undefs + number_of_holes. */
2295 32472 : Value *result = vec.begin();
2296 32472 : if (fval.isNull()) {
2297 : /*
2298 : * Sort using the default comparator converting all elements to
2299 : * strings.
2300 : */
2301 16533 : if (allStrings) {
2302 630 : if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorStrings(cx)))
2303 0 : return false;
2304 15903 : } else if (allInts) {
2305 12268 : if (!MergeSort(vec.begin(), n, vec.begin() + n,
2306 12268 : SortComparatorLexicographicInt32(cx))) {
2307 0 : return false;
2308 : }
2309 : } else {
2310 : /*
2311 : * Convert all elements to a jschar array in StringBuffer.
2312 : * Store the index and length of each stringified element with
2313 : * the corresponding index of the element in the array. Sort
2314 : * the stringified elements and with this result order the
2315 : * original array.
2316 : */
2317 19538 : StringBuffer sb(cx);
2318 19538 : Vector<StringifiedElement, 0, TempAllocPolicy> strElements(cx);
2319 9769 : if (!strElements.reserve(2 * n))
2320 0 : return false;
2321 :
2322 9769 : int cursor = 0;
2323 29307 : for (size_t i = 0; i < n; i++) {
2324 19538 : if (!JS_CHECK_OPERATION_LIMIT(cx))
2325 0 : return false;
2326 :
2327 19538 : if (!ValueToStringBuffer(cx, vec[i], sb))
2328 0 : return false;
2329 :
2330 19538 : StringifiedElement el = { cursor, sb.length(), i };
2331 19538 : strElements.infallibleAppend(el);
2332 19538 : cursor = sb.length();
2333 : }
2334 :
2335 : /* Resize strElements so we can perform the sorting */
2336 9769 : JS_ALWAYS_TRUE(strElements.resize(2 * n));
2337 :
2338 19538 : if (!MergeSort(strElements.begin(), n, strElements.begin() + n,
2339 19538 : SortComparatorStringifiedElements(cx, sb))) {
2340 0 : return false;
2341 : }
2342 :
2343 : /* Order vec[n:2n-1] using strElements.index */
2344 29307 : for (size_t i = 0; i < n; i ++)
2345 19538 : vec[n + i] = vec[strElements[i].elementIndex];
2346 :
2347 19538 : result = vec.begin() + n;
2348 : }
2349 : } else {
2350 31878 : InvokeArgsGuard args;
2351 31878 : if (!MergeSort(vec.begin(), n, vec.begin() + n,
2352 31878 : SortComparatorFunction(cx, fval, args))) {
2353 18 : return false;
2354 : }
2355 : }
2356 :
2357 32454 : if (!InitArrayElements(cx, obj, 0, uint32_t(n), result, DontUpdateTypes))
2358 0 : return false;
2359 : }
2360 :
2361 : /* Set undefs that sorted after the rest of elements. */
2362 64908 : while (undefs != 0) {
2363 0 : --undefs;
2364 0 : if (!JS_CHECK_OPERATION_LIMIT(cx) || !SetArrayElement(cx, obj, n++, UndefinedValue()))
2365 0 : return false;
2366 : }
2367 :
2368 : /* Re-create any holes that sorted to the end of the array. */
2369 64908 : while (len > n) {
2370 0 : if (!JS_CHECK_OPERATION_LIMIT(cx) || DeleteArrayElement(cx, obj, --len, true) < 0)
2371 0 : return false;
2372 : }
2373 32454 : args.rval().setObject(*obj);
2374 32454 : return true;
2375 : }
2376 :
2377 : /*
2378 : * Perl-inspired push, pop, shift, unshift, and splice methods.
2379 : */
2380 : static bool
2381 10487 : array_push_slowly(JSContext *cx, JSObject *obj, CallArgs &args)
2382 : {
2383 : uint32_t length;
2384 :
2385 10487 : if (!js_GetLengthProperty(cx, obj, &length))
2386 0 : return false;
2387 10487 : if (!InitArrayElements(cx, obj, length, args.length(), args.array(), UpdateTypes))
2388 0 : return false;
2389 :
2390 : /* Per ECMA-262, return the new array length. */
2391 10487 : double newlength = length + double(args.length());
2392 10487 : args.rval().setNumber(newlength);
2393 10487 : return js_SetLengthProperty(cx, obj, newlength);
2394 : }
2395 :
2396 : static bool
2397 5076433 : array_push1_dense(JSContext* cx, JSObject* obj, CallArgs &args)
2398 : {
2399 5076433 : JS_ASSERT(args.length() == 1);
2400 :
2401 5076433 : uint32_t length = obj->getArrayLength();
2402 5076433 : JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, 1);
2403 5076433 : if (result != JSObject::ED_OK) {
2404 351 : if (result == JSObject::ED_FAILED)
2405 0 : return false;
2406 351 : JS_ASSERT(result == JSObject::ED_SPARSE);
2407 351 : if (!obj->makeDenseArraySlow(cx))
2408 0 : return false;
2409 351 : return array_push_slowly(cx, obj, args);
2410 : }
2411 :
2412 5076082 : obj->setDenseArrayLength(length + 1);
2413 5076082 : obj->setDenseArrayElementWithType(cx, length, args[0]);
2414 5076082 : args.rval().setNumber(obj->getArrayLength());
2415 5076082 : return true;
2416 : }
2417 :
2418 : JS_ALWAYS_INLINE JSBool
2419 487065 : NewbornArrayPushImpl(JSContext *cx, JSObject *obj, const Value &v)
2420 : {
2421 487065 : JS_ASSERT(!v.isMagic());
2422 :
2423 487065 : uint32_t length = obj->getArrayLength();
2424 487065 : if (obj->isSlowArray()) {
2425 : /* This can happen in one evil case. See bug 630377. */
2426 : jsid id;
2427 0 : return IndexToId(cx, length, &id) &&
2428 0 : js_DefineProperty(cx, obj, id, &v, NULL, NULL, JSPROP_ENUMERATE);
2429 : }
2430 :
2431 487065 : JS_ASSERT(obj->isDenseArray());
2432 487065 : JS_ASSERT(length <= obj->getDenseArrayCapacity());
2433 :
2434 487065 : if (!obj->ensureElements(cx, length + 1))
2435 0 : return false;
2436 :
2437 487065 : obj->setDenseArrayInitializedLength(length + 1);
2438 487065 : obj->setDenseArrayLength(length + 1);
2439 487065 : obj->initDenseArrayElementWithType(cx, length, v);
2440 487065 : return true;
2441 : }
2442 :
2443 : JSBool
2444 487065 : js_NewbornArrayPush(JSContext *cx, JSObject *obj, const Value &vp)
2445 : {
2446 487065 : return NewbornArrayPushImpl(cx, obj, vp);
2447 : }
2448 :
2449 : JSBool
2450 5086569 : js::array_push(JSContext *cx, unsigned argc, Value *vp)
2451 : {
2452 5086569 : CallArgs args = CallArgsFromVp(argc, vp);
2453 5086569 : JSObject *obj = ToObject(cx, &args.thisv());
2454 5086569 : if (!obj)
2455 0 : return false;
2456 :
2457 : /* Insist on one argument and obj of the expected class. */
2458 5086569 : if (args.length() != 1 || !obj->isDenseArray())
2459 10136 : return array_push_slowly(cx, obj, args);
2460 :
2461 5076433 : return array_push1_dense(cx, obj, args);
2462 : }
2463 :
2464 : static JSBool
2465 45 : array_pop_slowly(JSContext *cx, JSObject* obj, CallArgs &args)
2466 : {
2467 : uint32_t index;
2468 45 : if (!js_GetLengthProperty(cx, obj, &index))
2469 0 : return false;
2470 :
2471 45 : if (index == 0) {
2472 9 : args.rval().setUndefined();
2473 9 : return js_SetLengthProperty(cx, obj, index);
2474 : }
2475 :
2476 36 : index--;
2477 :
2478 : JSBool hole;
2479 : Value elt;
2480 36 : if (!GetElement(cx, obj, index, &hole, &elt))
2481 0 : return false;
2482 :
2483 36 : if (!hole && DeleteArrayElement(cx, obj, index, true) < 0)
2484 0 : return false;
2485 :
2486 36 : args.rval() = elt;
2487 36 : return js_SetLengthProperty(cx, obj, index);
2488 : }
2489 :
2490 : static JSBool
2491 8189 : array_pop_dense(JSContext *cx, JSObject* obj, CallArgs &args)
2492 : {
2493 8189 : uint32_t index = obj->getArrayLength();
2494 8189 : if (index == 0) {
2495 420 : args.rval().setUndefined();
2496 420 : return JS_TRUE;
2497 : }
2498 :
2499 7769 : index--;
2500 :
2501 : JSBool hole;
2502 : Value elt;
2503 7769 : if (!GetElement(cx, obj, index, &hole, &elt))
2504 0 : return JS_FALSE;
2505 :
2506 7769 : if (!hole && DeleteArrayElement(cx, obj, index, true) < 0)
2507 0 : return JS_FALSE;
2508 7769 : if (obj->getDenseArrayInitializedLength() > index)
2509 7742 : obj->setDenseArrayInitializedLength(index);
2510 :
2511 7769 : obj->setArrayLength(cx, index);
2512 :
2513 7769 : args.rval() = elt;
2514 7769 : return JS_TRUE;
2515 : }
2516 :
2517 : JSBool
2518 8234 : js::array_pop(JSContext *cx, unsigned argc, Value *vp)
2519 : {
2520 8234 : CallArgs args = CallArgsFromVp(argc, vp);
2521 8234 : JSObject *obj = ToObject(cx, &args.thisv());
2522 8234 : if (!obj)
2523 0 : return false;
2524 8234 : if (obj->isDenseArray())
2525 8189 : return array_pop_dense(cx, obj, args);
2526 45 : return array_pop_slowly(cx, obj, args);
2527 : }
2528 :
2529 : #ifdef JS_METHODJIT
2530 : void JS_FASTCALL
2531 66 : mjit::stubs::ArrayShift(VMFrame &f)
2532 : {
2533 66 : JSObject *obj = &f.regs.sp[-1].toObject();
2534 66 : JS_ASSERT(obj->isDenseArray());
2535 :
2536 : /*
2537 : * At this point the length and initialized length have already been
2538 : * decremented and the result fetched, so just shift the array elements
2539 : * themselves.
2540 : */
2541 66 : uint32_t initlen = obj->getDenseArrayInitializedLength();
2542 66 : obj->moveDenseArrayElementsUnbarriered(0, 1, initlen);
2543 66 : }
2544 : #endif /* JS_METHODJIT */
2545 :
2546 : JSBool
2547 1613 : js::array_shift(JSContext *cx, unsigned argc, Value *vp)
2548 : {
2549 1613 : CallArgs args = CallArgsFromVp(argc, vp);
2550 1613 : JSObject *obj = ToObject(cx, &args.thisv());
2551 1613 : if (!obj)
2552 0 : return JS_FALSE;
2553 :
2554 : uint32_t length;
2555 1613 : if (!js_GetLengthProperty(cx, obj, &length))
2556 0 : return JS_FALSE;
2557 :
2558 1613 : if (length == 0) {
2559 428 : args.rval().setUndefined();
2560 : } else {
2561 1185 : length--;
2562 :
2563 3546 : if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
2564 1185 : length < obj->getDenseArrayCapacity() &&
2565 1176 : 0 < obj->getDenseArrayInitializedLength()) {
2566 1176 : args.rval() = obj->getDenseArrayElement(0);
2567 1176 : if (args.rval().isMagic(JS_ARRAY_HOLE))
2568 27 : args.rval().setUndefined();
2569 1176 : obj->moveDenseArrayElements(0, 1, obj->getDenseArrayInitializedLength() - 1);
2570 1176 : obj->setDenseArrayInitializedLength(obj->getDenseArrayInitializedLength() - 1);
2571 1176 : obj->setArrayLength(cx, length);
2572 1176 : if (!js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(length)))
2573 0 : return JS_FALSE;
2574 1176 : return JS_TRUE;
2575 : }
2576 :
2577 : JSBool hole;
2578 9 : if (!GetElement(cx, obj, 0u, &hole, &args.rval()))
2579 0 : return JS_FALSE;
2580 :
2581 : /* Slide down the array above the first element. */
2582 18 : AutoValueRooter tvr(cx);
2583 63 : for (uint32_t i = 0; i < length; i++) {
2584 162 : if (!JS_CHECK_OPERATION_LIMIT(cx) ||
2585 54 : !GetElement(cx, obj, i + 1, &hole, tvr.addr()) ||
2586 54 : !SetOrDeleteArrayElement(cx, obj, i, hole, tvr.value())) {
2587 0 : return JS_FALSE;
2588 : }
2589 : }
2590 :
2591 : /* Delete the only or last element when it exists. */
2592 9 : if (!hole && DeleteArrayElement(cx, obj, length, true) < 0)
2593 0 : return JS_FALSE;
2594 : }
2595 437 : return js_SetLengthProperty(cx, obj, length);
2596 : }
2597 :
2598 : static JSBool
2599 486 : array_unshift(JSContext *cx, unsigned argc, Value *vp)
2600 : {
2601 486 : CallArgs args = CallArgsFromVp(argc, vp);
2602 486 : JSObject *obj = ToObject(cx, &args.thisv());
2603 486 : if (!obj)
2604 0 : return false;
2605 :
2606 : uint32_t length;
2607 486 : if (!js_GetLengthProperty(cx, obj, &length))
2608 0 : return JS_FALSE;
2609 :
2610 486 : double newlen = length;
2611 486 : if (args.length() > 0) {
2612 : /* Slide up the array to make room for all args at the bottom. */
2613 486 : if (length > 0) {
2614 459 : bool optimized = false;
2615 : do {
2616 459 : if (!obj->isDenseArray())
2617 0 : break;
2618 459 : if (js_PrototypeHasIndexedProperties(cx, obj))
2619 0 : break;
2620 459 : JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, args.length());
2621 459 : if (result != JSObject::ED_OK) {
2622 351 : if (result == JSObject::ED_FAILED)
2623 0 : return false;
2624 351 : JS_ASSERT(result == JSObject::ED_SPARSE);
2625 351 : break;
2626 : }
2627 108 : obj->moveDenseArrayElements(args.length(), 0, length);
2628 216 : for (uint32_t i = 0; i < args.length(); i++)
2629 108 : obj->setDenseArrayElement(i, MagicValue(JS_ARRAY_HOLE));
2630 108 : optimized = true;
2631 : } while (false);
2632 :
2633 459 : if (!optimized) {
2634 351 : double last = length;
2635 351 : double upperIndex = last + args.length();
2636 702 : AutoValueRooter tvr(cx);
2637 96525 : do {
2638 96525 : --last, --upperIndex;
2639 : JSBool hole;
2640 289575 : if (!JS_CHECK_OPERATION_LIMIT(cx) ||
2641 96525 : !GetElement(cx, obj, last, &hole, tvr.addr()) ||
2642 96525 : !SetOrDeleteArrayElement(cx, obj, upperIndex, hole, tvr.value())) {
2643 0 : return JS_FALSE;
2644 : }
2645 : } while (last != 0);
2646 : }
2647 : }
2648 :
2649 : /* Copy from args to the bottom of the array. */
2650 486 : if (!InitArrayElements(cx, obj, 0, args.length(), args.array(), UpdateTypes))
2651 0 : return JS_FALSE;
2652 :
2653 486 : newlen += args.length();
2654 : }
2655 486 : if (!js_SetLengthProperty(cx, obj, newlen))
2656 0 : return JS_FALSE;
2657 :
2658 : /* Follow Perl by returning the new array length. */
2659 486 : args.rval().setNumber(newlen);
2660 486 : return JS_TRUE;
2661 : }
2662 :
2663 : static inline void
2664 4377 : TryReuseArrayType(JSObject *obj, JSObject *nobj)
2665 : {
2666 : /*
2667 : * Try to change the type of a newly created array nobj to the same type
2668 : * as obj. This can only be performed if the original object is an array
2669 : * and has the same prototype.
2670 : */
2671 4377 : JS_ASSERT(nobj->isDenseArray());
2672 4377 : JS_ASSERT(nobj->getProto()->hasNewType(nobj->type()));
2673 :
2674 4377 : if (obj->isArray() && !obj->hasSingletonType() && obj->getProto() == nobj->getProto())
2675 3531 : nobj->setType(obj->type());
2676 4377 : }
2677 :
2678 : /*
2679 : * Returns true if this is a dense array whose |count| properties starting from
2680 : * |startingIndex| may be accessed (get, set, delete) directly through its
2681 : * contiguous vector of elements without fear of getters, setters, etc. along
2682 : * the prototype chain, or of enumerators requiring notification of
2683 : * modifications.
2684 : */
2685 : static inline bool
2686 1503 : CanOptimizeForDenseStorage(JSObject *arr, uint32_t startingIndex, uint32_t count, JSContext *cx)
2687 : {
2688 : /* If the desired properties overflow dense storage, we can't optimize. */
2689 1503 : if (UINT32_MAX - startingIndex < count)
2690 0 : return false;
2691 :
2692 : /* There's no optimizing possible if it's not a dense array. */
2693 1503 : if (!arr->isDenseArray())
2694 684 : return false;
2695 :
2696 : /*
2697 : * Don't optimize if the array might be in the midst of iteration. We
2698 : * rely on this to be able to safely move dense array elements around with
2699 : * just a memmove (see JSObject::moveDenseArrayElements), without worrying
2700 : * about updating any in-progress enumerators for properties implicitly
2701 : * deleted if a hole is moved from one location to another location not yet
2702 : * visited. See bug 690622.
2703 : *
2704 : * Another potential wrinkle: what if the enumeration is happening on an
2705 : * object which merely has |arr| on its prototype chain? It turns out this
2706 : * case can't happen, because any dense array used as the prototype of
2707 : * another object is first slowified, for type inference's sake.
2708 : */
2709 819 : if (JS_UNLIKELY(arr->getType(cx)->hasAllFlags(OBJECT_FLAG_ITERATED)))
2710 404 : return false;
2711 :
2712 : /* Now just watch out for getters and setters along the prototype chain. */
2713 415 : return !js_PrototypeHasIndexedProperties(cx, arr) &&
2714 415 : startingIndex + count <= arr->getDenseArrayInitializedLength();
2715 : }
2716 :
2717 : /* ES5 15.4.4.12. */
2718 : static JSBool
2719 783 : array_splice(JSContext *cx, unsigned argc, Value *vp)
2720 : {
2721 783 : CallArgs args = CallArgsFromVp(argc, vp);
2722 :
2723 : /* Step 1. */
2724 783 : JSObject *obj = ToObject(cx, &args.thisv());
2725 783 : if (!obj)
2726 0 : return false;
2727 :
2728 : /* Steps 3-4. */
2729 : uint32_t len;
2730 783 : if (!js_GetLengthProperty(cx, obj, &len))
2731 18 : return false;
2732 :
2733 : /* Step 5. */
2734 : double relativeStart;
2735 765 : if (!ToInteger(cx, argc >= 1 ? args[0] : UndefinedValue(), &relativeStart))
2736 0 : return false;
2737 :
2738 : /* Step 6. */
2739 : uint32_t actualStart;
2740 765 : if (relativeStart < 0)
2741 0 : actualStart = JS_MAX(len + relativeStart, 0);
2742 : else
2743 765 : actualStart = JS_MIN(relativeStart, len);
2744 :
2745 : /* Step 7. */
2746 : uint32_t actualDeleteCount;
2747 765 : if (argc != 1) {
2748 : double deleteCountDouble;
2749 756 : if (!ToInteger(cx, argc >= 2 ? args[1] : Int32Value(0), &deleteCountDouble))
2750 0 : return false;
2751 756 : actualDeleteCount = JS_MIN(JS_MAX(deleteCountDouble, 0), len - actualStart);
2752 : } else {
2753 : /*
2754 : * Non-standard: if start was specified but deleteCount was omitted,
2755 : * delete to the end of the array. See bug 668024 for discussion.
2756 : */
2757 9 : actualDeleteCount = len - actualStart;
2758 : }
2759 :
2760 765 : JS_ASSERT(len - actualStart >= actualDeleteCount);
2761 :
2762 : /* Steps 2, 8-9. */
2763 : JSObject *arr;
2764 765 : if (CanOptimizeForDenseStorage(obj, actualStart, actualDeleteCount, cx)) {
2765 : arr = NewDenseCopiedArray(cx, actualDeleteCount,
2766 307 : obj->getDenseArrayElements() + actualStart);
2767 307 : if (!arr)
2768 0 : return false;
2769 307 : TryReuseArrayType(obj, arr);
2770 : } else {
2771 458 : arr = NewDenseAllocatedArray(cx, actualDeleteCount);
2772 458 : if (!arr)
2773 0 : return false;
2774 458 : TryReuseArrayType(obj, arr);
2775 :
2776 1129 : for (uint32_t k = 0; k < actualDeleteCount; k++) {
2777 : JSBool hole;
2778 : Value fromValue;
2779 2704 : if (!JS_CHECK_OPERATION_LIMIT(cx) ||
2780 689 : !GetElement(cx, obj, actualStart + k, &hole, &fromValue) ||
2781 1326 : (!hole && !arr->defineElement(cx, k, fromValue)))
2782 : {
2783 18 : return false;
2784 : }
2785 : }
2786 : }
2787 :
2788 : /* Step 11. */
2789 747 : uint32_t itemCount = (argc >= 2) ? (argc - 2) : 0;
2790 :
2791 747 : if (itemCount < actualDeleteCount) {
2792 : /* Step 12: the array is being shrunk. */
2793 225 : uint32_t sourceIndex = actualStart + actualDeleteCount;
2794 225 : uint32_t targetIndex = actualStart + itemCount;
2795 225 : uint32_t finalLength = len - actualDeleteCount + itemCount;
2796 :
2797 225 : if (CanOptimizeForDenseStorage(obj, 0, len, cx)) {
2798 : /* Steps 12(a)-(b). */
2799 52 : obj->moveDenseArrayElements(targetIndex, sourceIndex, len - sourceIndex);
2800 :
2801 : /*
2802 : * Update the initialized length. Do so before shrinking so that we
2803 : * can apply the write barrier to the old slots.
2804 : */
2805 52 : if (cx->typeInferenceEnabled())
2806 52 : obj->setDenseArrayInitializedLength(finalLength);
2807 :
2808 : /* Steps 12(c)-(d). */
2809 52 : obj->shrinkElements(cx, finalLength);
2810 :
2811 : /* Fix running enumerators for the deleted items. */
2812 52 : if (!js_SuppressDeletedElements(cx, obj, finalLength, len))
2813 0 : return false;
2814 : } else {
2815 : /*
2816 : * This is all very slow if the length is very large. We don't yet
2817 : * have the ability to iterate in sorted order, so we just do the
2818 : * pessimistic thing and let JS_CHECK_OPERATION_LIMIT handle the
2819 : * fallout.
2820 : */
2821 :
2822 : /* Steps 12(a)-(b). */
2823 569 : for (uint32_t from = sourceIndex, to = targetIndex; from < len; from++, to++) {
2824 : JSBool hole;
2825 : Value fromValue;
2826 1278 : if (!JS_CHECK_OPERATION_LIMIT(cx) ||
2827 432 : !GetElement(cx, obj, from, &hole, &fromValue) ||
2828 414 : !SetOrDeleteArrayElement(cx, obj, to, hole, fromValue))
2829 : {
2830 36 : return false;
2831 : }
2832 : }
2833 :
2834 : /* Steps 12(c)-(d). */
2835 439 : for (uint32_t k = len; k > finalLength; k--) {
2836 320 : if (DeleteArrayElement(cx, obj, k - 1, true) < 0)
2837 18 : return false;
2838 : }
2839 : }
2840 522 : } else if (itemCount > actualDeleteCount) {
2841 : /* Step 13. */
2842 :
2843 : /*
2844 : * Optimize only if the array is already dense and we can extend it to
2845 : * its new length.
2846 : */
2847 513 : if (obj->isDenseArray()) {
2848 : JSObject::EnsureDenseResult res =
2849 : obj->ensureDenseArrayElements(cx, obj->getArrayLength(),
2850 459 : itemCount - actualDeleteCount);
2851 459 : if (res == JSObject::ED_FAILED)
2852 0 : return false;
2853 :
2854 459 : if (res == JSObject::ED_SPARSE) {
2855 369 : if (!obj->makeDenseArraySlow(cx))
2856 0 : return false;
2857 : } else {
2858 90 : JS_ASSERT(res == JSObject::ED_OK);
2859 : }
2860 : }
2861 :
2862 513 : if (CanOptimizeForDenseStorage(obj, len, itemCount - actualDeleteCount, cx)) {
2863 : obj->moveDenseArrayElements(actualStart + itemCount,
2864 : actualStart + actualDeleteCount,
2865 50 : len - (actualStart + actualDeleteCount));
2866 :
2867 50 : if (cx->typeInferenceEnabled())
2868 50 : obj->setDenseArrayInitializedLength(len + itemCount - actualDeleteCount);
2869 : } else {
2870 110222 : for (double k = len - actualDeleteCount; k > actualStart; k--) {
2871 109795 : double from = k + actualDeleteCount - 1;
2872 109795 : double to = k + itemCount - 1;
2873 :
2874 : JSBool hole;
2875 : Value fromValue;
2876 329367 : if (!JS_CHECK_OPERATION_LIMIT(cx) ||
2877 109795 : !GetElement(cx, obj, from, &hole, &fromValue) ||
2878 109777 : !SetOrDeleteArrayElement(cx, obj, to, hole, fromValue))
2879 : {
2880 36 : return false;
2881 : }
2882 : }
2883 : }
2884 : }
2885 :
2886 : /* Step 10. */
2887 657 : Value *items = args.array() + 2;
2888 :
2889 : /* Steps 14-15. */
2890 2124 : for (uint32_t k = actualStart, i = 0; i < itemCount; i++, k++) {
2891 1476 : if (!SetArrayElement(cx, obj, k, items[i]))
2892 9 : return false;
2893 : }
2894 :
2895 : /* Step 16. */
2896 648 : double finalLength = double(len) - actualDeleteCount + itemCount;
2897 648 : if (!js_SetLengthProperty(cx, obj, finalLength))
2898 0 : return false;
2899 :
2900 : /* Step 17. */
2901 648 : args.rval().setObject(*arr);
2902 648 : return true;
2903 : }
2904 :
2905 : #ifdef JS_METHODJIT
2906 : void JS_FASTCALL
2907 114 : mjit::stubs::ArrayConcatTwoArrays(VMFrame &f)
2908 : {
2909 114 : JSObject *result = &f.regs.sp[-3].toObject();
2910 114 : JSObject *obj1 = &f.regs.sp[-2].toObject();
2911 114 : JSObject *obj2 = &f.regs.sp[-1].toObject();
2912 :
2913 114 : JS_ASSERT(result->isDenseArray() && obj1->isDenseArray() && obj2->isDenseArray());
2914 :
2915 114 : uint32_t initlen1 = obj1->getDenseArrayInitializedLength();
2916 114 : JS_ASSERT(initlen1 == obj1->getArrayLength());
2917 :
2918 114 : uint32_t initlen2 = obj2->getDenseArrayInitializedLength();
2919 114 : JS_ASSERT(initlen2 == obj2->getArrayLength());
2920 :
2921 : /* No overflow here due to nslots limit. */
2922 114 : uint32_t len = initlen1 + initlen2;
2923 :
2924 114 : if (!result->ensureElements(f.cx, len))
2925 0 : THROW();
2926 :
2927 114 : JS_ASSERT(!result->getDenseArrayInitializedLength());
2928 114 : result->setDenseArrayInitializedLength(len);
2929 :
2930 114 : result->initDenseArrayElements(0, obj1->getDenseArrayElements(), initlen1);
2931 114 : result->initDenseArrayElements(initlen1, obj2->getDenseArrayElements(), initlen2);
2932 :
2933 114 : result->setDenseArrayLength(len);
2934 : }
2935 : #endif /* JS_METHODJIT */
2936 :
2937 : /*
2938 : * Python-esque sequence operations.
2939 : */
2940 : JSBool
2941 561 : js::array_concat(JSContext *cx, unsigned argc, Value *vp)
2942 : {
2943 : /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */
2944 561 : Value *p = JS_ARGV(cx, vp) - 1;
2945 :
2946 : /* Create a new Array object and root it using *vp. */
2947 561 : JSObject *aobj = ToObject(cx, &vp[1]);
2948 561 : if (!aobj)
2949 0 : return false;
2950 :
2951 : JSObject *nobj;
2952 : uint32_t length;
2953 561 : if (aobj->isDenseArray()) {
2954 561 : length = aobj->getArrayLength();
2955 561 : const Value *vector = aobj->getDenseArrayElements();
2956 561 : uint32_t initlen = aobj->getDenseArrayInitializedLength();
2957 561 : nobj = NewDenseCopiedArray(cx, initlen, vector);
2958 561 : if (!nobj)
2959 0 : return JS_FALSE;
2960 561 : TryReuseArrayType(aobj, nobj);
2961 561 : nobj->setArrayLength(cx, length);
2962 561 : vp->setObject(*nobj);
2963 561 : if (argc == 0)
2964 18 : return JS_TRUE;
2965 543 : argc--;
2966 543 : p++;
2967 : } else {
2968 0 : nobj = NewDenseEmptyArray(cx);
2969 0 : if (!nobj)
2970 0 : return JS_FALSE;
2971 0 : vp->setObject(*nobj);
2972 0 : length = 0;
2973 : }
2974 :
2975 : /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */
2976 1122 : for (unsigned i = 0; i <= argc; i++) {
2977 579 : if (!JS_CHECK_OPERATION_LIMIT(cx))
2978 0 : return false;
2979 579 : const Value &v = p[i];
2980 579 : if (v.isObject()) {
2981 579 : JSObject &obj = v.toObject();
2982 579 : if (ObjectClassIs(obj, ESClass_Array, cx)) {
2983 : uint32_t alength;
2984 552 : if (!js_GetLengthProperty(cx, &obj, &alength))
2985 0 : return false;
2986 3723 : for (uint32_t slot = 0; slot < alength; slot++) {
2987 : JSBool hole;
2988 : Value tmp;
2989 3171 : if (!JS_CHECK_OPERATION_LIMIT(cx) || !GetElement(cx, &obj, slot, &hole, &tmp))
2990 0 : return false;
2991 :
2992 : /*
2993 : * Per ECMA 262, 15.4.4.4, step 9, ignore nonexistent
2994 : * properties.
2995 : */
2996 3171 : if (!hole && !SetArrayElement(cx, nobj, length + slot, tmp))
2997 0 : return false;
2998 : }
2999 552 : length += alength;
3000 552 : continue;
3001 : }
3002 : }
3003 :
3004 27 : if (!SetArrayElement(cx, nobj, length, v))
3005 0 : return false;
3006 27 : length++;
3007 : }
3008 :
3009 543 : return js_SetLengthProperty(cx, nobj, length);
3010 : }
3011 :
3012 : static JSBool
3013 3051 : array_slice(JSContext *cx, unsigned argc, Value *vp)
3014 : {
3015 : JSObject *nobj;
3016 : uint32_t length, begin, end, slot;
3017 : JSBool hole;
3018 :
3019 3051 : CallArgs args = CallArgsFromVp(argc, vp);
3020 :
3021 3051 : JSObject *obj = ToObject(cx, &args.thisv());
3022 3051 : if (!obj)
3023 0 : return false;
3024 :
3025 3051 : if (!js_GetLengthProperty(cx, obj, &length))
3026 0 : return JS_FALSE;
3027 3051 : begin = 0;
3028 3051 : end = length;
3029 :
3030 3051 : if (args.length() > 0) {
3031 : double d;
3032 3033 : if (!ToInteger(cx, args[0], &d))
3033 0 : return false;
3034 3033 : if (d < 0) {
3035 0 : d += length;
3036 0 : if (d < 0)
3037 0 : d = 0;
3038 3033 : } else if (d > length) {
3039 0 : d = length;
3040 : }
3041 3033 : begin = (uint32_t)d;
3042 :
3043 3033 : if (args.hasDefined(1)) {
3044 36 : if (!ToInteger(cx, args[1], &d))
3045 0 : return false;
3046 36 : if (d < 0) {
3047 0 : d += length;
3048 0 : if (d < 0)
3049 0 : d = 0;
3050 36 : } else if (d > length) {
3051 0 : d = length;
3052 : }
3053 36 : end = (uint32_t)d;
3054 : }
3055 : }
3056 :
3057 3051 : if (begin > end)
3058 0 : begin = end;
3059 :
3060 5247 : if (obj->isDenseArray() && end <= obj->getDenseArrayInitializedLength() &&
3061 2196 : !js_PrototypeHasIndexedProperties(cx, obj)) {
3062 2196 : nobj = NewDenseCopiedArray(cx, end - begin, obj->getDenseArrayElements() + begin);
3063 2196 : if (!nobj)
3064 0 : return JS_FALSE;
3065 2196 : TryReuseArrayType(obj, nobj);
3066 2196 : args.rval().setObject(*nobj);
3067 2196 : return JS_TRUE;
3068 : }
3069 :
3070 855 : nobj = NewDenseAllocatedArray(cx, end - begin);
3071 855 : if (!nobj)
3072 0 : return JS_FALSE;
3073 855 : TryReuseArrayType(obj, nobj);
3074 :
3075 1710 : AutoValueRooter tvr(cx);
3076 2646 : for (slot = begin; slot < end; slot++) {
3077 3582 : if (!JS_CHECK_OPERATION_LIMIT(cx) ||
3078 1791 : !GetElement(cx, obj, slot, &hole, tvr.addr())) {
3079 0 : return JS_FALSE;
3080 : }
3081 1791 : if (!hole && !SetArrayElement(cx, nobj, slot - begin, tvr.value()))
3082 0 : return JS_FALSE;
3083 : }
3084 :
3085 855 : args.rval().setObject(*nobj);
3086 855 : return JS_TRUE;
3087 : }
3088 :
3089 : enum IndexOfKind {
3090 : IndexOf,
3091 : LastIndexOf
3092 : };
3093 :
3094 : static JSBool
3095 15480 : array_indexOfHelper(JSContext *cx, IndexOfKind mode, CallArgs &args)
3096 : {
3097 : uint32_t length, i, stop;
3098 : Value tosearch;
3099 : int direction;
3100 : JSBool hole;
3101 :
3102 15480 : JSObject *obj = ToObject(cx, &args.thisv());
3103 15480 : if (!obj)
3104 0 : return false;
3105 15480 : if (!js_GetLengthProperty(cx, obj, &length))
3106 0 : return JS_FALSE;
3107 15480 : if (length == 0)
3108 198 : goto not_found;
3109 :
3110 15282 : if (args.length() <= 1) {
3111 15282 : i = (mode == LastIndexOf) ? length - 1 : 0;
3112 15282 : tosearch = (args.length() != 0) ? args[0] : UndefinedValue();
3113 : } else {
3114 : double start;
3115 :
3116 0 : tosearch = args[0];
3117 0 : if (!ToInteger(cx, args[1], &start))
3118 0 : return false;
3119 0 : if (start < 0) {
3120 0 : start += length;
3121 0 : if (start < 0) {
3122 0 : if (mode == LastIndexOf)
3123 0 : goto not_found;
3124 0 : i = 0;
3125 : } else {
3126 0 : i = (uint32_t)start;
3127 : }
3128 0 : } else if (start >= length) {
3129 0 : if (mode == IndexOf)
3130 0 : goto not_found;
3131 0 : i = length - 1;
3132 : } else {
3133 0 : i = (uint32_t)start;
3134 : }
3135 : }
3136 :
3137 15282 : if (mode == LastIndexOf) {
3138 0 : stop = 0;
3139 0 : direction = -1;
3140 : } else {
3141 15282 : stop = length - 1;
3142 15282 : direction = 1;
3143 : }
3144 :
3145 459234 : for (;;) {
3146 : Value elt;
3147 949032 : if (!JS_CHECK_OPERATION_LIMIT(cx) ||
3148 474516 : !GetElement(cx, obj, (uint32_t)i, &hole, &elt)) {
3149 0 : return JS_FALSE;
3150 : }
3151 474516 : if (!hole) {
3152 : bool equal;
3153 474516 : if (!StrictlyEqual(cx, elt, tosearch, &equal))
3154 0 : return false;
3155 474516 : if (equal) {
3156 4311 : args.rval().setNumber(i);
3157 4311 : return true;
3158 : }
3159 : }
3160 470205 : if (i == stop)
3161 10971 : goto not_found;
3162 459234 : i += direction;
3163 : }
3164 :
3165 : not_found:
3166 11169 : args.rval().setInt32(-1);
3167 11169 : return JS_TRUE;
3168 : }
3169 :
3170 : static JSBool
3171 15480 : array_indexOf(JSContext *cx, unsigned argc, Value *vp)
3172 : {
3173 15480 : CallArgs args = CallArgsFromVp(argc, vp);
3174 15480 : return array_indexOfHelper(cx, IndexOf, args);
3175 : }
3176 :
3177 : static JSBool
3178 0 : array_lastIndexOf(JSContext *cx, unsigned argc, Value *vp)
3179 : {
3180 0 : CallArgs args = CallArgsFromVp(argc, vp);
3181 0 : return array_indexOfHelper(cx, LastIndexOf, args);
3182 : }
3183 :
3184 : /* ECMA 15.4.4.16-15.4.4.18. */
3185 : class ArrayForEachBehavior
3186 : {
3187 : public:
3188 2142 : static bool shouldExit(Value &callval, Value *rval) { return false; }
3189 972 : static Value lateExitValue() { return UndefinedValue(); }
3190 : };
3191 :
3192 : class ArrayEveryBehavior
3193 : {
3194 : public:
3195 315 : static bool shouldExit(Value &callval, Value *rval)
3196 : {
3197 315 : if (!js_ValueToBoolean(callval)) {
3198 0 : *rval = BooleanValue(false);
3199 0 : return true;
3200 : }
3201 315 : return false;
3202 : }
3203 9 : static Value lateExitValue() { return BooleanValue(true); }
3204 : };
3205 :
3206 : class ArraySomeBehavior
3207 : {
3208 : public:
3209 9473 : static bool shouldExit(Value &callval, Value *rval)
3210 : {
3211 9473 : if (js_ValueToBoolean(callval)) {
3212 9 : *rval = BooleanValue(true);
3213 9 : return true;
3214 : }
3215 9464 : return false;
3216 : }
3217 9464 : static Value lateExitValue() { return BooleanValue(false); }
3218 : };
3219 :
3220 : template <class Behavior>
3221 : static inline bool
3222 15509 : array_readonlyCommon(JSContext *cx, CallArgs &args)
3223 : {
3224 : /* Step 1. */
3225 15509 : JSObject *obj = ToObject(cx, &args.thisv());
3226 15509 : if (!obj)
3227 0 : return false;
3228 :
3229 : /* Step 2-3. */
3230 : uint32_t len;
3231 15509 : if (!js_GetLengthProperty(cx, obj, &len))
3232 0 : return false;
3233 :
3234 : /* Step 4. */
3235 15509 : if (args.length() == 0) {
3236 0 : js_ReportMissingArg(cx, args.calleev(), 0);
3237 0 : return false;
3238 : }
3239 15509 : JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
3240 15509 : if (!callable)
3241 0 : return false;
3242 :
3243 : /* Step 5. */
3244 15509 : Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
3245 :
3246 : /* Step 6. */
3247 15509 : uint32_t k = 0;
3248 :
3249 : /* Step 7. */
3250 31018 : InvokeArgsGuard ag;
3251 42939 : while (k < len) {
3252 16985 : if (!JS_CHECK_OPERATION_LIMIT(cx))
3253 0 : return false;
3254 :
3255 : /* Step a, b, and c.i. */
3256 : Value kValue;
3257 : JSBool kNotPresent;
3258 16985 : if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
3259 0 : return false;
3260 :
3261 : /* Step c.ii-iii. */
3262 16985 : if (!kNotPresent) {
3263 16985 : if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag))
3264 0 : return false;
3265 16985 : ag.setCallee(ObjectValue(*callable));
3266 16985 : ag.thisv() = thisv;
3267 16985 : ag[0] = kValue;
3268 16985 : ag[1] = NumberValue(k);
3269 16985 : ag[2] = ObjectValue(*obj);
3270 16985 : if (!Invoke(cx, ag))
3271 5055 : return false;
3272 :
3273 11930 : if (Behavior::shouldExit(ag.rval(), &args.rval()))
3274 9 : return true;
3275 : }
3276 :
3277 : /* Step d. */
3278 11921 : k++;
3279 : }
3280 :
3281 : /* Step 8. */
3282 10445 : args.rval() = Behavior::lateExitValue();
3283 10445 : return true;
3284 : }
3285 :
3286 : /* ES5 15.4.4.16. */
3287 : static JSBool
3288 9 : array_every(JSContext *cx, unsigned argc, Value *vp)
3289 : {
3290 9 : CallArgs args = CallArgsFromVp(argc, vp);
3291 9 : return array_readonlyCommon<ArrayEveryBehavior>(cx, args);
3292 : }
3293 :
3294 : /* ES5 15.4.4.17. */
3295 : static JSBool
3296 14528 : array_some(JSContext *cx, unsigned argc, Value *vp)
3297 : {
3298 14528 : CallArgs args = CallArgsFromVp(argc, vp);
3299 14528 : return array_readonlyCommon<ArraySomeBehavior>(cx, args);
3300 : }
3301 :
3302 : /* ES5 15.4.4.18. */
3303 : static JSBool
3304 972 : array_forEach(JSContext *cx, unsigned argc, Value *vp)
3305 : {
3306 972 : CallArgs args = CallArgsFromVp(argc, vp);
3307 972 : return array_readonlyCommon<ArrayForEachBehavior>(cx, args);
3308 : }
3309 :
3310 : /* ES5 15.4.4.19. */
3311 : static JSBool
3312 1251 : array_map(JSContext *cx, unsigned argc, Value *vp)
3313 : {
3314 1251 : CallArgs args = CallArgsFromVp(argc, vp);
3315 :
3316 : /* Step 1. */
3317 1251 : JSObject *obj = ToObject(cx, &args.thisv());
3318 1251 : if (!obj)
3319 0 : return false;
3320 :
3321 : /* Step 2-3. */
3322 : uint32_t len;
3323 1251 : if (!js_GetLengthProperty(cx, obj, &len))
3324 0 : return false;
3325 :
3326 : /* Step 4. */
3327 1251 : if (args.length() == 0) {
3328 0 : js_ReportMissingArg(cx, args.calleev(), 0);
3329 0 : return false;
3330 : }
3331 1251 : JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
3332 1251 : if (!callable)
3333 0 : return false;
3334 :
3335 : /* Step 5. */
3336 1251 : Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
3337 :
3338 : /* Step 6. */
3339 1251 : JSObject *arr = NewDenseAllocatedArray(cx, len);
3340 1251 : if (!arr)
3341 0 : return false;
3342 1251 : TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array);
3343 1251 : if (!newtype)
3344 0 : return false;
3345 1251 : arr->setType(newtype);
3346 :
3347 : /* Step 7. */
3348 1251 : uint32_t k = 0;
3349 :
3350 : /* Step 8. */
3351 2502 : InvokeArgsGuard ag;
3352 6723 : while (k < len) {
3353 4230 : if (!JS_CHECK_OPERATION_LIMIT(cx))
3354 0 : return false;
3355 :
3356 : /* Step a, b, and c.i. */
3357 : JSBool kNotPresent;
3358 : Value kValue;
3359 4230 : if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
3360 0 : return false;
3361 :
3362 : /* Step c.ii-iii. */
3363 4230 : if (!kNotPresent) {
3364 4230 : if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag))
3365 0 : return false;
3366 4230 : ag.setCallee(ObjectValue(*callable));
3367 4230 : ag.thisv() = thisv;
3368 4230 : ag[0] = kValue;
3369 4230 : ag[1] = NumberValue(k);
3370 4230 : ag[2] = ObjectValue(*obj);
3371 4230 : if (!Invoke(cx, ag))
3372 9 : return false;
3373 4221 : if(!SetArrayElement(cx, arr, k, ag.rval()))
3374 0 : return false;
3375 : }
3376 :
3377 : /* Step d. */
3378 4221 : k++;
3379 : }
3380 :
3381 : /* Step 9. */
3382 1242 : args.rval().setObject(*arr);
3383 1242 : return true;
3384 : }
3385 :
3386 : /* ES5 15.4.4.20. */
3387 : static JSBool
3388 567 : array_filter(JSContext *cx, unsigned argc, Value *vp)
3389 : {
3390 567 : CallArgs args = CallArgsFromVp(argc, vp);
3391 :
3392 : /* Step 1. */
3393 567 : JSObject *obj = ToObject(cx, &args.thisv());
3394 567 : if (!obj)
3395 0 : return false;
3396 :
3397 : /* Step 2-3. */
3398 : uint32_t len;
3399 567 : if (!js_GetLengthProperty(cx, obj, &len))
3400 0 : return false;
3401 :
3402 : /* Step 4. */
3403 567 : if (args.length() == 0) {
3404 0 : js_ReportMissingArg(cx, args.calleev(), 0);
3405 0 : return false;
3406 : }
3407 567 : JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
3408 567 : if (!callable)
3409 0 : return false;
3410 :
3411 : /* Step 5. */
3412 567 : Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
3413 :
3414 : /* Step 6. */
3415 567 : JSObject *arr = NewDenseAllocatedArray(cx, 0);
3416 567 : if (!arr)
3417 0 : return false;
3418 567 : TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array);
3419 567 : if (!newtype)
3420 0 : return false;
3421 567 : arr->setType(newtype);
3422 :
3423 : /* Step 7. */
3424 567 : uint32_t k = 0;
3425 :
3426 : /* Step 8. */
3427 567 : uint32_t to = 0;
3428 :
3429 : /* Step 9. */
3430 1134 : InvokeArgsGuard ag;
3431 5994 : while (k < len) {
3432 4860 : if (!JS_CHECK_OPERATION_LIMIT(cx))
3433 0 : return false;
3434 :
3435 : /* Step a, b, and c.i. */
3436 : JSBool kNotPresent;
3437 : Value kValue;
3438 4860 : if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
3439 0 : return false;
3440 :
3441 : /* Step c.ii-iii. */
3442 4860 : if (!kNotPresent) {
3443 4860 : if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag))
3444 0 : return false;
3445 4860 : ag.setCallee(ObjectValue(*callable));
3446 4860 : ag.thisv() = thisv;
3447 4860 : ag[0] = kValue;
3448 4860 : ag[1] = NumberValue(k);
3449 4860 : ag[2] = ObjectValue(*obj);
3450 4860 : if (!Invoke(cx, ag))
3451 0 : return false;
3452 :
3453 4860 : if (js_ValueToBoolean(ag.rval())) {
3454 936 : if(!SetArrayElement(cx, arr, to, kValue))
3455 0 : return false;
3456 936 : to++;
3457 : }
3458 : }
3459 :
3460 : /* Step d. */
3461 4860 : k++;
3462 : }
3463 :
3464 : /* Step 10. */
3465 567 : args.rval().setObject(*arr);
3466 567 : return true;
3467 : }
3468 :
3469 : /* ES5 15.4.4.21-15.4.4.22. */
3470 : class ArrayReduceBehavior
3471 : {
3472 : public:
3473 0 : static void initialize(uint32_t len, uint32_t *start, uint32_t *end, int32_t *step)
3474 : {
3475 0 : *start = 0;
3476 0 : *step = 1;
3477 0 : *end = len;
3478 0 : }
3479 : };
3480 :
3481 : class ArrayReduceRightBehavior
3482 : {
3483 : public:
3484 0 : static void initialize(uint32_t len, uint32_t *start, uint32_t *end, int32_t *step)
3485 : {
3486 0 : *start = len - 1;
3487 0 : *step = -1;
3488 : /*
3489 : * We rely on (well defined) unsigned integer underflow to check our
3490 : * end condition after visiting the full range (including 0).
3491 : */
3492 0 : *end = UINT32_MAX;
3493 0 : }
3494 : };
3495 :
3496 : template<class Behavior>
3497 : static inline bool
3498 0 : array_reduceCommon(JSContext *cx, CallArgs &args)
3499 : {
3500 : /* Step 1. */
3501 0 : JSObject *obj = ToObject(cx, &args.thisv());
3502 0 : if (!obj)
3503 0 : return false;
3504 :
3505 : /* Step 2-3. */
3506 : uint32_t len;
3507 0 : if (!js_GetLengthProperty(cx, obj, &len))
3508 0 : return false;
3509 :
3510 : /* Step 4. */
3511 0 : if (args.length() == 0) {
3512 0 : js_ReportMissingArg(cx, args.calleev(), 0);
3513 0 : return false;
3514 : }
3515 0 : JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
3516 0 : if (!callable)
3517 0 : return false;
3518 :
3519 : /* Step 5. */
3520 0 : if (len == 0 && args.length() < 2) {
3521 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_EMPTY_ARRAY_REDUCE);
3522 0 : return false;
3523 : }
3524 :
3525 : /* Step 6. */
3526 : uint32_t k, end;
3527 : int32_t step;
3528 0 : Behavior::initialize(len, &k, &end, &step);
3529 :
3530 : /* Step 7-8. */
3531 : Value accumulator;
3532 0 : if (args.length() >= 2) {
3533 0 : accumulator = args[1];
3534 : } else {
3535 0 : JSBool kNotPresent = true;
3536 0 : while (kNotPresent && k != end) {
3537 0 : if (!GetElement(cx, obj, k, &kNotPresent, &accumulator))
3538 0 : return false;
3539 0 : k += step;
3540 : }
3541 0 : if (kNotPresent) {
3542 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_EMPTY_ARRAY_REDUCE);
3543 0 : return false;
3544 : }
3545 : }
3546 :
3547 : /* Step 9. */
3548 0 : InvokeArgsGuard ag;
3549 0 : while (k != end) {
3550 0 : if (!JS_CHECK_OPERATION_LIMIT(cx))
3551 0 : return false;
3552 :
3553 : /* Step a, b, and c.i. */
3554 : JSBool kNotPresent;
3555 : Value kValue;
3556 0 : if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
3557 0 : return false;
3558 :
3559 : /* Step c.ii. */
3560 0 : if (!kNotPresent) {
3561 0 : if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 4, &ag))
3562 0 : return false;
3563 0 : ag.setCallee(ObjectValue(*callable));
3564 0 : ag.thisv() = UndefinedValue();
3565 0 : ag[0] = accumulator;
3566 0 : ag[1] = kValue;
3567 0 : ag[2] = NumberValue(k);
3568 0 : ag[3] = ObjectValue(*obj);
3569 0 : if (!Invoke(cx, ag))
3570 0 : return false;
3571 0 : accumulator = ag.rval();
3572 : }
3573 :
3574 : /* Step d. */
3575 0 : k += step;
3576 : }
3577 :
3578 : /* Step 10. */
3579 0 : args.rval() = accumulator;
3580 0 : return true;
3581 : }
3582 :
3583 : /* ES5 15.4.4.21. */
3584 : static JSBool
3585 0 : array_reduce(JSContext *cx, unsigned argc, Value *vp)
3586 : {
3587 0 : CallArgs args = CallArgsFromVp(argc, vp);
3588 0 : return array_reduceCommon<ArrayReduceBehavior>(cx, args);
3589 : }
3590 :
3591 : /* ES5 15.4.4.22. */
3592 : static JSBool
3593 0 : array_reduceRight(JSContext *cx, unsigned argc, Value *vp)
3594 : {
3595 0 : CallArgs args = CallArgsFromVp(argc, vp);
3596 0 : return array_reduceCommon<ArrayReduceRightBehavior>(cx, args);
3597 : }
3598 :
3599 : static JSBool
3600 270 : array_isArray(JSContext *cx, unsigned argc, Value *vp)
3601 : {
3602 270 : CallArgs args = CallArgsFromVp(argc, vp);
3603 270 : bool isArray = args.length() > 0 && IsObjectWithClass(args[0], ESClass_Array, cx);
3604 270 : args.rval().setBoolean(isArray);
3605 270 : return true;
3606 : }
3607 :
3608 : #define GENERIC JSFUN_GENERIC_NATIVE
3609 :
3610 : static JSFunctionSpec array_methods[] = {
3611 : #if JS_HAS_TOSOURCE
3612 : JS_FN(js_toSource_str, array_toSource, 0,0),
3613 : #endif
3614 : JS_FN(js_toString_str, array_toString, 0,0),
3615 : JS_FN(js_toLocaleString_str,array_toLocaleString,0,0),
3616 :
3617 : /* Perl-ish methods. */
3618 : JS_FN("join", array_join, 1,JSFUN_GENERIC_NATIVE),
3619 : JS_FN("reverse", array_reverse, 0,JSFUN_GENERIC_NATIVE),
3620 : JS_FN("sort", array_sort, 1,JSFUN_GENERIC_NATIVE),
3621 : JS_FN("push", array_push, 1,JSFUN_GENERIC_NATIVE),
3622 : JS_FN("pop", array_pop, 0,JSFUN_GENERIC_NATIVE),
3623 : JS_FN("shift", array_shift, 0,JSFUN_GENERIC_NATIVE),
3624 : JS_FN("unshift", array_unshift, 1,JSFUN_GENERIC_NATIVE),
3625 : JS_FN("splice", array_splice, 2,JSFUN_GENERIC_NATIVE),
3626 :
3627 : /* Pythonic sequence methods. */
3628 : JS_FN("concat", array_concat, 1,JSFUN_GENERIC_NATIVE),
3629 : JS_FN("slice", array_slice, 2,JSFUN_GENERIC_NATIVE),
3630 :
3631 : JS_FN("indexOf", array_indexOf, 1,JSFUN_GENERIC_NATIVE),
3632 : JS_FN("lastIndexOf", array_lastIndexOf, 1,JSFUN_GENERIC_NATIVE),
3633 : JS_FN("forEach", array_forEach, 1,JSFUN_GENERIC_NATIVE),
3634 : JS_FN("map", array_map, 1,JSFUN_GENERIC_NATIVE),
3635 : JS_FN("reduce", array_reduce, 1,JSFUN_GENERIC_NATIVE),
3636 : JS_FN("reduceRight", array_reduceRight, 1,JSFUN_GENERIC_NATIVE),
3637 : JS_FN("filter", array_filter, 1,JSFUN_GENERIC_NATIVE),
3638 : JS_FN("some", array_some, 1,JSFUN_GENERIC_NATIVE),
3639 : JS_FN("every", array_every, 1,JSFUN_GENERIC_NATIVE),
3640 :
3641 : JS_FS_END
3642 : };
3643 :
3644 : static JSFunctionSpec array_static_methods[] = {
3645 : JS_FN("isArray", array_isArray, 1,0),
3646 : JS_FS_END
3647 : };
3648 :
3649 : /* ES5 15.4.2 */
3650 : JSBool
3651 76076 : js_Array(JSContext *cx, unsigned argc, Value *vp)
3652 : {
3653 76076 : CallArgs args = CallArgsFromVp(argc, vp);
3654 76076 : TypeObject *type = GetTypeCallerInitObject(cx, JSProto_Array);
3655 76076 : if (!type)
3656 0 : return JS_FALSE;
3657 :
3658 76076 : if (args.length() != 1 || !args[0].isNumber()) {
3659 27448 : if (!InitArrayTypes(cx, type, args.array(), args.length()))
3660 0 : return false;
3661 27448 : JSObject *obj = (args.length() == 0)
3662 : ? NewDenseEmptyArray(cx)
3663 27448 : : NewDenseCopiedArray(cx, args.length(), args.array());
3664 27448 : if (!obj)
3665 0 : return false;
3666 27448 : obj->setType(type);
3667 27448 : args.rval().setObject(*obj);
3668 27448 : return true;
3669 : }
3670 :
3671 : uint32_t length;
3672 48628 : if (args[0].isInt32()) {
3673 48574 : int32_t i = args[0].toInt32();
3674 48574 : if (i < 0) {
3675 9 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
3676 9 : return false;
3677 : }
3678 48565 : length = uint32_t(i);
3679 : } else {
3680 54 : double d = args[0].toDouble();
3681 54 : length = js_DoubleToECMAUint32(d);
3682 54 : if (d != double(length)) {
3683 36 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
3684 36 : return false;
3685 : }
3686 : }
3687 :
3688 48583 : JSObject *obj = NewDenseUnallocatedArray(cx, length);
3689 48583 : if (!obj)
3690 0 : return false;
3691 :
3692 48583 : obj->setType(type);
3693 :
3694 : /* If the length calculation overflowed, make sure that is marked for the new type. */
3695 48583 : if (obj->getArrayLength() > INT32_MAX)
3696 18 : obj->setArrayLength(cx, obj->getArrayLength());
3697 :
3698 48583 : args.rval().setObject(*obj);
3699 48583 : return true;
3700 : }
3701 :
3702 : JSObject *
3703 19345 : js_InitArrayClass(JSContext *cx, JSObject *obj)
3704 : {
3705 19345 : JS_ASSERT(obj->isNative());
3706 :
3707 38690 : RootedVar<GlobalObject*> global(cx);
3708 19345 : global = &obj->asGlobal();
3709 :
3710 38690 : RootedVarObject arrayProto(cx);
3711 19345 : arrayProto = global->createBlankPrototype(cx, &SlowArrayClass);
3712 19345 : if (!arrayProto || !AddLengthProperty(cx, arrayProto))
3713 0 : return NULL;
3714 19345 : arrayProto->setArrayLength(cx, 0);
3715 :
3716 38690 : RootedVarFunction ctor(cx);
3717 19345 : ctor = global->createConstructor(cx, js_Array, CLASS_ATOM(cx, Array), 1);
3718 19345 : if (!ctor)
3719 0 : return NULL;
3720 :
3721 : /*
3722 : * The default 'new' type of Array.prototype is required by type inference
3723 : * to have unknown properties, to simplify handling of e.g. heterogenous
3724 : * arrays in JSON and script literals and allows setDenseArrayElement to
3725 : * be used without updating the indexed type set for such default arrays.
3726 : */
3727 19345 : if (!arrayProto->setNewTypeUnknown(cx))
3728 0 : return NULL;
3729 :
3730 19345 : if (!LinkConstructorAndPrototype(cx, ctor, arrayProto))
3731 0 : return NULL;
3732 :
3733 38690 : if (!DefinePropertiesAndBrand(cx, arrayProto, NULL, array_methods) ||
3734 19345 : !DefinePropertiesAndBrand(cx, ctor, NULL, array_static_methods))
3735 : {
3736 0 : return NULL;
3737 : }
3738 :
3739 19345 : if (!DefineConstructorAndPrototype(cx, global, JSProto_Array, ctor, arrayProto))
3740 0 : return NULL;
3741 :
3742 19345 : return arrayProto;
3743 : }
3744 :
3745 : /*
3746 : * Array allocation functions.
3747 : */
3748 : namespace js {
3749 :
3750 : static inline bool
3751 2106373 : EnsureNewArrayElements(JSContext *cx, JSObject *obj, uint32_t length)
3752 : {
3753 : /*
3754 : * If ensureElements creates dynamically allocated slots, then having
3755 : * fixedSlots is a waste.
3756 : */
3757 4212746 : DebugOnly<uint32_t> cap = obj->getDenseArrayCapacity();
3758 :
3759 2106373 : if (!obj->ensureElements(cx, length))
3760 0 : return false;
3761 :
3762 2106373 : JS_ASSERT_IF(cap, !obj->hasDynamicElements());
3763 :
3764 2106373 : return true;
3765 : }
3766 :
3767 : template<bool allocateCapacity>
3768 : static JS_ALWAYS_INLINE JSObject *
3769 2266237 : NewArray(JSContext *cx, uint32_t length, JSObject *proto)
3770 : {
3771 2266237 : gc::AllocKind kind = GuessArrayGCKind(length);
3772 :
3773 : #ifdef JS_THREADSAFE
3774 2266237 : JS_ASSERT(CanBeFinalizedInBackground(kind, &ArrayClass));
3775 2266237 : kind = GetBackgroundAllocKind(kind);
3776 : #endif
3777 :
3778 2266237 : GlobalObject *parent = GetCurrentGlobal(cx);
3779 :
3780 2266237 : NewObjectCache &cache = cx->compartment->newObjectCache;
3781 :
3782 2266237 : NewObjectCache::EntryIndex entry = -1;
3783 2266237 : if (cache.lookupGlobal(&ArrayClass, parent, kind, &entry)) {
3784 2045720 : JSObject *obj = cache.newObjectFromHit(cx, entry);
3785 2045720 : if (!obj)
3786 0 : return NULL;
3787 : /* Fixup the elements pointer and length, which may be incorrect. */
3788 2045720 : obj->setFixedElements();
3789 2045720 : obj->setArrayLength(cx, length);
3790 1887507 : if (allocateCapacity && !EnsureNewArrayElements(cx, obj, length))
3791 0 : return NULL;
3792 2045720 : return obj;
3793 : }
3794 :
3795 441034 : Root<GlobalObject*> parentRoot(cx, &parent);
3796 :
3797 220517 : if (!proto && !FindProto(cx, &ArrayClass, parentRoot, &proto))
3798 0 : return NULL;
3799 :
3800 441034 : RootObject protoRoot(cx, &proto);
3801 441034 : RootedVarTypeObject type(cx);
3802 :
3803 220517 : type = proto->getNewType(cx);
3804 220517 : if (!type)
3805 0 : return NULL;
3806 :
3807 : /*
3808 : * Get a shape with zero fixed slots, regardless of the size class.
3809 : * See JSObject::createDenseArray.
3810 : */
3811 441034 : RootedVarShape shape(cx);
3812 220517 : shape = EmptyShape::getInitialShape(cx, &ArrayClass, proto,
3813 : parent, gc::FINALIZE_OBJECT0);
3814 220517 : if (!shape)
3815 0 : return NULL;
3816 :
3817 220517 : JSObject* obj = JSObject::createDenseArray(cx, kind, shape, type, length);
3818 220517 : if (!obj)
3819 0 : return NULL;
3820 :
3821 220517 : if (entry != -1)
3822 220517 : cache.fillGlobal(entry, &ArrayClass, parent, kind, obj);
3823 :
3824 218866 : if (allocateCapacity && !EnsureNewArrayElements(cx, obj, length))
3825 0 : return NULL;
3826 :
3827 220517 : Probes::createObject(cx, obj);
3828 220517 : return obj;
3829 : }
3830 :
3831 : JSObject * JS_FASTCALL
3832 26239 : NewDenseEmptyArray(JSContext *cx, JSObject *proto)
3833 : {
3834 26239 : return NewArray<false>(cx, 0, proto);
3835 : }
3836 :
3837 : JSObject * JS_FASTCALL
3838 2043956 : NewDenseAllocatedArray(JSContext *cx, uint32_t length, JSObject *proto)
3839 : {
3840 2043956 : return NewArray<true>(cx, length, proto);
3841 : }
3842 :
3843 : JSObject * JS_FASTCALL
3844 0 : NewDenseAllocatedEmptyArray(JSContext *cx, uint32_t length, JSObject *proto)
3845 : {
3846 0 : return NewArray<true>(cx, length, proto);
3847 : }
3848 :
3849 : JSObject * JS_FASTCALL
3850 58256 : NewDenseUnallocatedArray(JSContext *cx, uint32_t length, JSObject *proto)
3851 : {
3852 58256 : return NewArray<false>(cx, length, proto);
3853 : }
3854 :
3855 : #ifdef JS_METHODJIT
3856 : JSObject * JS_FASTCALL
3857 75369 : mjit::stubs::NewDenseUnallocatedArray(VMFrame &f, uint32_t length)
3858 : {
3859 75369 : JSObject *proto = (JSObject *) f.scratch;
3860 75369 : JSObject *obj = NewArray<false>(f.cx, length, proto);
3861 75369 : if (!obj)
3862 0 : THROWV(NULL);
3863 :
3864 75369 : return obj;
3865 : }
3866 : #endif
3867 :
3868 : JSObject *
3869 62417 : NewDenseCopiedArray(JSContext *cx, uint32_t length, const Value *vp, JSObject *proto /* = NULL */)
3870 : {
3871 62417 : JSObject* obj = NewArray<true>(cx, length, proto);
3872 62417 : if (!obj)
3873 0 : return NULL;
3874 :
3875 62417 : JS_ASSERT(obj->getDenseArrayCapacity() >= length);
3876 :
3877 62417 : obj->setDenseArrayInitializedLength(vp ? length : 0);
3878 :
3879 62417 : if (vp)
3880 43156 : obj->initDenseArrayElements(0, vp, length);
3881 :
3882 62417 : return obj;
3883 : }
3884 :
3885 : JSObject *
3886 210451 : NewSlowEmptyArray(JSContext *cx)
3887 : {
3888 210451 : JSObject *obj = NewBuiltinClassInstance(cx, &SlowArrayClass);
3889 210451 : if (!obj || !AddLengthProperty(cx, obj))
3890 0 : return NULL;
3891 :
3892 210451 : obj->setArrayLength(cx, 0);
3893 210451 : return obj;
3894 : }
3895 :
3896 : }
3897 :
3898 :
3899 : #ifdef DEBUG
3900 : JSBool
3901 0 : js_ArrayInfo(JSContext *cx, unsigned argc, jsval *vp)
3902 : {
3903 0 : CallArgs args = CallArgsFromVp(argc, vp);
3904 : JSObject *array;
3905 :
3906 0 : for (unsigned i = 0; i < args.length(); i++) {
3907 0 : Value arg = args[i];
3908 :
3909 0 : char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, arg, NULL);
3910 0 : if (!bytes)
3911 0 : return JS_FALSE;
3912 0 : if (arg.isPrimitive() ||
3913 0 : !(array = arg.toObjectOrNull())->isArray()) {
3914 0 : fprintf(stderr, "%s: not array\n", bytes);
3915 0 : cx->free_(bytes);
3916 0 : continue;
3917 : }
3918 : fprintf(stderr, "%s: %s (len %u", bytes,
3919 0 : array->isDenseArray() ? "dense" : "sparse",
3920 0 : array->getArrayLength());
3921 0 : if (array->isDenseArray()) {
3922 : fprintf(stderr, ", capacity %u",
3923 0 : array->getDenseArrayCapacity());
3924 : }
3925 0 : fputs(")\n", stderr);
3926 0 : cx->free_(bytes);
3927 : }
3928 :
3929 0 : args.rval().setUndefined();
3930 0 : return true;
3931 : }
3932 : #endif
|