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