js/src/jsarray.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:cd9ce40ed0d3
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "jsarray.h"
8
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/FloatingPoint.h"
12 #include "mozilla/MathAlgorithms.h"
13
14 #include "jsapi.h"
15 #include "jsatom.h"
16 #include "jscntxt.h"
17 #include "jsfriendapi.h"
18 #include "jsfun.h"
19 #include "jsiter.h"
20 #include "jsnum.h"
21 #include "jsobj.h"
22 #include "jstypes.h"
23 #include "jsutil.h"
24
25 #include "ds/Sort.h"
26 #include "gc/Heap.h"
27 #include "vm/ArgumentsObject.h"
28 #include "vm/ForkJoin.h"
29 #include "vm/Interpreter.h"
30 #include "vm/NumericConversions.h"
31 #include "vm/Shape.h"
32 #include "vm/StringBuffer.h"
33
34 #include "jsatominlines.h"
35
36 #include "vm/ArgumentsObject-inl.h"
37 #include "vm/ArrayObject-inl.h"
38 #include "vm/Interpreter-inl.h"
39 #include "vm/Runtime-inl.h"
40
41 using namespace js;
42 using namespace js::gc;
43 using namespace js::types;
44
45 using mozilla::Abs;
46 using mozilla::ArrayLength;
47 using mozilla::CeilingLog2;
48 using mozilla::DebugOnly;
49 using mozilla::IsNaN;
50 using mozilla::PointerRangeSize;
51
52 bool
53 js::GetLengthProperty(JSContext *cx, HandleObject obj, uint32_t *lengthp)
54 {
55 if (obj->is<ArrayObject>()) {
56 *lengthp = obj->as<ArrayObject>().length();
57 return true;
58 }
59
60 if (obj->is<ArgumentsObject>()) {
61 ArgumentsObject &argsobj = obj->as<ArgumentsObject>();
62 if (!argsobj.hasOverriddenLength()) {
63 *lengthp = argsobj.initialLength();
64 return true;
65 }
66 }
67
68 RootedValue value(cx);
69 if (!JSObject::getProperty(cx, obj, obj, cx->names().length, &value))
70 return false;
71
72 if (value.isInt32()) {
73 *lengthp = uint32_t(value.toInt32()); // uint32_t cast does ToUint32
74 return true;
75 }
76
77 return ToUint32(cx, value, lengthp);
78 }
79
80 /*
81 * Determine if the id represents an array index.
82 *
83 * An id is an array index according to ECMA by (15.4):
84 *
85 * "Array objects give special treatment to a certain class of property names.
86 * A property name P (in the form of a string value) is an array index if and
87 * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
88 * to 2^32-1."
89 *
90 * This means the largest allowed index is actually 2^32-2 (4294967294).
91 *
92 * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id)
93 * except that by using signed 31-bit integers we miss the top half of the
94 * valid range. This function checks the string representation itself; note
95 * that calling a standard conversion routine might allow strings such as
96 * "08" or "4.0" as array indices, which they are not.
97 *
98 */
99 JS_FRIEND_API(bool)
100 js::StringIsArrayIndex(JSLinearString *str, uint32_t *indexp)
101 {
102 const jschar *s = str->chars();
103 uint32_t length = str->length();
104 const jschar *end = s + length;
105
106 if (length == 0 || length > (sizeof("4294967294") - 1) || !JS7_ISDEC(*s))
107 return false;
108
109 uint32_t c = 0, previous = 0;
110 uint32_t index = JS7_UNDEC(*s++);
111
112 /* Don't allow leading zeros. */
113 if (index == 0 && s != end)
114 return false;
115
116 for (; s < end; s++) {
117 if (!JS7_ISDEC(*s))
118 return false;
119
120 previous = index;
121 c = JS7_UNDEC(*s);
122 index = 10 * index + c;
123 }
124
125 /* Make sure we didn't overflow. */
126 if (previous < (MAX_ARRAY_INDEX / 10) || (previous == (MAX_ARRAY_INDEX / 10) &&
127 c <= (MAX_ARRAY_INDEX % 10))) {
128 JS_ASSERT(index <= MAX_ARRAY_INDEX);
129 *indexp = index;
130 return true;
131 }
132
133 return false;
134 }
135
136 static bool
137 ToId(JSContext *cx, double index, MutableHandleId id)
138 {
139 if (index == uint32_t(index))
140 return IndexToId(cx, uint32_t(index), id);
141
142 Value tmp = DoubleValue(index);
143 return ValueToId<CanGC>(cx, HandleValue::fromMarkedLocation(&tmp), id);
144 }
145
146 static bool
147 ToId(JSContext *cx, uint32_t index, MutableHandleId id)
148 {
149 return IndexToId(cx, index, id);
150 }
151
152 /*
153 * If the property at the given index exists, get its value into location
154 * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp
155 * to JSVAL_VOID. This function assumes that the location pointed by vp is
156 * properly rooted and can be used as GC-protected storage for temporaries.
157 */
158 template<typename IndexType>
159 static inline bool
160 DoGetElement(JSContext *cx, HandleObject obj, HandleObject receiver,
161 IndexType index, bool *hole, MutableHandleValue vp)
162 {
163 RootedId id(cx);
164 if (!ToId(cx, index, &id))
165 return false;
166
167 RootedObject obj2(cx);
168 RootedShape prop(cx);
169 if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &prop))
170 return false;
171
172 if (!prop) {
173 vp.setUndefined();
174 *hole = true;
175 } else {
176 if (!JSObject::getGeneric(cx, obj, receiver, id, vp))
177 return false;
178 *hole = false;
179 }
180 return true;
181 }
182
183 template<typename IndexType>
184 static void
185 AssertGreaterThanZero(IndexType index)
186 {
187 JS_ASSERT(index >= 0);
188 JS_ASSERT(index == floor(index));
189 }
190
191 template<>
192 void
193 AssertGreaterThanZero(uint32_t index)
194 {
195 }
196
197 template<typename IndexType>
198 static bool
199 GetElement(JSContext *cx, HandleObject obj, HandleObject receiver,
200 IndexType index, bool *hole, MutableHandleValue vp)
201 {
202 AssertGreaterThanZero(index);
203 if (obj->isNative() && index < obj->getDenseInitializedLength()) {
204 vp.set(obj->getDenseElement(uint32_t(index)));
205 if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
206 *hole = false;
207 return true;
208 }
209 }
210 if (obj->is<ArgumentsObject>()) {
211 if (obj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp)) {
212 *hole = false;
213 return true;
214 }
215 }
216
217 return DoGetElement(cx, obj, receiver, index, hole, vp);
218 }
219
220 template<typename IndexType>
221 static inline bool
222 GetElement(JSContext *cx, HandleObject obj, IndexType index, bool *hole, MutableHandleValue vp)
223 {
224 return GetElement(cx, obj, obj, index, hole, vp);
225 }
226
227 static bool
228 GetElementsSlow(JSContext *cx, HandleObject aobj, uint32_t length, Value *vp)
229 {
230 for (uint32_t i = 0; i < length; i++) {
231 if (!JSObject::getElement(cx, aobj, aobj, i, MutableHandleValue::fromMarkedLocation(&vp[i])))
232 return false;
233 }
234
235 return true;
236 }
237
238 bool
239 js::GetElements(JSContext *cx, HandleObject aobj, uint32_t length, Value *vp)
240 {
241 if (aobj->is<ArrayObject>() && length <= aobj->getDenseInitializedLength() &&
242 !ObjectMayHaveExtraIndexedProperties(aobj))
243 {
244 /* No other indexed properties so hole = undefined */
245 const Value *srcbeg = aobj->getDenseElements();
246 const Value *srcend = srcbeg + length;
247 const Value *src = srcbeg;
248 for (Value *dst = vp; src < srcend; ++dst, ++src)
249 *dst = src->isMagic(JS_ELEMENTS_HOLE) ? UndefinedValue() : *src;
250 return true;
251 }
252
253 if (aobj->is<ArgumentsObject>()) {
254 ArgumentsObject &argsobj = aobj->as<ArgumentsObject>();
255 if (!argsobj.hasOverriddenLength()) {
256 if (argsobj.maybeGetElements(0, length, vp))
257 return true;
258 }
259 }
260
261 return GetElementsSlow(cx, aobj, length, vp);
262 }
263
264 /*
265 * Set the value of the property at the given index to v assuming v is rooted.
266 */
267 static bool
268 SetArrayElement(JSContext *cx, HandleObject obj, double index, HandleValue v)
269 {
270 JS_ASSERT(index >= 0);
271
272 if (obj->is<ArrayObject>() && !obj->isIndexed()) {
273 Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
274 /* Predicted/prefetched code should favor the remains-dense case. */
275 JSObject::EnsureDenseResult result = JSObject::ED_SPARSE;
276 do {
277 if (index > uint32_t(-1))
278 break;
279 uint32_t idx = uint32_t(index);
280 if (idx >= arr->length() && !arr->lengthIsWritable()) {
281 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, nullptr,
282 JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
283 return false;
284 }
285 result = arr->ensureDenseElements(cx, idx, 1);
286 if (result != JSObject::ED_OK)
287 break;
288 if (idx >= arr->length())
289 arr->setLengthInt32(idx + 1);
290 arr->setDenseElementWithType(cx, idx, v);
291 return true;
292 } while (false);
293
294 if (result == JSObject::ED_FAILED)
295 return false;
296 JS_ASSERT(result == JSObject::ED_SPARSE);
297 }
298
299 RootedId id(cx);
300 if (!ToId(cx, index, &id))
301 return false;
302
303 RootedValue tmp(cx, v);
304 return JSObject::setGeneric(cx, obj, obj, id, &tmp, true);
305 }
306
307 /*
308 * Attempt to delete the element |index| from |obj| as if by
309 * |obj.[[Delete]](index)|.
310 *
311 * If an error occurs while attempting to delete the element (that is, the call
312 * to [[Delete]] threw), return false.
313 *
314 * Otherwise set *succeeded to indicate whether the deletion attempt succeeded
315 * (that is, whether the call to [[Delete]] returned true or false). (Deletes
316 * generally fail only when the property is non-configurable, but proxies may
317 * implement different semantics.)
318 */
319 static bool
320 DeleteArrayElement(JSContext *cx, HandleObject obj, double index, bool *succeeded)
321 {
322 JS_ASSERT(index >= 0);
323 JS_ASSERT(floor(index) == index);
324
325 if (obj->is<ArrayObject>() && !obj->isIndexed()) {
326 if (index <= UINT32_MAX) {
327 uint32_t idx = uint32_t(index);
328 if (idx < obj->getDenseInitializedLength()) {
329 obj->markDenseElementsNotPacked(cx);
330 obj->setDenseElement(idx, MagicValue(JS_ELEMENTS_HOLE));
331 if (!js_SuppressDeletedElement(cx, obj, idx))
332 return false;
333 }
334 }
335
336 *succeeded = true;
337 return true;
338 }
339
340 if (index <= UINT32_MAX)
341 return JSObject::deleteElement(cx, obj, uint32_t(index), succeeded);
342
343 return JSObject::deleteByValue(cx, obj, DoubleValue(index), succeeded);
344 }
345
346 /* ES6 20130308 draft 9.3.5 */
347 static bool
348 DeletePropertyOrThrow(JSContext *cx, HandleObject obj, double index)
349 {
350 bool succeeded;
351 if (!DeleteArrayElement(cx, obj, index, &succeeded))
352 return false;
353 if (succeeded)
354 return true;
355
356 RootedId id(cx);
357 RootedValue indexv(cx, NumberValue(index));
358 if (!ValueToId<CanGC>(cx, indexv, &id))
359 return false;
360 return obj->reportNotConfigurable(cx, id, JSREPORT_ERROR);
361 }
362
363 bool
364 js::SetLengthProperty(JSContext *cx, HandleObject obj, double length)
365 {
366 RootedValue v(cx, NumberValue(length));
367 return JSObject::setProperty(cx, obj, obj, cx->names().length, &v, true);
368 }
369
370 /*
371 * Since SpiderMonkey supports cross-class prototype-based delegation, we have
372 * to be careful about the length getter and setter being called on an object
373 * not of Array class. For the getter, we search obj's prototype chain for the
374 * array that caused this getter to be invoked. In the setter case to overcome
375 * the JSPROP_SHARED attribute, we must define a shadowing length property.
376 */
377 static bool
378 array_length_getter(JSContext *cx, HandleObject obj_, HandleId id, MutableHandleValue vp)
379 {
380 RootedObject obj(cx, obj_);
381 do {
382 if (obj->is<ArrayObject>()) {
383 vp.setNumber(obj->as<ArrayObject>().length());
384 return true;
385 }
386 if (!JSObject::getProto(cx, obj, &obj))
387 return false;
388 } while (obj);
389 return true;
390 }
391
392 static bool
393 array_length_setter(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp)
394 {
395 if (!obj->is<ArrayObject>()) {
396 return JSObject::defineProperty(cx, obj, cx->names().length, vp,
397 nullptr, nullptr, JSPROP_ENUMERATE);
398 }
399
400 Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
401 MOZ_ASSERT(arr->lengthIsWritable(),
402 "setter shouldn't be called if property is non-writable");
403 return ArraySetLength<SequentialExecution>(cx, arr, id, JSPROP_PERMANENT, vp, strict);
404 }
405
406 struct ReverseIndexComparator
407 {
408 bool operator()(const uint32_t& a, const uint32_t& b, bool *lessOrEqualp) {
409 MOZ_ASSERT(a != b, "how'd we get duplicate indexes?");
410 *lessOrEqualp = b <= a;
411 return true;
412 }
413 };
414
415 template <ExecutionMode mode>
416 bool
417 js::CanonicalizeArrayLengthValue(typename ExecutionModeTraits<mode>::ContextType cx,
418 HandleValue v, uint32_t *newLen)
419 {
420 double d;
421
422 if (mode == ParallelExecution) {
423 if (v.isObject())
424 return false;
425
426 if (!NonObjectToUint32(cx, v, newLen))
427 return false;
428
429 if (!NonObjectToNumber(cx, v, &d))
430 return false;
431 } else {
432 if (!ToUint32(cx->asJSContext(), v, newLen))
433 return false;
434
435 if (!ToNumber(cx->asJSContext(), v, &d))
436 return false;
437 }
438
439 if (d == *newLen)
440 return true;
441
442 if (cx->isJSContext())
443 JS_ReportErrorNumber(cx->asJSContext(), js_GetErrorMessage, nullptr,
444 JSMSG_BAD_ARRAY_LENGTH);
445 return false;
446 }
447
448 template bool
449 js::CanonicalizeArrayLengthValue<SequentialExecution>(JSContext *cx,
450 HandleValue v, uint32_t *newLen);
451 template bool
452 js::CanonicalizeArrayLengthValue<ParallelExecution>(ForkJoinContext *cx,
453 HandleValue v, uint32_t *newLen);
454
455 /* ES6 20130308 draft 8.4.2.4 ArraySetLength */
456 template <ExecutionMode mode>
457 bool
458 js::ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cxArg,
459 Handle<ArrayObject*> arr, HandleId id,
460 unsigned attrs, HandleValue value, bool setterIsStrict)
461 {
462 MOZ_ASSERT(cxArg->isThreadLocal(arr));
463 MOZ_ASSERT(id == NameToId(cxArg->names().length));
464
465 /* Steps 1-2 are irrelevant in our implementation. */
466
467 /* Steps 3-5. */
468 uint32_t newLen;
469 if (!CanonicalizeArrayLengthValue<mode>(cxArg, value, &newLen))
470 return false;
471
472 // Abort if we're being asked to change enumerability or configurability.
473 // (The length property of arrays is non-configurable, so such attempts
474 // must fail.) This behavior is spread throughout the ArraySetLength spec
475 // algorithm, but we only need check it once as our array implementation
476 // is internally so different from the spec algorithm. (ES5 and ES6 define
477 // behavior by delegating to the default define-own-property algorithm --
478 // OrdinaryDefineOwnProperty in ES6, the default [[DefineOwnProperty]] in
479 // ES5 -- but we reimplement all the conflict-detection bits ourselves here
480 // so that we can use a customized length representation.)
481 if (!(attrs & JSPROP_PERMANENT) || (attrs & JSPROP_ENUMERATE)) {
482 if (!setterIsStrict)
483 return true;
484 // Bail for strict mode in parallel execution, as we need to go back
485 // to sequential mode to throw the error.
486 if (mode == ParallelExecution)
487 return false;
488 return Throw(cxArg->asJSContext(), id, JSMSG_CANT_REDEFINE_PROP);
489 }
490
491 /* Steps 6-7. */
492 bool lengthIsWritable = arr->lengthIsWritable();
493 #ifdef DEBUG
494 {
495 RootedShape lengthShape(cxArg, arr->nativeLookupPure(id));
496 MOZ_ASSERT(lengthShape);
497 MOZ_ASSERT(lengthShape->writable() == lengthIsWritable);
498 }
499 #endif
500
501 uint32_t oldLen = arr->length();
502
503 /* Steps 8-9 for arrays with non-writable length. */
504 if (!lengthIsWritable) {
505 if (newLen == oldLen)
506 return true;
507
508 if (!cxArg->isJSContext())
509 return false;
510
511 if (setterIsStrict) {
512 return JS_ReportErrorFlagsAndNumber(cxArg->asJSContext(),
513 JSREPORT_ERROR, js_GetErrorMessage, nullptr,
514 JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
515 }
516
517 return JSObject::reportReadOnly(cxArg->asJSContext(), id,
518 JSREPORT_STRICT | JSREPORT_WARNING);
519 }
520
521 /* Step 8. */
522 bool succeeded = true;
523 do {
524 // The initialized length and capacity of an array only need updating
525 // when non-hole elements are added or removed, which doesn't happen
526 // when array length stays the same or increases.
527 if (newLen >= oldLen)
528 break;
529
530 // Attempt to propagate dense-element optimization tricks, if possible,
531 // and avoid the generic (and accordingly slow) deletion code below.
532 // We can only do this if there are only densely-indexed elements.
533 // Once there's a sparse indexed element, there's no good way to know,
534 // save by enumerating all the properties to find it. But we *have* to
535 // know in case that sparse indexed element is non-configurable, as
536 // that element must prevent any deletions below it. Bug 586842 should
537 // fix this inefficiency by moving indexed storage to be entirely
538 // separate from non-indexed storage.
539 if (!arr->isIndexed()) {
540 uint32_t oldCapacity = arr->getDenseCapacity();
541 uint32_t oldInitializedLength = arr->getDenseInitializedLength();
542 MOZ_ASSERT(oldCapacity >= oldInitializedLength);
543 if (oldInitializedLength > newLen)
544 arr->setDenseInitializedLength(newLen);
545 if (oldCapacity > newLen)
546 arr->shrinkElements(cxArg, newLen);
547
548 // We've done the work of deleting any dense elements needing
549 // deletion, and there are no sparse elements. Thus we can skip
550 // straight to defining the length.
551 break;
552 }
553
554 // Bail from parallel execution if need to perform step 15, which is
555 // unsafe and isn't a common case.
556 if (mode == ParallelExecution)
557 return false;
558
559 JSContext *cx = cxArg->asJSContext();
560
561 // Step 15.
562 //
563 // Attempt to delete all elements above the new length, from greatest
564 // to least. If any of these deletions fails, we're supposed to define
565 // the length to one greater than the index that couldn't be deleted,
566 // *with the property attributes specified*. This might convert the
567 // length to be not the value specified, yet non-writable. (You may be
568 // forgiven for thinking these are interesting semantics.) Example:
569 //
570 // var arr =
571 // Object.defineProperty([0, 1, 2, 3], 1, { writable: false });
572 // Object.defineProperty(arr, "length",
573 // { value: 0, writable: false });
574 //
575 // will convert |arr| to an array of non-writable length two, then
576 // throw a TypeError.
577 //
578 // We implement this behavior, in the relevant lops below, by setting
579 // |succeeded| to false. Then we exit the loop, define the length
580 // appropriately, and only then throw a TypeError, if necessary.
581 uint32_t gap = oldLen - newLen;
582 const uint32_t RemoveElementsFastLimit = 1 << 24;
583 if (gap < RemoveElementsFastLimit) {
584 // If we're removing a relatively small number of elements, just do
585 // it exactly by the spec.
586 while (newLen < oldLen) {
587 /* Step 15a. */
588 oldLen--;
589
590 /* Steps 15b-d. */
591 bool deleteSucceeded;
592 if (!JSObject::deleteElement(cx, arr, oldLen, &deleteSucceeded))
593 return false;
594 if (!deleteSucceeded) {
595 newLen = oldLen + 1;
596 succeeded = false;
597 break;
598 }
599 }
600 } else {
601 // If we're removing a large number of elements from an array
602 // that's probably sparse, try a different tack. Get all the own
603 // property names, sift out the indexes in the deletion range into
604 // a vector, sort the vector greatest to least, then delete the
605 // indexes greatest to least using that vector. See bug 322135.
606 //
607 // This heuristic's kind of a huge guess -- "large number of
608 // elements" and "probably sparse" are completely unprincipled
609 // predictions. In the long run, bug 586842 will support the right
610 // fix: store sparse elements in a sorted data structure that
611 // permits fast in-reverse-order traversal and concurrent removals.
612
613 Vector<uint32_t> indexes(cx);
614 {
615 AutoIdVector props(cx);
616 if (!GetPropertyNames(cx, arr, JSITER_OWNONLY | JSITER_HIDDEN, &props))
617 return false;
618
619 for (size_t i = 0; i < props.length(); i++) {
620 if (!CheckForInterrupt(cx))
621 return false;
622
623 uint32_t index;
624 if (!js_IdIsIndex(props[i], &index))
625 continue;
626
627 if (index >= newLen && index < oldLen) {
628 if (!indexes.append(index))
629 return false;
630 }
631 }
632 }
633
634 uint32_t count = indexes.length();
635 {
636 // We should use radix sort to be O(n), but this is uncommon
637 // enough that we'll punt til someone complains.
638 Vector<uint32_t> scratch(cx);
639 if (!scratch.resize(count))
640 return false;
641 MOZ_ALWAYS_TRUE(MergeSort(indexes.begin(), count, scratch.begin(),
642 ReverseIndexComparator()));
643 }
644
645 uint32_t index = UINT32_MAX;
646 for (uint32_t i = 0; i < count; i++) {
647 MOZ_ASSERT(indexes[i] < index, "indexes should never repeat");
648 index = indexes[i];
649
650 /* Steps 15b-d. */
651 bool deleteSucceeded;
652 if (!JSObject::deleteElement(cx, arr, index, &deleteSucceeded))
653 return false;
654 if (!deleteSucceeded) {
655 newLen = index + 1;
656 succeeded = false;
657 break;
658 }
659 }
660 }
661 } while (false);
662
663 /* Steps 12, 16. */
664
665 // Yes, we totally drop a non-stub getter/setter from a defineProperty
666 // API call on the floor here. Given that getter/setter will go away in
667 // the long run, with accessors replacing them both internally and at the
668 // API level, just run with this.
669 RootedShape lengthShape(cxArg, mode == ParallelExecution
670 ? arr->nativeLookupPure(id)
671 : arr->nativeLookup(cxArg->asJSContext(), id));
672 if (!JSObject::changeProperty<mode>(cxArg, arr, lengthShape, attrs,
673 JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_SHARED,
674 array_length_getter, array_length_setter))
675 {
676 return false;
677 }
678
679 if (mode == ParallelExecution) {
680 // Overflowing int32 requires changing TI state.
681 if (newLen > INT32_MAX)
682 return false;
683 arr->setLengthInt32(newLen);
684 } else {
685 JSContext *cx = cxArg->asJSContext();
686 arr->setLength(cx, newLen);
687 }
688
689
690 // All operations past here until the |!succeeded| code must be infallible,
691 // so that all element fields remain properly synchronized.
692
693 // Trim the initialized length, if needed, to preserve the <= length
694 // invariant. (Capacity was already reduced during element deletion, if
695 // necessary.)
696 ObjectElements *header = arr->getElementsHeader();
697 header->initializedLength = Min(header->initializedLength, newLen);
698
699 if (attrs & JSPROP_READONLY) {
700 header->setNonwritableArrayLength();
701
702 // When an array's length becomes non-writable, writes to indexes
703 // greater than or equal to the length don't change the array. We
704 // handle this with a check for non-writable length in most places.
705 // But in JIT code every check counts -- so we piggyback the check on
706 // the already-required range check for |index < capacity| by making
707 // capacity of arrays with non-writable length never exceed the length.
708 if (arr->getDenseCapacity() > newLen) {
709 arr->shrinkElements(cxArg, newLen);
710 arr->getElementsHeader()->capacity = newLen;
711 }
712 }
713
714 if (setterIsStrict && !succeeded) {
715 // We can't have arrived here under ParallelExecution, as we have
716 // returned from the function before step 15 above.
717 JSContext *cx = cxArg->asJSContext();
718 RootedId elementId(cx);
719 if (!IndexToId(cx, newLen - 1, &elementId))
720 return false;
721 return arr->reportNotConfigurable(cx, elementId);
722 }
723
724 return true;
725 }
726
727 template bool
728 js::ArraySetLength<SequentialExecution>(JSContext *cx, Handle<ArrayObject*> arr,
729 HandleId id, unsigned attrs, HandleValue value,
730 bool setterIsStrict);
731 template bool
732 js::ArraySetLength<ParallelExecution>(ForkJoinContext *cx, Handle<ArrayObject*> arr,
733 HandleId id, unsigned attrs, HandleValue value,
734 bool setterIsStrict);
735
736 bool
737 js::WouldDefinePastNonwritableLength(ThreadSafeContext *cx,
738 HandleObject obj, uint32_t index, bool strict,
739 bool *definesPast)
740 {
741 if (!obj->is<ArrayObject>()) {
742 *definesPast = false;
743 return true;
744 }
745
746 Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
747 uint32_t length = arr->length();
748 if (index < length) {
749 *definesPast = false;
750 return true;
751 }
752
753 if (arr->lengthIsWritable()) {
754 *definesPast = false;
755 return true;
756 }
757
758 *definesPast = true;
759
760 // Error in strict mode code or warn with strict option.
761 unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_STRICT | JSREPORT_WARNING);
762 if (cx->isForkJoinContext())
763 return cx->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, flags);
764
765 if (!cx->isJSContext())
766 return true;
767
768 JSContext *ncx = cx->asJSContext();
769
770 if (!strict && !ncx->options().extraWarnings())
771 return true;
772
773 // XXX include the index and maybe array length in the error message
774 return JS_ReportErrorFlagsAndNumber(ncx, flags, js_GetErrorMessage, nullptr,
775 JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH);
776 }
777
778 static bool
779 array_addProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
780 {
781 Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
782
783 uint32_t index;
784 if (!js_IdIsIndex(id, &index))
785 return true;
786
787 uint32_t length = arr->length();
788 if (index >= length) {
789 MOZ_ASSERT(arr->lengthIsWritable(),
790 "how'd this element get added if length is non-writable?");
791 arr->setLength(cx, index + 1);
792 }
793 return true;
794 }
795
796 bool
797 js::ObjectMayHaveExtraIndexedProperties(JSObject *obj)
798 {
799 /*
800 * Whether obj may have indexed properties anywhere besides its dense
801 * elements. This includes other indexed properties in its shape hierarchy,
802 * and indexed properties or elements along its prototype chain.
803 */
804
805 JS_ASSERT(obj->isNative());
806
807 if (obj->isIndexed())
808 return true;
809
810 /*
811 * Walk up the prototype chain and see if this indexed element already
812 * exists. If we hit the end of the prototype chain, it's safe to set the
813 * element on the original object.
814 */
815 while ((obj = obj->getProto()) != nullptr) {
816 /*
817 * If the prototype is a non-native object (possibly a dense array), or
818 * a native object (possibly a slow array) that has indexed properties,
819 * return true.
820 */
821 if (!obj->isNative())
822 return true;
823 if (obj->isIndexed())
824 return true;
825 if (obj->getDenseInitializedLength() > 0)
826 return true;
827 if (obj->is<TypedArrayObject>())
828 return true;
829 }
830
831 return false;
832 }
833
834 const Class ArrayObject::class_ = {
835 "Array",
836 JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
837 array_addProperty,
838 JS_DeletePropertyStub, /* delProperty */
839 JS_PropertyStub, /* getProperty */
840 JS_StrictPropertyStub, /* setProperty */
841 JS_EnumerateStub,
842 JS_ResolveStub,
843 JS_ConvertStub,
844 nullptr,
845 nullptr, /* call */
846 nullptr, /* hasInstance */
847 nullptr, /* construct */
848 nullptr /* trace */
849 };
850
851 static bool
852 AddLengthProperty(ExclusiveContext *cx, HandleObject obj)
853 {
854 /*
855 * Add the 'length' property for a newly created array,
856 * and update the elements to be an empty array owned by the object.
857 * The shared emptyObjectElements singleton cannot be used for slow arrays,
858 * as accesses to 'length' will use the elements header.
859 */
860
861 RootedId lengthId(cx, NameToId(cx->names().length));
862 JS_ASSERT(!obj->nativeLookup(cx, lengthId));
863
864 return JSObject::addProperty(cx, obj, lengthId, array_length_getter, array_length_setter,
865 SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0,
866 /* allowDictionary = */ false);
867 }
868
869 #if JS_HAS_TOSOURCE
870 MOZ_ALWAYS_INLINE bool
871 IsArray(HandleValue v)
872 {
873 return v.isObject() && v.toObject().is<ArrayObject>();
874 }
875
876 MOZ_ALWAYS_INLINE bool
877 array_toSource_impl(JSContext *cx, CallArgs args)
878 {
879 JS_ASSERT(IsArray(args.thisv()));
880
881 Rooted<JSObject*> obj(cx, &args.thisv().toObject());
882 RootedValue elt(cx);
883
884 AutoCycleDetector detector(cx, obj);
885 if (!detector.init())
886 return false;
887
888 StringBuffer sb(cx);
889
890 if (detector.foundCycle()) {
891 if (!sb.append("[]"))
892 return false;
893 goto make_string;
894 }
895
896 if (!sb.append('['))
897 return false;
898
899 uint32_t length;
900 if (!GetLengthProperty(cx, obj, &length))
901 return false;
902
903 for (uint32_t index = 0; index < length; index++) {
904 bool hole;
905 if (!CheckForInterrupt(cx) ||
906 !GetElement(cx, obj, index, &hole, &elt)) {
907 return false;
908 }
909
910 /* Get element's character string. */
911 JSString *str;
912 if (hole) {
913 str = cx->runtime()->emptyString;
914 } else {
915 str = ValueToSource(cx, elt);
916 if (!str)
917 return false;
918 }
919
920 /* Append element to buffer. */
921 if (!sb.append(str))
922 return false;
923 if (index + 1 != length) {
924 if (!sb.append(", "))
925 return false;
926 } else if (hole) {
927 if (!sb.append(','))
928 return false;
929 }
930 }
931
932 /* Finalize the buffer. */
933 if (!sb.append(']'))
934 return false;
935
936 make_string:
937 JSString *str = sb.finishString();
938 if (!str)
939 return false;
940
941 args.rval().setString(str);
942 return true;
943 }
944
945 static bool
946 array_toSource(JSContext *cx, unsigned argc, Value *vp)
947 {
948 JS_CHECK_RECURSION(cx, return false);
949 CallArgs args = CallArgsFromVp(argc, vp);
950 return CallNonGenericMethod<IsArray, array_toSource_impl>(cx, args);
951 }
952 #endif
953
954 struct EmptySeparatorOp
955 {
956 bool operator()(JSContext *, StringBuffer &sb) { return true; }
957 };
958
959 struct CharSeparatorOp
960 {
961 jschar sep;
962 CharSeparatorOp(jschar sep) : sep(sep) {};
963 bool operator()(JSContext *, StringBuffer &sb) { return sb.append(sep); }
964 };
965
966 struct StringSeparatorOp
967 {
968 const jschar *sepchars;
969 size_t seplen;
970
971 StringSeparatorOp(const jschar *sepchars, size_t seplen)
972 : sepchars(sepchars), seplen(seplen) {};
973
974 bool operator()(JSContext *cx, StringBuffer &sb) {
975 return sb.append(sepchars, seplen);
976 }
977 };
978
979 template <bool Locale, typename SeparatorOp>
980 static bool
981 ArrayJoinKernel(JSContext *cx, SeparatorOp sepOp, HandleObject obj, uint32_t length,
982 StringBuffer &sb)
983 {
984 uint32_t i = 0;
985
986 if (!Locale && obj->is<ArrayObject>() && !ObjectMayHaveExtraIndexedProperties(obj)) {
987 // This loop handles all elements up to initializedLength. If
988 // length > initLength we rely on the second loop to add the
989 // other elements.
990 uint32_t initLength = obj->getDenseInitializedLength();
991 while (i < initLength) {
992 if (!CheckForInterrupt(cx))
993 return false;
994
995 const Value &elem = obj->getDenseElement(i);
996
997 if (elem.isString()) {
998 if (!sb.append(elem.toString()))
999 return false;
1000 } else if (elem.isNumber()) {
1001 if (!NumberValueToStringBuffer(cx, elem, sb))
1002 return false;
1003 } else if (elem.isBoolean()) {
1004 if (!BooleanToStringBuffer(elem.toBoolean(), sb))
1005 return false;
1006 } else if (elem.isObject()) {
1007 /*
1008 * Object stringifying could modify the initialized length or make
1009 * the array sparse. Delegate it to a separate loop to keep this
1010 * one tight.
1011 */
1012 break;
1013 } else {
1014 JS_ASSERT(elem.isMagic(JS_ELEMENTS_HOLE) || elem.isNullOrUndefined());
1015 }
1016
1017 if (++i != length && !sepOp(cx, sb))
1018 return false;
1019 }
1020 }
1021
1022 if (i != length) {
1023 RootedValue v(cx);
1024 while (i < length) {
1025 if (!CheckForInterrupt(cx))
1026 return false;
1027
1028 bool hole;
1029 if (!GetElement(cx, obj, i, &hole, &v))
1030 return false;
1031 if (!hole && !v.isNullOrUndefined()) {
1032 if (Locale) {
1033 JSObject *robj = ToObject(cx, v);
1034 if (!robj)
1035 return false;
1036 RootedId id(cx, NameToId(cx->names().toLocaleString));
1037 if (!robj->callMethod(cx, id, 0, nullptr, &v))
1038 return false;
1039 }
1040 if (!ValueToStringBuffer(cx, v, sb))
1041 return false;
1042 }
1043
1044 if (++i != length && !sepOp(cx, sb))
1045 return false;
1046 }
1047 }
1048
1049 return true;
1050 }
1051
1052 template <bool Locale>
1053 static bool
1054 ArrayJoin(JSContext *cx, CallArgs &args)
1055 {
1056 // This method is shared by Array.prototype.join and
1057 // Array.prototype.toLocaleString. The steps in ES5 are nearly the same, so
1058 // the annotations in this function apply to both toLocaleString and join.
1059
1060 // Step 1
1061 RootedObject obj(cx, ToObject(cx, args.thisv()));
1062 if (!obj)
1063 return false;
1064
1065 AutoCycleDetector detector(cx, obj);
1066 if (!detector.init())
1067 return false;
1068
1069 if (detector.foundCycle()) {
1070 args.rval().setString(cx->names().empty);
1071 return true;
1072 }
1073
1074 // Steps 2 and 3
1075 uint32_t length;
1076 if (!GetLengthProperty(cx, obj, &length))
1077 return false;
1078
1079 // Steps 4 and 5
1080 RootedString sepstr(cx, nullptr);
1081 const jschar *sepchars;
1082 size_t seplen;
1083 if (!Locale && args.hasDefined(0)) {
1084 sepstr = ToString<CanGC>(cx, args[0]);
1085 if (!sepstr)
1086 return false;
1087 sepchars = sepstr->getChars(cx);
1088 if (!sepchars)
1089 return false;
1090 seplen = sepstr->length();
1091 } else {
1092 HandlePropertyName comma = cx->names().comma;
1093 sepstr = comma;
1094 sepchars = comma->chars();
1095 seplen = comma->length();
1096 }
1097
1098 JS::Anchor<JSString*> anchor(sepstr);
1099
1100 // Step 6 is implicit in the loops below.
1101
1102 // An optimized version of a special case of steps 7-11: when length==1 and
1103 // the 0th element is a string, ToString() of that element is a no-op and
1104 // so it can be immediately returned as the result.
1105 if (length == 1 && !Locale && obj->is<ArrayObject>() &&
1106 obj->getDenseInitializedLength() == 1)
1107 {
1108 const Value &elem0 = obj->getDenseElement(0);
1109 if (elem0.isString()) {
1110 args.rval().setString(elem0.toString());
1111 return true;
1112 }
1113 }
1114
1115 StringBuffer sb(cx);
1116
1117 // The separator will be added |length - 1| times, reserve space for that
1118 // so that we don't have to unnecessarily grow the buffer.
1119 if (length > 0 && !sb.reserve(seplen * (length - 1)))
1120 return false;
1121
1122 // Various optimized versions of steps 7-10.
1123 if (seplen == 0) {
1124 EmptySeparatorOp op;
1125 if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb))
1126 return false;
1127 } else if (seplen == 1) {
1128 CharSeparatorOp op(sepchars[0]);
1129 if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb))
1130 return false;
1131 } else {
1132 StringSeparatorOp op(sepchars, seplen);
1133 if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb))
1134 return false;
1135 }
1136
1137 // Step 11
1138 JSString *str = sb.finishString();
1139 if (!str)
1140 return false;
1141 args.rval().setString(str);
1142 return true;
1143 }
1144
1145 /* ES5 15.4.4.2. NB: The algorithm here differs from the one in ES3. */
1146 static bool
1147 array_toString(JSContext *cx, unsigned argc, Value *vp)
1148 {
1149 JS_CHECK_RECURSION(cx, return false);
1150
1151 CallArgs args = CallArgsFromVp(argc, vp);
1152 RootedObject obj(cx, ToObject(cx, args.thisv()));
1153 if (!obj)
1154 return false;
1155
1156 RootedValue join(cx, args.calleev());
1157 if (!JSObject::getProperty(cx, obj, obj, cx->names().join, &join))
1158 return false;
1159
1160 if (!js_IsCallable(join)) {
1161 JSString *str = JS_BasicObjectToString(cx, obj);
1162 if (!str)
1163 return false;
1164 args.rval().setString(str);
1165 return true;
1166 }
1167
1168 InvokeArgs args2(cx);
1169 if (!args2.init(0))
1170 return false;
1171
1172 args2.setCallee(join);
1173 args2.setThis(ObjectValue(*obj));
1174
1175 /* Do the call. */
1176 if (!Invoke(cx, args2))
1177 return false;
1178 args.rval().set(args2.rval());
1179 return true;
1180 }
1181
1182 /* ES5 15.4.4.3 */
1183 static bool
1184 array_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
1185 {
1186 JS_CHECK_RECURSION(cx, return false);
1187
1188 CallArgs args = CallArgsFromVp(argc, vp);
1189 return ArrayJoin<true>(cx, args);
1190 }
1191
1192 /* ES5 15.4.4.5 */
1193 static bool
1194 array_join(JSContext *cx, unsigned argc, Value *vp)
1195 {
1196 JS_CHECK_RECURSION(cx, return false);
1197
1198 CallArgs args = CallArgsFromVp(argc, vp);
1199 return ArrayJoin<false>(cx, args);
1200 }
1201
1202 static inline bool
1203 InitArrayTypes(JSContext *cx, TypeObject *type, const Value *vector, unsigned count)
1204 {
1205 if (!type->unknownProperties()) {
1206 AutoEnterAnalysis enter(cx);
1207
1208 HeapTypeSet *types = type->getProperty(cx, JSID_VOID);
1209 if (!types)
1210 return false;
1211
1212 for (unsigned i = 0; i < count; i++) {
1213 if (vector[i].isMagic(JS_ELEMENTS_HOLE))
1214 continue;
1215 Type valtype = GetValueType(vector[i]);
1216 types->addType(cx, valtype);
1217 }
1218 }
1219 return true;
1220 }
1221
1222 enum ShouldUpdateTypes
1223 {
1224 UpdateTypes = true,
1225 DontUpdateTypes = false
1226 };
1227
1228 /* vector must point to rooted memory. */
1229 static bool
1230 InitArrayElements(JSContext *cx, HandleObject obj, uint32_t start, uint32_t count, const Value *vector, ShouldUpdateTypes updateTypes)
1231 {
1232 JS_ASSERT(count <= MAX_ARRAY_INDEX);
1233
1234 if (count == 0)
1235 return true;
1236
1237 types::TypeObject *type = obj->getType(cx);
1238 if (!type)
1239 return false;
1240 if (updateTypes && !InitArrayTypes(cx, type, vector, count))
1241 return false;
1242
1243 /*
1244 * Optimize for dense arrays so long as adding the given set of elements
1245 * wouldn't otherwise make the array slow or exceed a non-writable array
1246 * length.
1247 */
1248 do {
1249 if (!obj->is<ArrayObject>())
1250 break;
1251 if (ObjectMayHaveExtraIndexedProperties(obj))
1252 break;
1253
1254 if (obj->shouldConvertDoubleElements())
1255 break;
1256
1257 Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
1258
1259 if (!arr->lengthIsWritable() && start + count > arr->length())
1260 break;
1261
1262 JSObject::EnsureDenseResult result = arr->ensureDenseElements(cx, start, count);
1263 if (result != JSObject::ED_OK) {
1264 if (result == JSObject::ED_FAILED)
1265 return false;
1266 JS_ASSERT(result == JSObject::ED_SPARSE);
1267 break;
1268 }
1269
1270 uint32_t newlen = start + count;
1271 if (newlen > arr->length())
1272 arr->setLengthInt32(newlen);
1273
1274 JS_ASSERT(count < UINT32_MAX / sizeof(Value));
1275 arr->copyDenseElements(start, vector, count);
1276 JS_ASSERT_IF(count != 0, !arr->getDenseElement(newlen - 1).isMagic(JS_ELEMENTS_HOLE));
1277 return true;
1278 } while (false);
1279
1280 const Value* end = vector + count;
1281 while (vector < end && start <= MAX_ARRAY_INDEX) {
1282 if (!CheckForInterrupt(cx) ||
1283 !SetArrayElement(cx, obj, start++, HandleValue::fromMarkedLocation(vector++))) {
1284 return false;
1285 }
1286 }
1287
1288 if (vector == end)
1289 return true;
1290
1291 JS_ASSERT(start == MAX_ARRAY_INDEX + 1);
1292 RootedValue value(cx);
1293 RootedId id(cx);
1294 RootedValue indexv(cx);
1295 double index = MAX_ARRAY_INDEX + 1;
1296 do {
1297 value = *vector++;
1298 indexv = DoubleValue(index);
1299 if (!ValueToId<CanGC>(cx, indexv, &id) ||
1300 !JSObject::setGeneric(cx, obj, obj, id, &value, true))
1301 {
1302 return false;
1303 }
1304 index += 1;
1305 } while (vector != end);
1306
1307 return true;
1308 }
1309
1310 static bool
1311 array_reverse(JSContext *cx, unsigned argc, Value *vp)
1312 {
1313 CallArgs args = CallArgsFromVp(argc, vp);
1314 RootedObject obj(cx, ToObject(cx, args.thisv()));
1315 if (!obj)
1316 return false;
1317
1318 uint32_t len;
1319 if (!GetLengthProperty(cx, obj, &len))
1320 return false;
1321
1322 do {
1323 if (!obj->is<ArrayObject>())
1324 break;
1325 if (ObjectMayHaveExtraIndexedProperties(obj))
1326 break;
1327
1328 /* An empty array or an array with no elements is already reversed. */
1329 if (len == 0 || obj->getDenseCapacity() == 0) {
1330 args.rval().setObject(*obj);
1331 return true;
1332 }
1333
1334 /*
1335 * It's actually surprisingly complicated to reverse an array due to the
1336 * orthogonality of array length and array capacity while handling
1337 * leading and trailing holes correctly. Reversing seems less likely to
1338 * be a common operation than other array mass-mutation methods, so for
1339 * now just take a probably-small memory hit (in the absence of too many
1340 * holes in the array at its start) and ensure that the capacity is
1341 * sufficient to hold all the elements in the array if it were full.
1342 */
1343 JSObject::EnsureDenseResult result = obj->ensureDenseElements(cx, len, 0);
1344 if (result != JSObject::ED_OK) {
1345 if (result == JSObject::ED_FAILED)
1346 return false;
1347 JS_ASSERT(result == JSObject::ED_SPARSE);
1348 break;
1349 }
1350
1351 /* Fill out the array's initialized length to its proper length. */
1352 obj->ensureDenseInitializedLength(cx, len, 0);
1353
1354 RootedValue origlo(cx), orighi(cx);
1355
1356 uint32_t lo = 0, hi = len - 1;
1357 for (; lo < hi; lo++, hi--) {
1358 origlo = obj->getDenseElement(lo);
1359 orighi = obj->getDenseElement(hi);
1360 obj->setDenseElement(lo, orighi);
1361 if (orighi.isMagic(JS_ELEMENTS_HOLE) &&
1362 !js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(lo))) {
1363 return false;
1364 }
1365 obj->setDenseElement(hi, origlo);
1366 if (origlo.isMagic(JS_ELEMENTS_HOLE) &&
1367 !js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(hi))) {
1368 return false;
1369 }
1370 }
1371
1372 /*
1373 * Per ECMA-262, don't update the length of the array, even if the new
1374 * array has trailing holes (and thus the original array began with
1375 * holes).
1376 */
1377 args.rval().setObject(*obj);
1378 return true;
1379 } while (false);
1380
1381 RootedValue lowval(cx), hival(cx);
1382 for (uint32_t i = 0, half = len / 2; i < half; i++) {
1383 bool hole, hole2;
1384 if (!CheckForInterrupt(cx) ||
1385 !GetElement(cx, obj, i, &hole, &lowval) ||
1386 !GetElement(cx, obj, len - i - 1, &hole2, &hival))
1387 {
1388 return false;
1389 }
1390
1391 if (!hole && !hole2) {
1392 if (!SetArrayElement(cx, obj, i, hival))
1393 return false;
1394 if (!SetArrayElement(cx, obj, len - i - 1, lowval))
1395 return false;
1396 } else if (hole && !hole2) {
1397 if (!SetArrayElement(cx, obj, i, hival))
1398 return false;
1399 if (!DeletePropertyOrThrow(cx, obj, len - i - 1))
1400 return false;
1401 } else if (!hole && hole2) {
1402 if (!DeletePropertyOrThrow(cx, obj, i))
1403 return false;
1404 if (!SetArrayElement(cx, obj, len - i - 1, lowval))
1405 return false;
1406 } else {
1407 // No action required.
1408 }
1409 }
1410 args.rval().setObject(*obj);
1411 return true;
1412 }
1413
1414 namespace {
1415
1416 inline bool
1417 CompareStringValues(JSContext *cx, const Value &a, const Value &b, bool *lessOrEqualp)
1418 {
1419 if (!CheckForInterrupt(cx))
1420 return false;
1421
1422 JSString *astr = a.toString();
1423 JSString *bstr = b.toString();
1424 int32_t result;
1425 if (!CompareStrings(cx, astr, bstr, &result))
1426 return false;
1427
1428 *lessOrEqualp = (result <= 0);
1429 return true;
1430 }
1431
1432 static const uint64_t powersOf10[] = {
1433 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 1000000000000ULL
1434 };
1435
1436 static inline unsigned
1437 NumDigitsBase10(uint32_t n)
1438 {
1439 /*
1440 * This is just floor_log10(n) + 1
1441 * Algorithm taken from
1442 * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
1443 */
1444 uint32_t log2 = CeilingLog2(n);
1445 uint32_t t = log2 * 1233 >> 12;
1446 return t - (n < powersOf10[t]) + 1;
1447 }
1448
1449 static inline bool
1450 CompareLexicographicInt32(const Value &a, const Value &b, bool *lessOrEqualp)
1451 {
1452 int32_t aint = a.toInt32();
1453 int32_t bint = b.toInt32();
1454
1455 /*
1456 * If both numbers are equal ... trivial
1457 * If only one of both is negative --> arithmetic comparison as char code
1458 * of '-' is always less than any other digit
1459 * If both numbers are negative convert them to positive and continue
1460 * handling ...
1461 */
1462 if (aint == bint) {
1463 *lessOrEqualp = true;
1464 } else if ((aint < 0) && (bint >= 0)) {
1465 *lessOrEqualp = true;
1466 } else if ((aint >= 0) && (bint < 0)) {
1467 *lessOrEqualp = false;
1468 } else {
1469 uint32_t auint = Abs(aint);
1470 uint32_t buint = Abs(bint);
1471
1472 /*
1473 * ... get number of digits of both integers.
1474 * If they have the same number of digits --> arithmetic comparison.
1475 * If digits_a > digits_b: a < b*10e(digits_a - digits_b).
1476 * If digits_b > digits_a: a*10e(digits_b - digits_a) <= b.
1477 */
1478 unsigned digitsa = NumDigitsBase10(auint);
1479 unsigned digitsb = NumDigitsBase10(buint);
1480 if (digitsa == digitsb) {
1481 *lessOrEqualp = (auint <= buint);
1482 } else if (digitsa > digitsb) {
1483 JS_ASSERT((digitsa - digitsb) < ArrayLength(powersOf10));
1484 *lessOrEqualp = (uint64_t(auint) < uint64_t(buint) * powersOf10[digitsa - digitsb]);
1485 } else { /* if (digitsb > digitsa) */
1486 JS_ASSERT((digitsb - digitsa) < ArrayLength(powersOf10));
1487 *lessOrEqualp = (uint64_t(auint) * powersOf10[digitsb - digitsa] <= uint64_t(buint));
1488 }
1489 }
1490
1491 return true;
1492 }
1493
1494 static inline bool
1495 CompareSubStringValues(JSContext *cx, const jschar *s1, size_t l1,
1496 const jschar *s2, size_t l2, bool *lessOrEqualp)
1497 {
1498 if (!CheckForInterrupt(cx))
1499 return false;
1500
1501 if (!s1 || !s2)
1502 return false;
1503
1504 int32_t result = CompareChars(s1, l1, s2, l2);
1505 *lessOrEqualp = (result <= 0);
1506 return true;
1507 }
1508
1509 struct SortComparatorStrings
1510 {
1511 JSContext *const cx;
1512
1513 SortComparatorStrings(JSContext *cx)
1514 : cx(cx) {}
1515
1516 bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) {
1517 return CompareStringValues(cx, a, b, lessOrEqualp);
1518 }
1519 };
1520
1521 struct SortComparatorLexicographicInt32
1522 {
1523 bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) {
1524 return CompareLexicographicInt32(a, b, lessOrEqualp);
1525 }
1526 };
1527
1528 struct StringifiedElement
1529 {
1530 size_t charsBegin;
1531 size_t charsEnd;
1532 size_t elementIndex;
1533 };
1534
1535 struct SortComparatorStringifiedElements
1536 {
1537 JSContext *const cx;
1538 const StringBuffer &sb;
1539
1540 SortComparatorStringifiedElements(JSContext *cx, const StringBuffer &sb)
1541 : cx(cx), sb(sb) {}
1542
1543 bool operator()(const StringifiedElement &a, const StringifiedElement &b, bool *lessOrEqualp) {
1544 return CompareSubStringValues(cx, sb.begin() + a.charsBegin, a.charsEnd - a.charsBegin,
1545 sb.begin() + b.charsBegin, b.charsEnd - b.charsBegin,
1546 lessOrEqualp);
1547 }
1548 };
1549
1550 struct SortComparatorFunction
1551 {
1552 JSContext *const cx;
1553 const Value &fval;
1554 FastInvokeGuard &fig;
1555
1556 SortComparatorFunction(JSContext *cx, const Value &fval, FastInvokeGuard &fig)
1557 : cx(cx), fval(fval), fig(fig) { }
1558
1559 bool operator()(const Value &a, const Value &b, bool *lessOrEqualp);
1560 };
1561
1562 bool
1563 SortComparatorFunction::operator()(const Value &a, const Value &b, bool *lessOrEqualp)
1564 {
1565 /*
1566 * array_sort deals with holes and undefs on its own and they should not
1567 * come here.
1568 */
1569 JS_ASSERT(!a.isMagic() && !a.isUndefined());
1570 JS_ASSERT(!a.isMagic() && !b.isUndefined());
1571
1572 if (!CheckForInterrupt(cx))
1573 return false;
1574
1575 InvokeArgs &args = fig.args();
1576 if (!args.init(2))
1577 return false;
1578
1579 args.setCallee(fval);
1580 args.setThis(UndefinedValue());
1581 args[0].set(a);
1582 args[1].set(b);
1583
1584 if (!fig.invoke(cx))
1585 return false;
1586
1587 double cmp;
1588 if (!ToNumber(cx, args.rval(), &cmp))
1589 return false;
1590
1591 /*
1592 * XXX eport some kind of error here if cmp is NaN? ECMA talks about
1593 * 'consistent compare functions' that don't return NaN, but is silent
1594 * about what the result should be. So we currently ignore it.
1595 */
1596 *lessOrEqualp = (IsNaN(cmp) || cmp <= 0);
1597 return true;
1598 }
1599
1600 struct NumericElement
1601 {
1602 double dv;
1603 size_t elementIndex;
1604 };
1605
1606 bool
1607 ComparatorNumericLeftMinusRight(const NumericElement &a, const NumericElement &b,
1608 bool *lessOrEqualp)
1609 {
1610 *lessOrEqualp = (a.dv <= b.dv);
1611 return true;
1612 }
1613
1614 bool
1615 ComparatorNumericRightMinusLeft(const NumericElement &a, const NumericElement &b,
1616 bool *lessOrEqualp)
1617 {
1618 *lessOrEqualp = (b.dv <= a.dv);
1619 return true;
1620 }
1621
1622 typedef bool (*ComparatorNumeric)(const NumericElement &a, const NumericElement &b,
1623 bool *lessOrEqualp);
1624
1625 ComparatorNumeric SortComparatorNumerics[] = {
1626 nullptr,
1627 nullptr,
1628 ComparatorNumericLeftMinusRight,
1629 ComparatorNumericRightMinusLeft
1630 };
1631
1632 bool
1633 ComparatorInt32LeftMinusRight(const Value &a, const Value &b, bool *lessOrEqualp)
1634 {
1635 *lessOrEqualp = (a.toInt32() <= b.toInt32());
1636 return true;
1637 }
1638
1639 bool
1640 ComparatorInt32RightMinusLeft(const Value &a, const Value &b, bool *lessOrEqualp)
1641 {
1642 *lessOrEqualp = (b.toInt32() <= a.toInt32());
1643 return true;
1644 }
1645
1646 typedef bool (*ComparatorInt32)(const Value &a, const Value &b, bool *lessOrEqualp);
1647
1648 ComparatorInt32 SortComparatorInt32s[] = {
1649 nullptr,
1650 nullptr,
1651 ComparatorInt32LeftMinusRight,
1652 ComparatorInt32RightMinusLeft
1653 };
1654
1655 // Note: Values for this enum must match up with SortComparatorNumerics
1656 // and SortComparatorInt32s.
1657 enum ComparatorMatchResult {
1658 Match_Failure = 0,
1659 Match_None,
1660 Match_LeftMinusRight,
1661 Match_RightMinusLeft
1662 };
1663
1664 /*
1665 * Specialize behavior for comparator functions with particular common bytecode
1666 * patterns: namely, |return x - y| and |return y - x|.
1667 */
1668 ComparatorMatchResult
1669 MatchNumericComparator(JSContext *cx, const Value &v)
1670 {
1671 if (!v.isObject())
1672 return Match_None;
1673
1674 JSObject &obj = v.toObject();
1675 if (!obj.is<JSFunction>())
1676 return Match_None;
1677
1678 JSFunction *fun = &obj.as<JSFunction>();
1679 if (!fun->isInterpreted())
1680 return Match_None;
1681
1682 JSScript *script = fun->getOrCreateScript(cx);
1683 if (!script)
1684 return Match_Failure;
1685
1686 jsbytecode *pc = script->code();
1687
1688 uint16_t arg0, arg1;
1689 if (JSOp(*pc) != JSOP_GETARG)
1690 return Match_None;
1691 arg0 = GET_ARGNO(pc);
1692 pc += JSOP_GETARG_LENGTH;
1693
1694 if (JSOp(*pc) != JSOP_GETARG)
1695 return Match_None;
1696 arg1 = GET_ARGNO(pc);
1697 pc += JSOP_GETARG_LENGTH;
1698
1699 if (JSOp(*pc) != JSOP_SUB)
1700 return Match_None;
1701 pc += JSOP_SUB_LENGTH;
1702
1703 if (JSOp(*pc) != JSOP_RETURN)
1704 return Match_None;
1705
1706 if (arg0 == 0 && arg1 == 1)
1707 return Match_LeftMinusRight;
1708
1709 if (arg0 == 1 && arg1 == 0)
1710 return Match_RightMinusLeft;
1711
1712 return Match_None;
1713 }
1714
1715 template<typename K, typename C>
1716 static inline bool
1717 MergeSortByKey(K keys, size_t len, K scratch, C comparator, AutoValueVector *vec)
1718 {
1719 MOZ_ASSERT(vec->length() >= len);
1720
1721 /* Sort keys. */
1722 if (!MergeSort(keys, len, scratch, comparator))
1723 return false;
1724
1725 /*
1726 * Reorder vec by keys in-place, going element by element. When an out-of-
1727 * place element is encountered, move that element to its proper position,
1728 * displacing whatever element was at *that* point to its proper position,
1729 * and so on until an element must be moved to the current position.
1730 *
1731 * At each outer iteration all elements up to |i| are sorted. If
1732 * necessary each inner iteration moves some number of unsorted elements
1733 * (including |i|) directly to sorted position. Thus on completion |*vec|
1734 * is sorted, and out-of-position elements have moved once. Complexity is
1735 * Θ(len) + O(len) == O(2*len), with each element visited at most twice.
1736 */
1737 for (size_t i = 0; i < len; i++) {
1738 size_t j = keys[i].elementIndex;
1739 if (i == j)
1740 continue; // fixed point
1741
1742 MOZ_ASSERT(j > i, "Everything less than |i| should be in the right place!");
1743 Value tv = (*vec)[j];
1744 do {
1745 size_t k = keys[j].elementIndex;
1746 keys[j].elementIndex = j;
1747 (*vec)[j] = (*vec)[k];
1748 j = k;
1749 } while (j != i);
1750
1751 // We could assert the loop invariant that |i == keys[i].elementIndex|
1752 // here if we synced |keys[i].elementIndex|. But doing so would render
1753 // the assertion vacuous, so don't bother, even in debug builds.
1754 (*vec)[i] = tv;
1755 }
1756
1757 return true;
1758 }
1759
1760 /*
1761 * Sort Values as strings.
1762 *
1763 * To minimize #conversions, SortLexicographically() first converts all Values
1764 * to strings at once, then sorts the elements by these cached strings.
1765 */
1766 bool
1767 SortLexicographically(JSContext *cx, AutoValueVector *vec, size_t len)
1768 {
1769 JS_ASSERT(vec->length() >= len);
1770
1771 StringBuffer sb(cx);
1772 Vector<StringifiedElement, 0, TempAllocPolicy> strElements(cx);
1773
1774 /* MergeSort uses the upper half as scratch space. */
1775 if (!strElements.reserve(2 * len))
1776 return false;
1777
1778 /* Convert Values to strings. */
1779 size_t cursor = 0;
1780 for (size_t i = 0; i < len; i++) {
1781 if (!CheckForInterrupt(cx))
1782 return false;
1783
1784 if (!ValueToStringBuffer(cx, (*vec)[i], sb))
1785 return false;
1786
1787 StringifiedElement el = { cursor, sb.length(), i };
1788 strElements.infallibleAppend(el);
1789 cursor = sb.length();
1790 }
1791
1792 /* Resize strElements so we can perform MergeSort. */
1793 JS_ALWAYS_TRUE(strElements.resize(2 * len));
1794
1795 /* Sort Values in vec alphabetically. */
1796 return MergeSortByKey(strElements.begin(), len, strElements.begin() + len,
1797 SortComparatorStringifiedElements(cx, sb), vec);
1798 }
1799
1800 /*
1801 * Sort Values as numbers.
1802 *
1803 * To minimize #conversions, SortNumerically first converts all Values to
1804 * numerics at once, then sorts the elements by these cached numerics.
1805 */
1806 bool
1807 SortNumerically(JSContext *cx, AutoValueVector *vec, size_t len, ComparatorMatchResult comp)
1808 {
1809 JS_ASSERT(vec->length() >= len);
1810
1811 Vector<NumericElement, 0, TempAllocPolicy> numElements(cx);
1812
1813 /* MergeSort uses the upper half as scratch space. */
1814 if (!numElements.reserve(2 * len))
1815 return false;
1816
1817 /* Convert Values to numerics. */
1818 for (size_t i = 0; i < len; i++) {
1819 if (!CheckForInterrupt(cx))
1820 return false;
1821
1822 double dv;
1823 if (!ToNumber(cx, vec->handleAt(i), &dv))
1824 return false;
1825
1826 NumericElement el = { dv, i };
1827 numElements.infallibleAppend(el);
1828 }
1829
1830 /* Resize strElements so we can perform MergeSort. */
1831 JS_ALWAYS_TRUE(numElements.resize(2 * len));
1832
1833 /* Sort Values in vec numerically. */
1834 return MergeSortByKey(numElements.begin(), len, numElements.begin() + len,
1835 SortComparatorNumerics[comp], vec);
1836 }
1837
1838 } /* namespace anonymous */
1839
1840 bool
1841 js::array_sort(JSContext *cx, unsigned argc, Value *vp)
1842 {
1843 CallArgs args = CallArgsFromVp(argc, vp);
1844
1845 RootedValue fvalRoot(cx);
1846 Value &fval = fvalRoot.get();
1847
1848 if (args.hasDefined(0)) {
1849 if (args[0].isPrimitive()) {
1850 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_SORT_ARG);
1851 return false;
1852 }
1853 fval = args[0]; /* non-default compare function */
1854 } else {
1855 fval.setNull();
1856 }
1857
1858 RootedObject obj(cx, ToObject(cx, args.thisv()));
1859 if (!obj)
1860 return false;
1861
1862 uint32_t len;
1863 if (!GetLengthProperty(cx, obj, &len))
1864 return false;
1865 if (len < 2) {
1866 /* [] and [a] remain unchanged when sorted. */
1867 args.rval().setObject(*obj);
1868 return true;
1869 }
1870
1871 /*
1872 * We need a temporary array of 2 * len Value to hold the array elements
1873 * and the scratch space for merge sort. Check that its size does not
1874 * overflow size_t, which would allow for indexing beyond the end of the
1875 * malloc'd vector.
1876 */
1877 #if JS_BITS_PER_WORD == 32
1878 if (size_t(len) > size_t(-1) / (2 * sizeof(Value))) {
1879 js_ReportAllocationOverflow(cx);
1880 return false;
1881 }
1882 #endif
1883
1884 /*
1885 * Initialize vec as a root. We will clear elements of vec one by
1886 * one while increasing the rooted amount of vec when we know that the
1887 * property at the corresponding index exists and its value must be rooted.
1888 *
1889 * In this way when sorting a huge mostly sparse array we will not
1890 * access the tail of vec corresponding to properties that do not
1891 * exist, allowing OS to avoiding committing RAM. See bug 330812.
1892 */
1893 size_t n, undefs;
1894 {
1895 AutoValueVector vec(cx);
1896 if (!vec.reserve(2 * size_t(len)))
1897 return false;
1898
1899 /*
1900 * By ECMA 262, 15.4.4.11, a property that does not exist (which we
1901 * call a "hole") is always greater than an existing property with
1902 * value undefined and that is always greater than any other property.
1903 * Thus to sort holes and undefs we simply count them, sort the rest
1904 * of elements, append undefs after them and then make holes after
1905 * undefs.
1906 */
1907 undefs = 0;
1908 bool allStrings = true;
1909 bool allInts = true;
1910 RootedValue v(cx);
1911 for (uint32_t i = 0; i < len; i++) {
1912 if (!CheckForInterrupt(cx))
1913 return false;
1914
1915 /* Clear vec[newlen] before including it in the rooted set. */
1916 bool hole;
1917 if (!GetElement(cx, obj, i, &hole, &v))
1918 return false;
1919 if (hole)
1920 continue;
1921 if (v.isUndefined()) {
1922 ++undefs;
1923 continue;
1924 }
1925 vec.infallibleAppend(v);
1926 allStrings = allStrings && v.isString();
1927 allInts = allInts && v.isInt32();
1928 }
1929
1930
1931 /*
1932 * If the array only contains holes, we're done. But if it contains
1933 * undefs, those must be sorted to the front of the array.
1934 */
1935 n = vec.length();
1936 if (n == 0 && undefs == 0) {
1937 args.rval().setObject(*obj);
1938 return true;
1939 }
1940
1941 /* Here len == n + undefs + number_of_holes. */
1942 if (fval.isNull()) {
1943 /*
1944 * Sort using the default comparator converting all elements to
1945 * strings.
1946 */
1947 if (allStrings) {
1948 JS_ALWAYS_TRUE(vec.resize(n * 2));
1949 if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorStrings(cx)))
1950 return false;
1951 } else if (allInts) {
1952 JS_ALWAYS_TRUE(vec.resize(n * 2));
1953 if (!MergeSort(vec.begin(), n, vec.begin() + n,
1954 SortComparatorLexicographicInt32())) {
1955 return false;
1956 }
1957 } else {
1958 if (!SortLexicographically(cx, &vec, n))
1959 return false;
1960 }
1961 } else {
1962 ComparatorMatchResult comp = MatchNumericComparator(cx, fval);
1963 if (comp == Match_Failure)
1964 return false;
1965
1966 if (comp != Match_None) {
1967 if (allInts) {
1968 JS_ALWAYS_TRUE(vec.resize(n * 2));
1969 if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorInt32s[comp]))
1970 return false;
1971 } else {
1972 if (!SortNumerically(cx, &vec, n, comp))
1973 return false;
1974 }
1975 } else {
1976 FastInvokeGuard fig(cx, fval);
1977 MOZ_ASSERT(!InParallelSection(),
1978 "Array.sort() can't currently be used from parallel code");
1979 JS_ALWAYS_TRUE(vec.resize(n * 2));
1980 if (!MergeSort(vec.begin(), n, vec.begin() + n,
1981 SortComparatorFunction(cx, fval, fig)))
1982 {
1983 return false;
1984 }
1985 }
1986 }
1987
1988 if (!InitArrayElements(cx, obj, 0, uint32_t(n), vec.begin(), DontUpdateTypes))
1989 return false;
1990 }
1991
1992 /* Set undefs that sorted after the rest of elements. */
1993 while (undefs != 0) {
1994 --undefs;
1995 if (!CheckForInterrupt(cx) || !SetArrayElement(cx, obj, n++, UndefinedHandleValue))
1996 return false;
1997 }
1998
1999 /* Re-create any holes that sorted to the end of the array. */
2000 while (len > n) {
2001 if (!CheckForInterrupt(cx) || !DeletePropertyOrThrow(cx, obj, --len))
2002 return false;
2003 }
2004 args.rval().setObject(*obj);
2005 return true;
2006 }
2007
2008 bool
2009 js::NewbornArrayPush(JSContext *cx, HandleObject obj, const Value &v)
2010 {
2011 Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
2012
2013 JS_ASSERT(!v.isMagic());
2014 JS_ASSERT(arr->lengthIsWritable());
2015
2016 uint32_t length = arr->length();
2017 JS_ASSERT(length <= arr->getDenseCapacity());
2018
2019 if (!arr->ensureElements(cx, length + 1))
2020 return false;
2021
2022 arr->setDenseInitializedLength(length + 1);
2023 arr->setLengthInt32(length + 1);
2024 arr->initDenseElementWithType(cx, length, v);
2025 return true;
2026 }
2027
2028 /* ES5 15.4.4.7 */
2029 bool
2030 js::array_push(JSContext *cx, unsigned argc, Value *vp)
2031 {
2032 CallArgs args = CallArgsFromVp(argc, vp);
2033
2034 /* Step 1. */
2035 RootedObject obj(cx, ToObject(cx, args.thisv()));
2036 if (!obj)
2037 return false;
2038
2039 /* Steps 2-3. */
2040 uint32_t length;
2041 if (!GetLengthProperty(cx, obj, &length))
2042 return false;
2043
2044 /* Fast path for native objects with dense elements. */
2045 do {
2046 if (!obj->isNative() || obj->is<TypedArrayObject>())
2047 break;
2048
2049 if (obj->is<ArrayObject>() && !obj->as<ArrayObject>().lengthIsWritable())
2050 break;
2051
2052 if (ObjectMayHaveExtraIndexedProperties(obj))
2053 break;
2054
2055 uint32_t argCount = args.length();
2056 JSObject::EnsureDenseResult result = obj->ensureDenseElements(cx, length, argCount);
2057 if (result == JSObject::ED_FAILED)
2058 return false;
2059
2060 if (result == JSObject::ED_OK) {
2061 for (uint32_t i = 0, index = length; i < argCount; index++, i++)
2062 obj->setDenseElementWithType(cx, index, args[i]);
2063 uint32_t newlength = length + argCount;
2064 args.rval().setNumber(newlength);
2065 if (obj->is<ArrayObject>()) {
2066 obj->as<ArrayObject>().setLengthInt32(newlength);
2067 return true;
2068 }
2069 return SetLengthProperty(cx, obj, newlength);
2070 }
2071
2072 MOZ_ASSERT(result == JSObject::ED_SPARSE);
2073 } while (false);
2074
2075 /* Steps 4-5. */
2076 if (!InitArrayElements(cx, obj, length, args.length(), args.array(), UpdateTypes))
2077 return false;
2078
2079 /* Steps 6-7. */
2080 double newlength = length + double(args.length());
2081 args.rval().setNumber(newlength);
2082 return SetLengthProperty(cx, obj, newlength);
2083 }
2084
2085 /* ES6 20130308 draft 15.4.4.6. */
2086 bool
2087 js::array_pop(JSContext *cx, unsigned argc, Value *vp)
2088 {
2089 CallArgs args = CallArgsFromVp(argc, vp);
2090
2091 /* Step 1. */
2092 RootedObject obj(cx, ToObject(cx, args.thisv()));
2093 if (!obj)
2094 return false;
2095
2096 /* Steps 2-3. */
2097 uint32_t index;
2098 if (!GetLengthProperty(cx, obj, &index))
2099 return false;
2100
2101 /* Steps 4-5. */
2102 if (index == 0) {
2103 /* Step 4b. */
2104 args.rval().setUndefined();
2105 } else {
2106 /* Step 5a. */
2107 index--;
2108
2109 /* Step 5b, 5e. */
2110 bool hole;
2111 if (!GetElement(cx, obj, index, &hole, args.rval()))
2112 return false;
2113
2114 /* Step 5c. */
2115 if (!hole && !DeletePropertyOrThrow(cx, obj, index))
2116 return false;
2117 }
2118
2119 // If this was an array, then there are no elements above the one we just
2120 // deleted (if we deleted an element). Thus we can shrink the dense
2121 // initialized length accordingly. (This is fine even if the array length
2122 // is non-writable: length-changing occurs after element-deletion effects.)
2123 // Don't do anything if this isn't an array, as any deletion above has no
2124 // effect on any elements after the "last" one indicated by the "length"
2125 // property.
2126 if (obj->is<ArrayObject>() && obj->getDenseInitializedLength() > index)
2127 obj->setDenseInitializedLength(index);
2128
2129 /* Steps 4a, 5d. */
2130 return SetLengthProperty(cx, obj, index);
2131 }
2132
2133 void
2134 js::ArrayShiftMoveElements(JSObject *obj)
2135 {
2136 JS_ASSERT(obj->is<ArrayObject>());
2137 JS_ASSERT(obj->as<ArrayObject>().lengthIsWritable());
2138
2139 /*
2140 * At this point the length and initialized length have already been
2141 * decremented and the result fetched, so just shift the array elements
2142 * themselves.
2143 */
2144 uint32_t initlen = obj->getDenseInitializedLength();
2145 obj->moveDenseElementsNoPreBarrier(0, 1, initlen);
2146 }
2147
2148 /* ES5 15.4.4.9 */
2149 bool
2150 js::array_shift(JSContext *cx, unsigned argc, Value *vp)
2151 {
2152 CallArgs args = CallArgsFromVp(argc, vp);
2153
2154 /* Step 1. */
2155 RootedObject obj(cx, ToObject(cx, args.thisv()));
2156 if (!obj)
2157 return false;
2158
2159 /* Steps 2-3. */
2160 uint32_t len;
2161 if (!GetLengthProperty(cx, obj, &len))
2162 return false;
2163
2164 /* Step 4. */
2165 if (len == 0) {
2166 /* Step 4a. */
2167 if (!SetLengthProperty(cx, obj, 0))
2168 return false;
2169
2170 /* Step 4b. */
2171 args.rval().setUndefined();
2172 return true;
2173 }
2174
2175 uint32_t newlen = len - 1;
2176
2177 /* Fast paths. */
2178 if (obj->is<ArrayObject>() &&
2179 obj->getDenseInitializedLength() > 0 &&
2180 newlen < obj->getDenseCapacity() &&
2181 !ObjectMayHaveExtraIndexedProperties(obj))
2182 {
2183 args.rval().set(obj->getDenseElement(0));
2184 if (args.rval().isMagic(JS_ELEMENTS_HOLE))
2185 args.rval().setUndefined();
2186
2187 obj->moveDenseElements(0, 1, obj->getDenseInitializedLength() - 1);
2188 obj->setDenseInitializedLength(obj->getDenseInitializedLength() - 1);
2189
2190 if (!SetLengthProperty(cx, obj, newlen))
2191 return false;
2192
2193 return js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(newlen));
2194 }
2195
2196 /* Steps 5, 10. */
2197 bool hole;
2198 if (!GetElement(cx, obj, uint32_t(0), &hole, args.rval()))
2199 return false;
2200
2201 /* Steps 6-7. */
2202 RootedValue value(cx);
2203 for (uint32_t i = 0; i < newlen; i++) {
2204 if (!CheckForInterrupt(cx))
2205 return false;
2206 if (!GetElement(cx, obj, i + 1, &hole, &value))
2207 return false;
2208 if (hole) {
2209 if (!DeletePropertyOrThrow(cx, obj, i))
2210 return false;
2211 } else {
2212 if (!SetArrayElement(cx, obj, i, value))
2213 return false;
2214 }
2215 }
2216
2217 /* Step 8. */
2218 if (!DeletePropertyOrThrow(cx, obj, newlen))
2219 return false;
2220
2221 /* Step 9. */
2222 return SetLengthProperty(cx, obj, newlen);
2223 }
2224
2225 static bool
2226 array_unshift(JSContext *cx, unsigned argc, Value *vp)
2227 {
2228 CallArgs args = CallArgsFromVp(argc, vp);
2229 RootedObject obj(cx, ToObject(cx, args.thisv()));
2230 if (!obj)
2231 return false;
2232
2233 uint32_t length;
2234 if (!GetLengthProperty(cx, obj, &length))
2235 return false;
2236
2237 double newlen = length;
2238 if (args.length() > 0) {
2239 /* Slide up the array to make room for all args at the bottom. */
2240 if (length > 0) {
2241 bool optimized = false;
2242 do {
2243 if (!obj->is<ArrayObject>())
2244 break;
2245 if (ObjectMayHaveExtraIndexedProperties(obj))
2246 break;
2247 if (!obj->as<ArrayObject>().lengthIsWritable())
2248 break;
2249 JSObject::EnsureDenseResult result = obj->ensureDenseElements(cx, length, args.length());
2250 if (result != JSObject::ED_OK) {
2251 if (result == JSObject::ED_FAILED)
2252 return false;
2253 JS_ASSERT(result == JSObject::ED_SPARSE);
2254 break;
2255 }
2256 obj->moveDenseElements(args.length(), 0, length);
2257 for (uint32_t i = 0; i < args.length(); i++)
2258 obj->setDenseElement(i, MagicValue(JS_ELEMENTS_HOLE));
2259 optimized = true;
2260 } while (false);
2261
2262 if (!optimized) {
2263 double last = length;
2264 double upperIndex = last + args.length();
2265 RootedValue value(cx);
2266 do {
2267 --last, --upperIndex;
2268 bool hole;
2269 if (!CheckForInterrupt(cx))
2270 return false;
2271 if (!GetElement(cx, obj, last, &hole, &value))
2272 return false;
2273 if (hole) {
2274 if (!DeletePropertyOrThrow(cx, obj, upperIndex))
2275 return false;
2276 } else {
2277 if (!SetArrayElement(cx, obj, upperIndex, value))
2278 return false;
2279 }
2280 } while (last != 0);
2281 }
2282 }
2283
2284 /* Copy from args to the bottom of the array. */
2285 if (!InitArrayElements(cx, obj, 0, args.length(), args.array(), UpdateTypes))
2286 return false;
2287
2288 newlen += args.length();
2289 }
2290 if (!SetLengthProperty(cx, obj, newlen))
2291 return false;
2292
2293 /* Follow Perl by returning the new array length. */
2294 args.rval().setNumber(newlen);
2295 return true;
2296 }
2297
2298 static inline void
2299 TryReuseArrayType(JSObject *obj, ArrayObject *narr)
2300 {
2301 /*
2302 * Try to change the type of a newly created array narr to the same type
2303 * as obj. This can only be performed if the original object is an array
2304 * and has the same prototype.
2305 */
2306 JS_ASSERT(narr->getProto()->hasNewType(&ArrayObject::class_, narr->type()));
2307
2308 if (obj->is<ArrayObject>() && !obj->hasSingletonType() && obj->getProto() == narr->getProto())
2309 narr->setType(obj->type());
2310 }
2311
2312 /*
2313 * Returns true if this is a dense array whose |count| properties starting from
2314 * |startingIndex| may be accessed (get, set, delete) directly through its
2315 * contiguous vector of elements without fear of getters, setters, etc. along
2316 * the prototype chain, or of enumerators requiring notification of
2317 * modifications.
2318 */
2319 static inline bool
2320 CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t count, JSContext *cx)
2321 {
2322 /* If the desired properties overflow dense storage, we can't optimize. */
2323 if (UINT32_MAX - startingIndex < count)
2324 return false;
2325
2326 /* There's no optimizing possible if it's not an array. */
2327 if (!arr->is<ArrayObject>())
2328 return false;
2329
2330 /*
2331 * Don't optimize if the array might be in the midst of iteration. We
2332 * rely on this to be able to safely move dense array elements around with
2333 * just a memmove (see JSObject::moveDenseArrayElements), without worrying
2334 * about updating any in-progress enumerators for properties implicitly
2335 * deleted if a hole is moved from one location to another location not yet
2336 * visited. See bug 690622.
2337 *
2338 * Another potential wrinkle: what if the enumeration is happening on an
2339 * object which merely has |arr| on its prototype chain? It turns out this
2340 * case can't happen, because any dense array used as the prototype of
2341 * another object is first slowified, for type inference's sake.
2342 */
2343 types::TypeObject *arrType = arr->getType(cx);
2344 if (MOZ_UNLIKELY(!arrType || arrType->hasAllFlags(OBJECT_FLAG_ITERATED)))
2345 return false;
2346
2347 /*
2348 * Now watch out for getters and setters along the prototype chain or in
2349 * other indexed properties on the object. (Note that non-writable length
2350 * is subsumed by the initializedLength comparison.)
2351 */
2352 return !ObjectMayHaveExtraIndexedProperties(arr) &&
2353 startingIndex + count <= arr->getDenseInitializedLength();
2354 }
2355
2356 /* ES5 15.4.4.12. */
2357 bool
2358 js::array_splice(JSContext *cx, unsigned argc, Value *vp)
2359 {
2360 return array_splice_impl(cx, argc, vp, true);
2361 }
2362
2363 bool
2364 js::array_splice_impl(JSContext *cx, unsigned argc, Value *vp, bool returnValueIsUsed)
2365 {
2366 CallArgs args = CallArgsFromVp(argc, vp);
2367
2368 /* Step 1. */
2369 RootedObject obj(cx, ToObject(cx, args.thisv()));
2370 if (!obj)
2371 return false;
2372
2373 /* Steps 3-4. */
2374 uint32_t len;
2375 if (!GetLengthProperty(cx, obj, &len))
2376 return false;
2377
2378 /* Step 5. */
2379 double relativeStart;
2380 if (!ToInteger(cx, args.get(0), &relativeStart))
2381 return false;
2382
2383 /* Step 6. */
2384 uint32_t actualStart;
2385 if (relativeStart < 0)
2386 actualStart = Max(len + relativeStart, 0.0);
2387 else
2388 actualStart = Min(relativeStart, double(len));
2389
2390 /* Step 7. */
2391 uint32_t actualDeleteCount;
2392 if (args.length() != 1) {
2393 double deleteCountDouble;
2394 RootedValue cnt(cx, args.length() >= 2 ? args[1] : Int32Value(0));
2395 if (!ToInteger(cx, cnt, &deleteCountDouble))
2396 return false;
2397 actualDeleteCount = Min(Max(deleteCountDouble, 0.0), double(len - actualStart));
2398 } else {
2399 /*
2400 * Non-standard: if start was specified but deleteCount was omitted,
2401 * delete to the end of the array. See bug 668024 for discussion.
2402 */
2403 actualDeleteCount = len - actualStart;
2404 }
2405
2406 JS_ASSERT(len - actualStart >= actualDeleteCount);
2407
2408 /* Steps 2, 8-9. */
2409 Rooted<ArrayObject*> arr(cx);
2410 if (CanOptimizeForDenseStorage(obj, actualStart, actualDeleteCount, cx)) {
2411 if (returnValueIsUsed) {
2412 arr = NewDenseCopiedArray(cx, actualDeleteCount, obj, actualStart);
2413 if (!arr)
2414 return false;
2415 TryReuseArrayType(obj, arr);
2416 }
2417 } else {
2418 arr = NewDenseAllocatedArray(cx, actualDeleteCount);
2419 if (!arr)
2420 return false;
2421 TryReuseArrayType(obj, arr);
2422
2423 RootedValue fromValue(cx);
2424 for (uint32_t k = 0; k < actualDeleteCount; k++) {
2425 bool hole;
2426 if (!CheckForInterrupt(cx) ||
2427 !GetElement(cx, obj, actualStart + k, &hole, &fromValue) ||
2428 (!hole && !JSObject::defineElement(cx, arr, k, fromValue)))
2429 {
2430 return false;
2431 }
2432 }
2433 }
2434
2435 /* Step 11. */
2436 uint32_t itemCount = (args.length() >= 2) ? (args.length() - 2) : 0;
2437
2438 if (itemCount < actualDeleteCount) {
2439 /* Step 12: the array is being shrunk. */
2440 uint32_t sourceIndex = actualStart + actualDeleteCount;
2441 uint32_t targetIndex = actualStart + itemCount;
2442 uint32_t finalLength = len - actualDeleteCount + itemCount;
2443
2444 if (CanOptimizeForDenseStorage(obj, 0, len, cx)) {
2445 /* Steps 12(a)-(b). */
2446 obj->moveDenseElements(targetIndex, sourceIndex, len - sourceIndex);
2447
2448 /*
2449 * Update the initialized length. Do so before shrinking so that we
2450 * can apply the write barrier to the old slots.
2451 */
2452 obj->setDenseInitializedLength(finalLength);
2453
2454 /* Steps 12(c)-(d). */
2455 obj->shrinkElements(cx, finalLength);
2456 } else {
2457 /*
2458 * This is all very slow if the length is very large. We don't yet
2459 * have the ability to iterate in sorted order, so we just do the
2460 * pessimistic thing and let CheckForInterrupt handle the
2461 * fallout.
2462 */
2463
2464 /* Steps 12(a)-(b). */
2465 RootedValue fromValue(cx);
2466 for (uint32_t from = sourceIndex, to = targetIndex; from < len; from++, to++) {
2467 if (!CheckForInterrupt(cx))
2468 return false;
2469
2470 bool hole;
2471 if (!GetElement(cx, obj, from, &hole, &fromValue))
2472 return false;
2473 if (hole) {
2474 if (!DeletePropertyOrThrow(cx, obj, to))
2475 return false;
2476 } else {
2477 if (!SetArrayElement(cx, obj, to, fromValue))
2478 return false;
2479 }
2480 }
2481
2482 /* Steps 12(c)-(d). */
2483 for (uint32_t k = len; k > finalLength; k--) {
2484 if (!DeletePropertyOrThrow(cx, obj, k - 1))
2485 return false;
2486 }
2487 }
2488 } else if (itemCount > actualDeleteCount) {
2489 /* Step 13. */
2490
2491 /*
2492 * Optimize only if the array is already dense and we can extend it to
2493 * its new length. It would be wrong to extend the elements here for a
2494 * number of reasons.
2495 *
2496 * First, this could cause us to fall into the fast-path below. This
2497 * would cause elements to be moved into places past the non-writable
2498 * length. And when the dense initialized length is updated, that'll
2499 * cause the |in| operator to think that those elements actually exist,
2500 * even though, properly, setting them must fail.
2501 *
2502 * Second, extending the elements here will trigger assertions inside
2503 * ensureDenseElements that the elements aren't being extended past the
2504 * length of a non-writable array. This is because extending elements
2505 * will extend capacity -- which might extend them past a non-writable
2506 * length, violating the |capacity <= length| invariant for such
2507 * arrays. And that would make the various JITted fast-path method
2508 * implementations of [].push, [].unshift, and so on wrong.
2509 *
2510 * If the array length is non-writable, this method *will* throw. For
2511 * simplicity, have the slow-path code do it. (Also note that the slow
2512 * path may validly *not* throw -- if all the elements being moved are
2513 * holes.)
2514 */
2515 if (obj->is<ArrayObject>()) {
2516 Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
2517 if (arr->lengthIsWritable()) {
2518 JSObject::EnsureDenseResult res =
2519 arr->ensureDenseElements(cx, arr->length(), itemCount - actualDeleteCount);
2520 if (res == JSObject::ED_FAILED)
2521 return false;
2522 }
2523 }
2524
2525 if (CanOptimizeForDenseStorage(obj, len, itemCount - actualDeleteCount, cx)) {
2526 obj->moveDenseElements(actualStart + itemCount,
2527 actualStart + actualDeleteCount,
2528 len - (actualStart + actualDeleteCount));
2529 obj->setDenseInitializedLength(len + itemCount - actualDeleteCount);
2530 } else {
2531 RootedValue fromValue(cx);
2532 for (double k = len - actualDeleteCount; k > actualStart; k--) {
2533 if (!CheckForInterrupt(cx))
2534 return false;
2535
2536 double from = k + actualDeleteCount - 1;
2537 double to = k + itemCount - 1;
2538
2539 bool hole;
2540 if (!GetElement(cx, obj, from, &hole, &fromValue))
2541 return false;
2542
2543 if (hole) {
2544 if (!DeletePropertyOrThrow(cx, obj, to))
2545 return false;
2546 } else {
2547 if (!SetArrayElement(cx, obj, to, fromValue))
2548 return false;
2549 }
2550 }
2551 }
2552 }
2553
2554 /* Step 10. */
2555 Value *items = args.array() + 2;
2556
2557 /* Steps 14-15. */
2558 for (uint32_t k = actualStart, i = 0; i < itemCount; i++, k++) {
2559 if (!SetArrayElement(cx, obj, k, HandleValue::fromMarkedLocation(&items[i])))
2560 return false;
2561 }
2562
2563 /* Step 16. */
2564 double finalLength = double(len) - actualDeleteCount + itemCount;
2565 if (!SetLengthProperty(cx, obj, finalLength))
2566 return false;
2567
2568 /* Step 17. */
2569 if (returnValueIsUsed)
2570 args.rval().setObject(*arr);
2571
2572 return true;
2573 }
2574
2575 #ifdef JS_ION
2576 bool
2577 js::array_concat_dense(JSContext *cx, Handle<ArrayObject*> arr1, Handle<ArrayObject*> arr2,
2578 Handle<ArrayObject*> result)
2579 {
2580 uint32_t initlen1 = arr1->getDenseInitializedLength();
2581 JS_ASSERT(initlen1 == arr1->length());
2582
2583 uint32_t initlen2 = arr2->getDenseInitializedLength();
2584 JS_ASSERT(initlen2 == arr2->length());
2585
2586 /* No overflow here due to nelements limit. */
2587 uint32_t len = initlen1 + initlen2;
2588
2589 if (!result->ensureElements(cx, len))
2590 return false;
2591
2592 JS_ASSERT(!result->getDenseInitializedLength());
2593 result->setDenseInitializedLength(len);
2594
2595 result->initDenseElements(0, arr1->getDenseElements(), initlen1);
2596 result->initDenseElements(initlen1, arr2->getDenseElements(), initlen2);
2597 result->setLengthInt32(len);
2598 return true;
2599 }
2600 #endif /* JS_ION */
2601
2602 /*
2603 * Python-esque sequence operations.
2604 */
2605 bool
2606 js::array_concat(JSContext *cx, unsigned argc, Value *vp)
2607 {
2608 CallArgs args = CallArgsFromVp(argc, vp);
2609
2610 /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */
2611 Value *p = args.array() - 1;
2612
2613 /* Create a new Array object and root it using *vp. */
2614 RootedObject aobj(cx, ToObject(cx, args.thisv()));
2615 if (!aobj)
2616 return false;
2617
2618 Rooted<ArrayObject*> narr(cx);
2619 uint32_t length;
2620 if (aobj->is<ArrayObject>() && !aobj->isIndexed()) {
2621 length = aobj->as<ArrayObject>().length();
2622 uint32_t initlen = aobj->getDenseInitializedLength();
2623 narr = NewDenseCopiedArray(cx, initlen, aobj, 0);
2624 if (!narr)
2625 return false;
2626 TryReuseArrayType(aobj, narr);
2627 narr->setLength(cx, length);
2628 args.rval().setObject(*narr);
2629 if (argc == 0)
2630 return true;
2631 argc--;
2632 p++;
2633 } else {
2634 narr = NewDenseEmptyArray(cx);
2635 if (!narr)
2636 return false;
2637 args.rval().setObject(*narr);
2638 length = 0;
2639 }
2640
2641 /* Loop over [0, argc] to concat args into narr, expanding all Arrays. */
2642 for (unsigned i = 0; i <= argc; i++) {
2643 if (!CheckForInterrupt(cx))
2644 return false;
2645 HandleValue v = HandleValue::fromMarkedLocation(&p[i]);
2646 if (v.isObject()) {
2647 RootedObject obj(cx, &v.toObject());
2648 if (ObjectClassIs(obj, ESClass_Array, cx)) {
2649 uint32_t alength;
2650 if (!GetLengthProperty(cx, obj, &alength))
2651 return false;
2652 RootedValue tmp(cx);
2653 for (uint32_t slot = 0; slot < alength; slot++) {
2654 bool hole;
2655 if (!CheckForInterrupt(cx) || !GetElement(cx, obj, slot, &hole, &tmp))
2656 return false;
2657
2658 /*
2659 * Per ECMA 262, 15.4.4.4, step 9, ignore nonexistent
2660 * properties.
2661 */
2662 if (!hole && !SetArrayElement(cx, narr, length + slot, tmp))
2663 return false;
2664 }
2665 length += alength;
2666 continue;
2667 }
2668 }
2669
2670 if (!SetArrayElement(cx, narr, length, v))
2671 return false;
2672 length++;
2673 }
2674
2675 return SetLengthProperty(cx, narr, length);
2676 }
2677
2678 static bool
2679 array_slice(JSContext *cx, unsigned argc, Value *vp)
2680 {
2681 CallArgs args = CallArgsFromVp(argc, vp);
2682
2683 RootedObject obj(cx, ToObject(cx, args.thisv()));
2684 if (!obj)
2685 return false;
2686
2687 uint32_t length;
2688 if (!GetLengthProperty(cx, obj, &length))
2689 return false;
2690
2691 uint32_t begin = 0;
2692 uint32_t end = length;
2693 if (args.length() > 0) {
2694 double d;
2695 if (!ToInteger(cx, args[0], &d))
2696 return false;
2697 if (d < 0) {
2698 d += length;
2699 if (d < 0)
2700 d = 0;
2701 } else if (d > length) {
2702 d = length;
2703 }
2704 begin = (uint32_t)d;
2705
2706 if (args.hasDefined(1)) {
2707 if (!ToInteger(cx, args[1], &d))
2708 return false;
2709 if (d < 0) {
2710 d += length;
2711 if (d < 0)
2712 d = 0;
2713 } else if (d > length) {
2714 d = length;
2715 }
2716 end = (uint32_t)d;
2717 }
2718 }
2719
2720 if (begin > end)
2721 begin = end;
2722
2723 Rooted<ArrayObject*> narr(cx);
2724 narr = NewDenseAllocatedArray(cx, end - begin);
2725 if (!narr)
2726 return false;
2727 TryReuseArrayType(obj, narr);
2728
2729 if (obj->is<ArrayObject>() && !ObjectMayHaveExtraIndexedProperties(obj)) {
2730 if (obj->getDenseInitializedLength() > begin) {
2731 uint32_t numSourceElements = obj->getDenseInitializedLength() - begin;
2732 uint32_t initLength = Min(numSourceElements, end - begin);
2733 narr->setDenseInitializedLength(initLength);
2734 narr->initDenseElements(0, &obj->getDenseElement(begin), initLength);
2735 }
2736 args.rval().setObject(*narr);
2737 return true;
2738 }
2739
2740 if (js::SliceOp op = obj->getOps()->slice) {
2741 // Ensure that we have dense elements, so that DOM can use js::UnsafeDefineElement.
2742 JSObject::EnsureDenseResult result = narr->ensureDenseElements(cx, 0, end - begin);
2743 if (result == JSObject::ED_FAILED)
2744 return false;
2745
2746 if (result == JSObject::ED_OK) {
2747 if (!op(cx, obj, begin, end, narr))
2748 return false;
2749
2750 args.rval().setObject(*narr);
2751 return true;
2752 }
2753
2754 // Fallthrough
2755 JS_ASSERT(result == JSObject::ED_SPARSE);
2756 }
2757
2758
2759 if (!SliceSlowly(cx, obj, obj, begin, end, narr))
2760 return false;
2761
2762 args.rval().setObject(*narr);
2763 return true;
2764 }
2765
2766 JS_FRIEND_API(bool)
2767 js::SliceSlowly(JSContext* cx, HandleObject obj, HandleObject receiver,
2768 uint32_t begin, uint32_t end, HandleObject result)
2769 {
2770 RootedValue value(cx);
2771 for (uint32_t slot = begin; slot < end; slot++) {
2772 bool hole;
2773 if (!CheckForInterrupt(cx) ||
2774 !GetElement(cx, obj, receiver, slot, &hole, &value))
2775 {
2776 return false;
2777 }
2778 if (!hole && !JSObject::defineElement(cx, result, slot - begin, value))
2779 return false;
2780 }
2781 return true;
2782 }
2783
2784 /* ES5 15.4.4.20. */
2785 static bool
2786 array_filter(JSContext *cx, unsigned argc, Value *vp)
2787 {
2788 CallArgs args = CallArgsFromVp(argc, vp);
2789
2790 /* Step 1. */
2791 RootedObject obj(cx, ToObject(cx, args.thisv()));
2792 if (!obj)
2793 return false;
2794
2795 /* Step 2-3. */
2796 uint32_t len;
2797 if (!GetLengthProperty(cx, obj, &len))
2798 return false;
2799
2800 /* Step 4. */
2801 if (args.length() == 0) {
2802 js_ReportMissingArg(cx, args.calleev(), 0);
2803 return false;
2804 }
2805 RootedObject callable(cx, ValueToCallable(cx, args[0], args.length() - 1));
2806 if (!callable)
2807 return false;
2808
2809 /* Step 5. */
2810 RootedValue thisv(cx, args.length() >= 2 ? args[1] : UndefinedValue());
2811
2812 /* Step 6. */
2813 RootedObject arr(cx, NewDenseAllocatedArray(cx, 0));
2814 if (!arr)
2815 return false;
2816 TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array);
2817 if (!newtype)
2818 return false;
2819 arr->setType(newtype);
2820
2821 /* Step 7. */
2822 uint32_t k = 0;
2823
2824 /* Step 8. */
2825 uint32_t to = 0;
2826
2827 /* Step 9. */
2828 JS_ASSERT(!InParallelSection());
2829 FastInvokeGuard fig(cx, ObjectValue(*callable));
2830 InvokeArgs &args2 = fig.args();
2831 RootedValue kValue(cx);
2832 while (k < len) {
2833 if (!CheckForInterrupt(cx))
2834 return false;
2835
2836 /* Step a, b, and c.i. */
2837 bool kNotPresent;
2838 if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
2839 return false;
2840
2841 /* Step c.ii-iii. */
2842 if (!kNotPresent) {
2843 if (!args2.init(3))
2844 return false;
2845 args2.setCallee(ObjectValue(*callable));
2846 args2.setThis(thisv);
2847 args2[0].set(kValue);
2848 args2[1].setNumber(k);
2849 args2[2].setObject(*obj);
2850 if (!fig.invoke(cx))
2851 return false;
2852
2853 if (ToBoolean(args2.rval())) {
2854 if (!SetArrayElement(cx, arr, to, kValue))
2855 return false;
2856 to++;
2857 }
2858 }
2859
2860 /* Step d. */
2861 k++;
2862 }
2863
2864 /* Step 10. */
2865 args.rval().setObject(*arr);
2866 return true;
2867 }
2868
2869 static bool
2870 array_isArray(JSContext *cx, unsigned argc, Value *vp)
2871 {
2872 CallArgs args = CallArgsFromVp(argc, vp);
2873 bool isArray = args.length() > 0 && IsObjectWithClass(args[0], ESClass_Array, cx);
2874 args.rval().setBoolean(isArray);
2875 return true;
2876 }
2877
2878 static bool
2879 IsArrayConstructor(const Value &v)
2880 {
2881 // This must only return true if v is *the* Array constructor for the
2882 // current compartment; we rely on the fact that any other Array
2883 // constructor would be represented as a wrapper.
2884 return v.isObject() &&
2885 v.toObject().is<JSFunction>() &&
2886 v.toObject().as<JSFunction>().isNative() &&
2887 v.toObject().as<JSFunction>().native() == js_Array;
2888 }
2889
2890 static bool
2891 ArrayFromCallArgs(JSContext *cx, RootedTypeObject &type, CallArgs &args)
2892 {
2893 if (!InitArrayTypes(cx, type, args.array(), args.length()))
2894 return false;
2895 JSObject *obj = (args.length() == 0)
2896 ? NewDenseEmptyArray(cx)
2897 : NewDenseCopiedArray(cx, args.length(), args.array());
2898 if (!obj)
2899 return false;
2900 obj->setType(type);
2901 args.rval().setObject(*obj);
2902 return true;
2903 }
2904
2905 static bool
2906 array_of(JSContext *cx, unsigned argc, Value *vp)
2907 {
2908 CallArgs args = CallArgsFromVp(argc, vp);
2909
2910 if (IsArrayConstructor(args.thisv()) || !IsConstructor(args.thisv())) {
2911 // IsArrayConstructor(this) will usually be true in practice. This is
2912 // the most common path.
2913 RootedTypeObject type(cx, GetTypeCallerInitObject(cx, JSProto_Array));
2914 if (!type)
2915 return false;
2916 return ArrayFromCallArgs(cx, type, args);
2917 }
2918
2919 // Step 4.
2920 RootedObject obj(cx);
2921 {
2922 RootedValue v(cx);
2923 Value argv[1] = {NumberValue(args.length())};
2924 if (!InvokeConstructor(cx, args.thisv(), 1, argv, v.address()))
2925 return false;
2926 obj = ToObject(cx, v);
2927 if (!obj)
2928 return false;
2929 }
2930
2931 // Step 8.
2932 for (unsigned k = 0; k < args.length(); k++) {
2933 if (!JSObject::defineElement(cx, obj, k, args[k]))
2934 return false;
2935 }
2936
2937 // Steps 9-10.
2938 RootedValue v(cx, NumberValue(args.length()));
2939 if (!JSObject::setProperty(cx, obj, obj, cx->names().length, &v, true))
2940 return false;
2941
2942 // Step 11.
2943 args.rval().setObject(*obj);
2944 return true;
2945 }
2946
2947 #define GENERIC JSFUN_GENERIC_NATIVE
2948
2949 static const JSFunctionSpec array_methods[] = {
2950 #if JS_HAS_TOSOURCE
2951 JS_FN(js_toSource_str, array_toSource, 0,0),
2952 #endif
2953 JS_FN(js_toString_str, array_toString, 0,0),
2954 JS_FN(js_toLocaleString_str,array_toLocaleString,0,0),
2955
2956 /* Perl-ish methods. */
2957 JS_FN("join", array_join, 1,JSFUN_GENERIC_NATIVE),
2958 JS_FN("reverse", array_reverse, 0,JSFUN_GENERIC_NATIVE),
2959 JS_FN("sort", array_sort, 1,JSFUN_GENERIC_NATIVE),
2960 JS_FN("push", array_push, 1,JSFUN_GENERIC_NATIVE),
2961 JS_FN("pop", array_pop, 0,JSFUN_GENERIC_NATIVE),
2962 JS_FN("shift", array_shift, 0,JSFUN_GENERIC_NATIVE),
2963 JS_FN("unshift", array_unshift, 1,JSFUN_GENERIC_NATIVE),
2964 JS_FN("splice", array_splice, 2,JSFUN_GENERIC_NATIVE),
2965
2966 /* Pythonic sequence methods. */
2967 JS_FN("concat", array_concat, 1,JSFUN_GENERIC_NATIVE),
2968 JS_FN("slice", array_slice, 2,JSFUN_GENERIC_NATIVE),
2969
2970 JS_SELF_HOSTED_FN("lastIndexOf", "ArrayLastIndexOf", 1,0),
2971 JS_SELF_HOSTED_FN("indexOf", "ArrayIndexOf", 1,0),
2972 JS_SELF_HOSTED_FN("forEach", "ArrayForEach", 1,0),
2973 JS_SELF_HOSTED_FN("map", "ArrayMap", 1,0),
2974 JS_SELF_HOSTED_FN("reduce", "ArrayReduce", 1,0),
2975 JS_SELF_HOSTED_FN("reduceRight", "ArrayReduceRight", 1,0),
2976 JS_FN("filter", array_filter, 1,JSFUN_GENERIC_NATIVE),
2977 JS_SELF_HOSTED_FN("some", "ArraySome", 1,0),
2978 JS_SELF_HOSTED_FN("every", "ArrayEvery", 1,0),
2979
2980 #ifdef ENABLE_PARALLEL_JS
2981 /* Parallelizable and pure methods. */
2982 JS_SELF_HOSTED_FN("mapPar", "ArrayMapPar", 2,0),
2983 JS_SELF_HOSTED_FN("reducePar", "ArrayReducePar", 2,0),
2984 JS_SELF_HOSTED_FN("scanPar", "ArrayScanPar", 2,0),
2985 JS_SELF_HOSTED_FN("scatterPar", "ArrayScatterPar", 5,0),
2986 JS_SELF_HOSTED_FN("filterPar", "ArrayFilterPar", 2,0),
2987 #endif
2988
2989 /* ES6 additions */
2990 JS_SELF_HOSTED_FN("find", "ArrayFind", 1,0),
2991 JS_SELF_HOSTED_FN("findIndex", "ArrayFindIndex", 1,0),
2992
2993 JS_SELF_HOSTED_FN("fill", "ArrayFill", 3,0),
2994
2995 JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0,0),
2996 JS_SELF_HOSTED_FN("entries", "ArrayEntries", 0,0),
2997 JS_SELF_HOSTED_FN("keys", "ArrayKeys", 0,0),
2998 JS_FS_END
2999 };
3000
3001 static const JSFunctionSpec array_static_methods[] = {
3002 JS_FN("isArray", array_isArray, 1,0),
3003 JS_SELF_HOSTED_FN("lastIndexOf", "ArrayStaticLastIndexOf", 2,0),
3004 JS_SELF_HOSTED_FN("indexOf", "ArrayStaticIndexOf", 2,0),
3005 JS_SELF_HOSTED_FN("forEach", "ArrayStaticForEach", 2,0),
3006 JS_SELF_HOSTED_FN("map", "ArrayStaticMap", 2,0),
3007 JS_SELF_HOSTED_FN("every", "ArrayStaticEvery", 2,0),
3008 JS_SELF_HOSTED_FN("some", "ArrayStaticSome", 2,0),
3009 JS_SELF_HOSTED_FN("reduce", "ArrayStaticReduce", 2,0),
3010 JS_SELF_HOSTED_FN("reduceRight", "ArrayStaticReduceRight", 2,0),
3011 JS_FN("of", array_of, 0,0),
3012
3013 #ifdef ENABLE_PARALLEL_JS
3014 JS_SELF_HOSTED_FN("build", "ArrayStaticBuild", 2,0),
3015 /* Parallelizable and pure static methods. */
3016 JS_SELF_HOSTED_FN("buildPar", "ArrayStaticBuildPar", 3,0),
3017 #endif
3018
3019 JS_FS_END
3020 };
3021
3022 /* ES5 15.4.2 */
3023 bool
3024 js_Array(JSContext *cx, unsigned argc, Value *vp)
3025 {
3026 CallArgs args = CallArgsFromVp(argc, vp);
3027 RootedTypeObject type(cx, GetTypeCallerInitObject(cx, JSProto_Array));
3028 if (!type)
3029 return false;
3030
3031 if (args.length() != 1 || !args[0].isNumber())
3032 return ArrayFromCallArgs(cx, type, args);
3033
3034 uint32_t length;
3035 if (args[0].isInt32()) {
3036 int32_t i = args[0].toInt32();
3037 if (i < 0) {
3038 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
3039 return false;
3040 }
3041 length = uint32_t(i);
3042 } else {
3043 double d = args[0].toDouble();
3044 length = ToUint32(d);
3045 if (d != double(length)) {
3046 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
3047 return false;
3048 }
3049 }
3050
3051 /*
3052 * Allocate dense elements eagerly for small arrays, to avoid reallocating
3053 * elements when filling the array.
3054 */
3055 RootedObject obj(cx);
3056 obj = (length <= ArrayObject::EagerAllocationMaxLength)
3057 ? NewDenseAllocatedArray(cx, length)
3058 : NewDenseUnallocatedArray(cx, length);
3059 if (!obj)
3060 return false;
3061 Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
3062
3063 arr->setType(type);
3064
3065 /* If the length calculation overflowed, make sure that is marked for the new type. */
3066 if (arr->length() > INT32_MAX)
3067 arr->setLength(cx, arr->length());
3068
3069 args.rval().setObject(*arr);
3070 return true;
3071 }
3072
3073 JSObject *
3074 js_InitArrayClass(JSContext *cx, HandleObject obj)
3075 {
3076 JS_ASSERT(obj->isNative());
3077
3078 Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
3079
3080 RootedObject proto(cx, global->getOrCreateObjectPrototype(cx));
3081 if (!proto)
3082 return nullptr;
3083
3084 RootedTypeObject type(cx, cx->getNewType(&ArrayObject::class_, proto.get()));
3085 if (!type)
3086 return nullptr;
3087
3088 JSObject *metadata = nullptr;
3089 if (!NewObjectMetadata(cx, &metadata))
3090 return nullptr;
3091
3092 RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayObject::class_, TaggedProto(proto),
3093 proto->getParent(), metadata,
3094 gc::FINALIZE_OBJECT0));
3095 if (!shape)
3096 return nullptr;
3097
3098 RootedObject arrayProto(cx, JSObject::createArray(cx, gc::FINALIZE_OBJECT4, gc::TenuredHeap, shape, type, 0));
3099 if (!arrayProto || !JSObject::setSingletonType(cx, arrayProto) || !AddLengthProperty(cx, arrayProto))
3100 return nullptr;
3101
3102 RootedFunction ctor(cx);
3103 ctor = global->createConstructor(cx, js_Array, cx->names().Array, 1);
3104 if (!ctor)
3105 return nullptr;
3106
3107 /*
3108 * The default 'new' type of Array.prototype is required by type inference
3109 * to have unknown properties, to simplify handling of e.g. heterogenous
3110 * arrays in JSON and script literals and allows setDenseArrayElement to
3111 * be used without updating the indexed type set for such default arrays.
3112 */
3113 if (!JSObject::setNewTypeUnknown(cx, &ArrayObject::class_, arrayProto))
3114 return nullptr;
3115
3116 if (!LinkConstructorAndPrototype(cx, ctor, arrayProto))
3117 return nullptr;
3118
3119 if (!DefinePropertiesAndBrand(cx, arrayProto, nullptr, array_methods) ||
3120 !DefinePropertiesAndBrand(cx, ctor, nullptr, array_static_methods))
3121 {
3122 return nullptr;
3123 }
3124
3125 if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Array, ctor, arrayProto))
3126 return nullptr;
3127
3128 return arrayProto;
3129 }
3130
3131 /*
3132 * Array allocation functions.
3133 */
3134
3135 static inline bool
3136 EnsureNewArrayElements(ExclusiveContext *cx, JSObject *obj, uint32_t length)
3137 {
3138 /*
3139 * If ensureElements creates dynamically allocated slots, then having
3140 * fixedSlots is a waste.
3141 */
3142 DebugOnly<uint32_t> cap = obj->getDenseCapacity();
3143
3144 if (!obj->ensureElements(cx, length))
3145 return false;
3146
3147 JS_ASSERT_IF(cap, !obj->hasDynamicElements());
3148
3149 return true;
3150 }
3151
3152 template<bool allocateCapacity>
3153 static MOZ_ALWAYS_INLINE ArrayObject *
3154 NewArray(ExclusiveContext *cxArg, uint32_t length,
3155 JSObject *protoArg, NewObjectKind newKind = GenericObject)
3156 {
3157 gc::AllocKind allocKind = GuessArrayGCKind(length);
3158 JS_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_));
3159 allocKind = GetBackgroundAllocKind(allocKind);
3160
3161 NewObjectCache::EntryIndex entry = -1;
3162 if (JSContext *cx = cxArg->maybeJSContext()) {
3163 NewObjectCache &cache = cx->runtime()->newObjectCache;
3164 if (newKind == GenericObject &&
3165 !cx->compartment()->hasObjectMetadataCallback() &&
3166 cache.lookupGlobal(&ArrayObject::class_, cx->global(), allocKind, &entry))
3167 {
3168 gc::InitialHeap heap = GetInitialHeap(newKind, &ArrayObject::class_);
3169 JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, heap);
3170 if (obj) {
3171 /* Fixup the elements pointer and length, which may be incorrect. */
3172 ArrayObject *arr = &obj->as<ArrayObject>();
3173 arr->setFixedElements();
3174 arr->setLength(cx, length);
3175 if (allocateCapacity && !EnsureNewArrayElements(cx, arr, length))
3176 return nullptr;
3177 return arr;
3178 } else {
3179 RootedObject proto(cxArg, protoArg);
3180 obj = cache.newObjectFromHit<CanGC>(cx, entry, heap);
3181 JS_ASSERT(!obj);
3182 protoArg = proto;
3183 }
3184 }
3185 }
3186
3187 RootedObject proto(cxArg, protoArg);
3188 if (protoArg)
3189 JS::PoisonPtr(&protoArg);
3190
3191 if (!proto && !GetBuiltinPrototype(cxArg, JSProto_Array, &proto))
3192 return nullptr;
3193
3194 RootedTypeObject type(cxArg, cxArg->getNewType(&ArrayObject::class_, proto.get()));
3195 if (!type)
3196 return nullptr;
3197
3198 JSObject *metadata = nullptr;
3199 if (!NewObjectMetadata(cxArg, &metadata))
3200 return nullptr;
3201
3202 /*
3203 * Get a shape with zero fixed slots, regardless of the size class.
3204 * See JSObject::createArray.
3205 */
3206 RootedShape shape(cxArg, EmptyShape::getInitialShape(cxArg, &ArrayObject::class_,
3207 TaggedProto(proto), cxArg->global(),
3208 metadata, gc::FINALIZE_OBJECT0));
3209 if (!shape)
3210 return nullptr;
3211
3212 Rooted<ArrayObject*> arr(cxArg, JSObject::createArray(cxArg, allocKind,
3213 GetInitialHeap(newKind, &ArrayObject::class_),
3214 shape, type, length));
3215 if (!arr)
3216 return nullptr;
3217
3218 if (shape->isEmptyShape()) {
3219 if (!AddLengthProperty(cxArg, arr))
3220 return nullptr;
3221 shape = arr->lastProperty();
3222 EmptyShape::insertInitialShape(cxArg, shape, proto);
3223 }
3224
3225 if (newKind == SingletonObject && !JSObject::setSingletonType(cxArg, arr))
3226 return nullptr;
3227
3228 if (entry != -1) {
3229 cxArg->asJSContext()->runtime()->newObjectCache.fillGlobal(entry, &ArrayObject::class_,
3230 cxArg->global(), allocKind, arr);
3231 }
3232
3233 if (allocateCapacity && !EnsureNewArrayElements(cxArg, arr, length))
3234 return nullptr;
3235
3236 probes::CreateObject(cxArg, arr);
3237 return arr;
3238 }
3239
3240 ArrayObject * JS_FASTCALL
3241 js::NewDenseEmptyArray(JSContext *cx, JSObject *proto /* = nullptr */,
3242 NewObjectKind newKind /* = GenericObject */)
3243 {
3244 return NewArray<false>(cx, 0, proto, newKind);
3245 }
3246
3247 ArrayObject * JS_FASTCALL
3248 js::NewDenseAllocatedArray(ExclusiveContext *cx, uint32_t length, JSObject *proto /* = nullptr */,
3249 NewObjectKind newKind /* = GenericObject */)
3250 {
3251 return NewArray<true>(cx, length, proto, newKind);
3252 }
3253
3254 ArrayObject * JS_FASTCALL
3255 js::NewDenseUnallocatedArray(ExclusiveContext *cx, uint32_t length, JSObject *proto /* = nullptr */,
3256 NewObjectKind newKind /* = GenericObject */)
3257 {
3258 return NewArray<false>(cx, length, proto, newKind);
3259 }
3260
3261 ArrayObject *
3262 js::NewDenseCopiedArray(JSContext *cx, uint32_t length, HandleObject src, uint32_t elementOffset,
3263 JSObject *proto /* = nullptr */)
3264 {
3265 JS_ASSERT(!src->isIndexed());
3266
3267 ArrayObject* arr = NewArray<true>(cx, length, proto);
3268 if (!arr)
3269 return nullptr;
3270
3271 JS_ASSERT(arr->getDenseCapacity() >= length);
3272
3273 const Value* vp = src->getDenseElements() + elementOffset;
3274 arr->setDenseInitializedLength(vp ? length : 0);
3275
3276 if (vp)
3277 arr->initDenseElements(0, vp, length);
3278
3279 return arr;
3280 }
3281
3282 // values must point at already-rooted Value objects
3283 ArrayObject *
3284 js::NewDenseCopiedArray(JSContext *cx, uint32_t length, const Value *values,
3285 JSObject *proto /* = nullptr */, NewObjectKind newKind /* = GenericObject */)
3286 {
3287 ArrayObject* arr = NewArray<true>(cx, length, proto);
3288 if (!arr)
3289 return nullptr;
3290
3291 JS_ASSERT(arr->getDenseCapacity() >= length);
3292
3293 arr->setDenseInitializedLength(values ? length : 0);
3294
3295 if (values)
3296 arr->initDenseElements(0, values, length);
3297
3298 return arr;
3299 }
3300
3301 ArrayObject *
3302 js::NewDenseAllocatedArrayWithTemplate(JSContext *cx, uint32_t length, JSObject *templateObject)
3303 {
3304 gc::AllocKind allocKind = GuessArrayGCKind(length);
3305 JS_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_));
3306 allocKind = GetBackgroundAllocKind(allocKind);
3307
3308 RootedTypeObject type(cx, templateObject->type());
3309 if (!type)
3310 return nullptr;
3311
3312 RootedShape shape(cx, templateObject->lastProperty());
3313 if (!shape)
3314 return nullptr;
3315
3316 gc::InitialHeap heap = GetInitialHeap(GenericObject, &ArrayObject::class_);
3317 Rooted<ArrayObject *> arr(cx, JSObject::createArray(cx, allocKind, heap, shape, type, length));
3318 if (!arr)
3319 return nullptr;
3320
3321 if (!EnsureNewArrayElements(cx, arr, length))
3322 return nullptr;
3323
3324 probes::CreateObject(cx, arr);
3325
3326 return arr;
3327 }
3328
3329 #ifdef DEBUG
3330 bool
3331 js_ArrayInfo(JSContext *cx, unsigned argc, Value *vp)
3332 {
3333 CallArgs args = CallArgsFromVp(argc, vp);
3334 JSObject *obj;
3335
3336 for (unsigned i = 0; i < args.length(); i++) {
3337 RootedValue arg(cx, args[i]);
3338
3339 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, arg, NullPtr());
3340 if (!bytes)
3341 return false;
3342 if (arg.isPrimitive() ||
3343 !(obj = arg.toObjectOrNull())->is<ArrayObject>()) {
3344 fprintf(stderr, "%s: not array\n", bytes);
3345 js_free(bytes);
3346 continue;
3347 }
3348 fprintf(stderr, "%s: (len %u", bytes, obj->as<ArrayObject>().length());
3349 fprintf(stderr, ", capacity %u", obj->getDenseCapacity());
3350 fputs(")\n", stderr);
3351 js_free(bytes);
3352 }
3353
3354 args.rval().setUndefined();
3355 return true;
3356 }
3357 #endif

mercurial