Line data Source code
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 "jsarrayinlines.h"
8 :
9 : #include "mozilla/ArrayUtils.h"
10 : #include "mozilla/CheckedInt.h"
11 : #include "mozilla/DebugOnly.h"
12 : #include "mozilla/FloatingPoint.h"
13 : #include "mozilla/MathAlgorithms.h"
14 :
15 : #include <algorithm>
16 :
17 : #include "jsapi.h"
18 : #include "jsatom.h"
19 : #include "jscntxt.h"
20 : #include "jsfriendapi.h"
21 : #include "jsfun.h"
22 : #include "jsiter.h"
23 : #include "jsnum.h"
24 : #include "jsobj.h"
25 : #include "jstypes.h"
26 : #include "jsutil.h"
27 :
28 : #include "ds/Sort.h"
29 : #include "gc/Heap.h"
30 : #include "jit/InlinableNatives.h"
31 : #include "js/Class.h"
32 : #include "js/Conversions.h"
33 : #include "vm/ArgumentsObject.h"
34 : #include "vm/Interpreter.h"
35 : #include "vm/SelfHosting.h"
36 : #include "vm/Shape.h"
37 : #include "vm/StringBuffer.h"
38 : #include "vm/TypedArrayObject.h"
39 : #include "vm/WrapperObject.h"
40 :
41 : #include "jsatominlines.h"
42 :
43 : #include "vm/ArgumentsObject-inl.h"
44 : #include "vm/ArrayObject-inl.h"
45 : #include "vm/Caches-inl.h"
46 : #include "vm/Interpreter-inl.h"
47 : #include "vm/NativeObject-inl.h"
48 : #include "vm/UnboxedObject-inl.h"
49 :
50 : using namespace js;
51 : using namespace js::gc;
52 :
53 : using mozilla::Abs;
54 : using mozilla::ArrayLength;
55 : using mozilla::CeilingLog2;
56 : using mozilla::CheckedInt;
57 : using mozilla::DebugOnly;
58 : using mozilla::IsNaN;
59 :
60 : using JS::AutoCheckCannotGC;
61 : using JS::IsArrayAnswer;
62 : using JS::ToUint32;
63 :
64 : static inline bool
65 2489 : IsBoxedOrUnboxedArray(const JSObject* obj)
66 : {
67 2489 : return obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>();
68 : }
69 :
70 : bool
71 466 : JS::IsArray(JSContext* cx, HandleObject obj, IsArrayAnswer* answer)
72 : {
73 466 : if (IsBoxedOrUnboxedArray(obj)) {
74 315 : *answer = IsArrayAnswer::Array;
75 315 : return true;
76 : }
77 :
78 151 : if (obj->is<ProxyObject>())
79 55 : return Proxy::isArray(cx, obj, answer);
80 :
81 96 : *answer = IsArrayAnswer::NotArray;
82 96 : return true;
83 : }
84 :
85 : bool
86 415 : JS::IsArray(JSContext* cx, HandleObject obj, bool* isArray)
87 : {
88 : IsArrayAnswer answer;
89 415 : if (!IsArray(cx, obj, &answer))
90 0 : return false;
91 :
92 415 : if (answer == IsArrayAnswer::RevokedProxy) {
93 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
94 0 : return false;
95 : }
96 :
97 415 : *isArray = answer == IsArrayAnswer::Array;
98 415 : return true;
99 : }
100 :
101 : // ES2017 7.1.15 ToLength, but clamped to the [0,2^32-2] range.
102 : static bool
103 59 : ToLengthClamped(JSContext* cx, HandleValue v, uint32_t* out)
104 : {
105 59 : if (v.isInt32()) {
106 59 : int32_t i = v.toInt32();
107 59 : *out = i < 0 ? 0 : i;
108 59 : return true;
109 : }
110 : double d;
111 0 : if (v.isDouble()) {
112 0 : d = v.toDouble();
113 : } else {
114 0 : if (!ToNumber(cx, v, &d))
115 0 : return false;
116 : }
117 0 : d = JS::ToInteger(d);
118 0 : if (d <= 0.0)
119 0 : *out = 0;
120 0 : else if (d < double(UINT32_MAX - 1))
121 0 : *out = uint32_t(d);
122 : else
123 0 : *out = UINT32_MAX;
124 0 : return true;
125 : }
126 :
127 : bool
128 1168 : js::GetLengthProperty(JSContext* cx, HandleObject obj, uint32_t* lengthp)
129 : {
130 1168 : if (obj->is<ArrayObject>()) {
131 1109 : *lengthp = obj->as<ArrayObject>().length();
132 1109 : return true;
133 : }
134 :
135 59 : if (obj->is<UnboxedArrayObject>()) {
136 0 : *lengthp = obj->as<UnboxedArrayObject>().length();
137 0 : return true;
138 : }
139 :
140 59 : if (obj->is<ArgumentsObject>()) {
141 17 : ArgumentsObject& argsobj = obj->as<ArgumentsObject>();
142 17 : if (!argsobj.hasOverriddenLength()) {
143 0 : *lengthp = argsobj.initialLength();
144 0 : return true;
145 : }
146 : }
147 :
148 118 : RootedValue value(cx);
149 59 : if (!GetProperty(cx, obj, obj, cx->names().length, &value))
150 0 : return false;
151 :
152 59 : if (!ToLengthClamped(cx, value, lengthp))
153 0 : return false;
154 :
155 59 : return true;
156 : }
157 :
158 : // ES2017 7.1.15 ToLength.
159 : static bool
160 2 : ToLength(JSContext* cx, HandleValue v, uint64_t* out)
161 : {
162 2 : if (v.isInt32()) {
163 2 : int32_t i = v.toInt32();
164 2 : *out = i < 0 ? 0 : i;
165 2 : return true;
166 : }
167 :
168 : double d;
169 0 : if (v.isDouble()) {
170 0 : d = v.toDouble();
171 : } else {
172 0 : if (!ToNumber(cx, v, &d))
173 0 : return false;
174 : }
175 :
176 0 : d = JS::ToInteger(d);
177 0 : if (d <= 0.0)
178 0 : *out = 0;
179 : else
180 0 : *out = uint64_t(Min(d, DOUBLE_INTEGRAL_PRECISION_LIMIT - 1));
181 0 : return true;
182 : }
183 :
184 : static bool
185 2348 : GetLengthProperty(JSContext* cx, HandleObject obj, uint64_t* lengthp)
186 : {
187 2348 : if (obj->is<ArrayObject>()) {
188 2297 : *lengthp = obj->as<ArrayObject>().length();
189 2297 : return true;
190 : }
191 :
192 51 : if (obj->is<UnboxedArrayObject>()) {
193 0 : *lengthp = obj->as<UnboxedArrayObject>().length();
194 0 : return true;
195 : }
196 :
197 51 : if (obj->is<ArgumentsObject>()) {
198 49 : ArgumentsObject& argsobj = obj->as<ArgumentsObject>();
199 49 : if (!argsobj.hasOverriddenLength()) {
200 49 : *lengthp = argsobj.initialLength();
201 49 : return true;
202 : }
203 : }
204 :
205 4 : RootedValue value(cx);
206 2 : if (!GetProperty(cx, obj, obj, cx->names().length, &value))
207 0 : return false;
208 :
209 2 : return ToLength(cx, value, lengthp);
210 : }
211 :
212 : /*
213 : * Determine if the id represents an array index.
214 : *
215 : * An id is an array index according to ECMA by (15.4):
216 : *
217 : * "Array objects give special treatment to a certain class of property names.
218 : * A property name P (in the form of a string value) is an array index if and
219 : * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
220 : * to 2^32-1."
221 : *
222 : * This means the largest allowed index is actually 2^32-2 (4294967294).
223 : *
224 : * In our implementation, it would be sufficient to check for id.isInt32()
225 : * except that by using signed 31-bit integers we miss the top half of the
226 : * valid range. This function checks the string representation itself; note
227 : * that calling a standard conversion routine might allow strings such as
228 : * "08" or "4.0" as array indices, which they are not.
229 : *
230 : */
231 : template <typename CharT>
232 : static bool
233 337 : StringIsArrayIndex(const CharT* s, uint32_t length, uint32_t* indexp)
234 : {
235 337 : const CharT* end = s + length;
236 :
237 337 : if (length == 0 || length > (sizeof("4294967294") - 1) || !JS7_ISDEC(*s))
238 337 : return false;
239 :
240 0 : uint32_t c = 0, previous = 0;
241 0 : uint32_t index = JS7_UNDEC(*s++);
242 :
243 : /* Don't allow leading zeros. */
244 0 : if (index == 0 && s != end)
245 0 : return false;
246 :
247 0 : for (; s < end; s++) {
248 0 : if (!JS7_ISDEC(*s))
249 0 : return false;
250 :
251 0 : previous = index;
252 0 : c = JS7_UNDEC(*s);
253 0 : index = 10 * index + c;
254 : }
255 :
256 : /* Make sure we didn't overflow. */
257 0 : if (previous < (MAX_ARRAY_INDEX / 10) || (previous == (MAX_ARRAY_INDEX / 10) &&
258 : c <= (MAX_ARRAY_INDEX % 10))) {
259 0 : MOZ_ASSERT(index <= MAX_ARRAY_INDEX);
260 0 : *indexp = index;
261 0 : return true;
262 : }
263 :
264 0 : return false;
265 : }
266 :
267 : JS_FRIEND_API(bool)
268 337 : js::StringIsArrayIndex(JSLinearString* str, uint32_t* indexp)
269 : {
270 674 : AutoCheckCannotGC nogc;
271 337 : return str->hasLatin1Chars()
272 337 : ? ::StringIsArrayIndex(str->latin1Chars(nogc), str->length(), indexp)
273 674 : : ::StringIsArrayIndex(str->twoByteChars(nogc), str->length(), indexp);
274 : }
275 :
276 : template <typename T>
277 : static bool
278 : ToId(JSContext* cx, T index, MutableHandleId id);
279 :
280 : template <>
281 : bool
282 0 : ToId(JSContext* cx, uint32_t index, MutableHandleId id)
283 : {
284 0 : return IndexToId(cx, index, id);
285 : }
286 :
287 : template <>
288 : bool
289 95 : ToId(JSContext* cx, uint64_t index, MutableHandleId id)
290 : {
291 95 : MOZ_ASSERT(index < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT));
292 :
293 95 : if (index == uint32_t(index))
294 95 : return IndexToId(cx, uint32_t(index), id);
295 :
296 0 : Value tmp = DoubleValue(index);
297 0 : return ValueToId<CanGC>(cx, HandleValue::fromMarkedLocation(&tmp), id);
298 : }
299 :
300 : /*
301 : * If the property at the given index exists, get its value into |vp| and set
302 : * |*hole| to false. Otherwise set |*hole| to true and |vp| to Undefined.
303 : */
304 : template <typename T>
305 : static bool
306 113 : HasAndGetElement(JSContext* cx, HandleObject obj, HandleObject receiver, T index, bool* hole,
307 : MutableHandleValue vp)
308 : {
309 113 : if (index < GetAnyBoxedOrUnboxedInitializedLength(obj)) {
310 9 : vp.set(GetAnyBoxedOrUnboxedDenseElement(obj, size_t(index)));
311 9 : if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
312 9 : *hole = false;
313 9 : return true;
314 : }
315 : }
316 104 : if (obj->is<ArgumentsObject>() && index <= UINT32_MAX) {
317 75 : if (obj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp)) {
318 75 : *hole = false;
319 75 : return true;
320 : }
321 : }
322 :
323 58 : RootedId id(cx);
324 29 : if (!ToId(cx, index, &id))
325 0 : return false;
326 :
327 : bool found;
328 29 : if (!HasProperty(cx, obj, id, &found))
329 0 : return false;
330 :
331 29 : if (found) {
332 0 : if (!GetProperty(cx, obj, receiver, id, vp))
333 0 : return false;
334 : } else {
335 29 : vp.setUndefined();
336 : }
337 29 : *hole = !found;
338 29 : return true;
339 : }
340 :
341 : template <typename T>
342 : static inline bool
343 113 : HasAndGetElement(JSContext* cx, HandleObject obj, T index, bool* hole, MutableHandleValue vp)
344 : {
345 113 : return HasAndGetElement(cx, obj, obj, index, hole, vp);
346 : }
347 :
348 : bool
349 66 : ElementAdder::append(JSContext* cx, HandleValue v)
350 : {
351 66 : MOZ_ASSERT(index_ < length_);
352 66 : if (resObj_) {
353 : DenseElementResult result =
354 0 : SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, resObj_, index_, v.address(), 1);
355 0 : if (result == DenseElementResult::Failure)
356 0 : return false;
357 0 : if (result == DenseElementResult::Incomplete) {
358 0 : if (!DefineElement(cx, resObj_, index_, v))
359 0 : return false;
360 : }
361 : } else {
362 66 : vp_[index_] = v;
363 : }
364 66 : index_++;
365 66 : return true;
366 : }
367 :
368 : void
369 0 : ElementAdder::appendHole()
370 : {
371 0 : MOZ_ASSERT(getBehavior_ == ElementAdder::CheckHasElemPreserveHoles);
372 0 : MOZ_ASSERT(index_ < length_);
373 0 : if (!resObj_)
374 0 : vp_[index_].setMagic(JS_ELEMENTS_HOLE);
375 0 : index_++;
376 0 : }
377 :
378 : bool
379 35 : js::GetElementsWithAdder(JSContext* cx, HandleObject obj, HandleObject receiver,
380 : uint32_t begin, uint32_t end, ElementAdder* adder)
381 : {
382 35 : MOZ_ASSERT(begin <= end);
383 :
384 70 : RootedValue val(cx);
385 101 : for (uint32_t i = begin; i < end; i++) {
386 66 : if (adder->getBehavior() == ElementAdder::CheckHasElemPreserveHoles) {
387 : bool hole;
388 0 : if (!HasAndGetElement(cx, obj, receiver, i, &hole, &val))
389 0 : return false;
390 0 : if (hole) {
391 0 : adder->appendHole();
392 0 : continue;
393 : }
394 : } else {
395 66 : MOZ_ASSERT(adder->getBehavior() == ElementAdder::GetElement);
396 66 : if (!GetElement(cx, obj, receiver, i, &val))
397 0 : return false;
398 : }
399 66 : if (!adder->append(cx, val))
400 0 : return false;
401 : }
402 :
403 35 : return true;
404 : }
405 :
406 : template <JSValueType Type>
407 : DenseElementResult
408 103 : GetBoxedOrUnboxedDenseElements(JSObject* aobj, uint32_t length, Value* vp)
409 : {
410 103 : MOZ_ASSERT(!ObjectMayHaveExtraIndexedProperties(aobj));
411 :
412 103 : if (length > GetBoxedOrUnboxedInitializedLength<Type>(aobj))
413 0 : return DenseElementResult::Incomplete;
414 :
415 387 : for (size_t i = 0; i < length; i++) {
416 284 : vp[i] = GetBoxedOrUnboxedDenseElement<Type>(aobj, i);
417 :
418 : // No other indexed properties so hole => undefined.
419 284 : if (vp[i].isMagic(JS_ELEMENTS_HOLE))
420 0 : vp[i] = UndefinedValue();
421 : }
422 :
423 103 : return DenseElementResult::Success;
424 : }
425 :
426 206 : DefineBoxedOrUnboxedFunctor3(GetBoxedOrUnboxedDenseElements,
427 : JSObject*, uint32_t, Value*);
428 :
429 : bool
430 155 : js::GetElements(JSContext* cx, HandleObject aobj, uint32_t length, Value* vp)
431 : {
432 155 : if (!ObjectMayHaveExtraIndexedProperties(aobj)) {
433 103 : GetBoxedOrUnboxedDenseElementsFunctor functor(aobj, length, vp);
434 103 : DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, aobj);
435 103 : if (result != DenseElementResult::Incomplete)
436 103 : return result == DenseElementResult::Success;
437 : }
438 :
439 52 : if (aobj->is<ArgumentsObject>()) {
440 17 : ArgumentsObject& argsobj = aobj->as<ArgumentsObject>();
441 17 : if (!argsobj.hasOverriddenLength()) {
442 0 : if (argsobj.maybeGetElements(0, length, vp))
443 0 : return true;
444 : }
445 : }
446 :
447 52 : if (js::GetElementsOp op = aobj->getOpsGetElements()) {
448 70 : ElementAdder adder(cx, vp, length, ElementAdder::GetElement);
449 35 : return op(cx, aobj, 0, length, &adder);
450 : }
451 :
452 83 : for (uint32_t i = 0; i < length; i++) {
453 66 : if (!GetElement(cx, aobj, aobj, i, MutableHandleValue::fromMarkedLocation(&vp[i])))
454 0 : return false;
455 : }
456 :
457 17 : return true;
458 : }
459 :
460 : static inline bool
461 1 : GetArrayElement(JSContext* cx, HandleObject obj, uint64_t index, MutableHandleValue vp)
462 : {
463 1 : if (index < GetAnyBoxedOrUnboxedInitializedLength(obj)) {
464 1 : vp.set(GetAnyBoxedOrUnboxedDenseElement(obj, size_t(index)));
465 1 : if (!vp.isMagic(JS_ELEMENTS_HOLE))
466 1 : return true;
467 : }
468 :
469 0 : if (obj->is<ArgumentsObject>() && index <= UINT32_MAX) {
470 0 : if (obj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp))
471 0 : return true;
472 : }
473 :
474 0 : RootedId id(cx);
475 0 : if (!ToId(cx, index, &id))
476 0 : return false;
477 0 : return GetProperty(cx, obj, obj, id, vp);
478 : }
479 :
480 : static inline bool
481 0 : DefineArrayElement(JSContext* cx, HandleObject obj, uint64_t index, HandleValue value)
482 : {
483 0 : RootedId id(cx);
484 0 : if (!ToId(cx, index, &id))
485 0 : return false;
486 0 : return DefineProperty(cx, obj, id, value);
487 : }
488 :
489 : // Set the value of the property at the given index to v.
490 : static inline bool
491 49 : SetArrayElement(JSContext* cx, HandleObject obj, uint64_t index, HandleValue v)
492 : {
493 98 : RootedId id(cx);
494 49 : if (!ToId(cx, index, &id))
495 0 : return false;
496 :
497 49 : return SetProperty(cx, obj, id, v);
498 : }
499 :
500 : /*
501 : * Attempt to delete the element |index| from |obj| as if by
502 : * |obj.[[Delete]](index)|.
503 : *
504 : * If an error occurs while attempting to delete the element (that is, the call
505 : * to [[Delete]] threw), return false.
506 : *
507 : * Otherwise call result.succeed() or result.fail() to indicate whether the
508 : * deletion attempt succeeded (that is, whether the call to [[Delete]] returned
509 : * true or false). (Deletes generally fail only when the property is
510 : * non-configurable, but proxies may implement different semantics.)
511 : */
512 : static bool
513 31 : DeleteArrayElement(JSContext* cx, HandleObject obj, uint64_t index, ObjectOpResult& result)
514 : {
515 62 : if (obj->is<ArrayObject>() && !obj->isIndexed() &&
516 31 : !obj->as<NativeObject>().denseElementsAreFrozen())
517 : {
518 31 : ArrayObject* aobj = &obj->as<ArrayObject>();
519 31 : if (index <= UINT32_MAX) {
520 31 : uint32_t idx = uint32_t(index);
521 31 : if (idx < aobj->getDenseInitializedLength()) {
522 1 : if (!aobj->maybeCopyElementsForWrite(cx))
523 0 : return false;
524 1 : if (idx+1 == aobj->getDenseInitializedLength()) {
525 1 : aobj->setDenseInitializedLength(idx);
526 : } else {
527 0 : aobj->markDenseElementsNotPacked(cx);
528 0 : aobj->setDenseElement(idx, MagicValue(JS_ELEMENTS_HOLE));
529 : }
530 1 : if (!SuppressDeletedElement(cx, obj, idx))
531 0 : return false;
532 : }
533 : }
534 :
535 31 : return result.succeed();
536 : }
537 :
538 0 : RootedId id(cx);
539 0 : if (!ToId(cx, index, &id))
540 0 : return false;
541 0 : return DeleteProperty(cx, obj, id, result);
542 : }
543 :
544 : /* ES6 draft rev 32 (2 Febr 2015) 7.3.7 */
545 : static bool
546 31 : DeletePropertyOrThrow(JSContext* cx, HandleObject obj, uint64_t index)
547 : {
548 31 : ObjectOpResult success;
549 31 : if (!DeleteArrayElement(cx, obj, index, success))
550 0 : return false;
551 31 : if (!success) {
552 0 : RootedId id(cx);
553 0 : if (!ToId(cx, index, &id))
554 0 : return false;
555 0 : return success.reportError(cx, obj, id);
556 : }
557 31 : return true;
558 : }
559 :
560 : static bool
561 62 : SetLengthProperty(JSContext* cx, HandleObject obj, uint64_t length)
562 : {
563 62 : MOZ_ASSERT(length < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT));
564 :
565 124 : RootedValue v(cx, NumberValue(length));
566 124 : return SetProperty(cx, obj, cx->names().length, v);
567 : }
568 :
569 : bool
570 0 : js::SetLengthProperty(JSContext* cx, HandleObject obj, uint32_t length)
571 : {
572 0 : RootedValue v(cx, NumberValue(length));
573 0 : return SetProperty(cx, obj, cx->names().length, v);
574 : }
575 :
576 : static bool
577 593 : array_length_getter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
578 : {
579 593 : vp.setNumber(obj->as<ArrayObject>().length());
580 593 : return true;
581 : }
582 :
583 : static bool
584 0 : array_length_setter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
585 : ObjectOpResult& result)
586 : {
587 0 : if (!obj->is<ArrayObject>()) {
588 : // This array .length property was found on the prototype
589 : // chain. Ideally the setter should not have been called, but since
590 : // we're here, do an impression of SetPropertyByDefining.
591 0 : const Class* clasp = obj->getClass();
592 0 : return DefineProperty(cx, obj, cx->names().length, vp,
593 : clasp->getGetProperty(), clasp->getSetProperty(),
594 0 : JSPROP_ENUMERATE, result);
595 : }
596 :
597 0 : Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
598 0 : MOZ_ASSERT(arr->lengthIsWritable(),
599 : "setter shouldn't be called if property is non-writable");
600 :
601 0 : return ArraySetLength(cx, arr, id, JSPROP_PERMANENT, vp, result);
602 : }
603 :
604 : struct ReverseIndexComparator
605 : {
606 0 : bool operator()(const uint32_t& a, const uint32_t& b, bool* lessOrEqualp) {
607 0 : MOZ_ASSERT(a != b, "how'd we get duplicate indexes?");
608 0 : *lessOrEqualp = b <= a;
609 0 : return true;
610 : }
611 : };
612 :
613 : static bool
614 53 : MaybeInIteration(HandleObject obj, JSContext* cx)
615 : {
616 : /*
617 : * Don't optimize if the array might be in the midst of iteration. We
618 : * rely on this to be able to safely move dense array elements around with
619 : * just a memmove (see NativeObject::moveDenseArrayElements), without worrying
620 : * about updating any in-progress enumerators for properties implicitly
621 : * deleted if a hole is moved from one location to another location not yet
622 : * visited. See bug 690622.
623 : *
624 : * Note that it's fine to optimize if |obj| is on the prototype of another
625 : * object: SuppressDeletedProperty only suppresses properties deleted from
626 : * the iterated object itself.
627 : */
628 :
629 53 : if (MOZ_LIKELY(!cx->compartment()->objectMaybeInIteration(obj)))
630 53 : return false;
631 :
632 0 : ObjectGroup* group = JSObject::getGroup(cx, obj);
633 0 : if (MOZ_UNLIKELY(!group)) {
634 0 : cx->recoverFromOutOfMemory();
635 0 : return true;
636 : }
637 :
638 0 : if (MOZ_UNLIKELY(group->hasAllFlags(OBJECT_FLAG_ITERATED)))
639 0 : return true;
640 :
641 0 : return false;
642 : }
643 :
644 : bool
645 123 : js::CanonicalizeArrayLengthValue(JSContext* cx, HandleValue v, uint32_t* newLen)
646 : {
647 : double d;
648 :
649 123 : if (!ToUint32(cx, v, newLen))
650 0 : return false;
651 :
652 123 : if (!ToNumber(cx, v, &d))
653 0 : return false;
654 :
655 123 : if (d == *newLen)
656 123 : return true;
657 :
658 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
659 0 : return false;
660 : }
661 :
662 : /* ES6 draft rev 34 (2015 Feb 20) 9.4.2.4 ArraySetLength */
663 : bool
664 123 : js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
665 : unsigned attrs, HandleValue value, ObjectOpResult& result)
666 : {
667 123 : MOZ_ASSERT(id == NameToId(cx->names().length));
668 :
669 123 : if (!arr->maybeCopyElementsForWrite(cx))
670 0 : return false;
671 :
672 : // Step 1.
673 : uint32_t newLen;
674 123 : if (attrs & JSPROP_IGNORE_VALUE) {
675 0 : MOZ_ASSERT(value.isUndefined());
676 :
677 : // The spec has us calling OrdinaryDefineOwnProperty if
678 : // Desc.[[Value]] is absent, but our implementation is so different that
679 : // this is impossible. Instead, set newLen to the current length and
680 : // proceed to step 9.
681 0 : newLen = arr->length();
682 : } else {
683 : // Step 2 is irrelevant in our implementation.
684 :
685 : // Steps 3-7.
686 123 : if (!CanonicalizeArrayLengthValue(cx, value, &newLen))
687 0 : return false;
688 :
689 : // Step 8 is irrelevant in our implementation.
690 : }
691 :
692 : // Steps 9-11.
693 123 : bool lengthIsWritable = arr->lengthIsWritable();
694 : #ifdef DEBUG
695 : {
696 246 : RootedShape lengthShape(cx, arr->lookupPure(id));
697 123 : MOZ_ASSERT(lengthShape);
698 123 : MOZ_ASSERT(lengthShape->writable() == lengthIsWritable);
699 : }
700 : #endif
701 123 : uint32_t oldLen = arr->length();
702 :
703 : // Part of steps 1.a, 12.a, and 16: Fail if we're being asked to change
704 : // enumerability or configurability, or otherwise break the object
705 : // invariants. (ES6 checks these by calling OrdinaryDefineOwnProperty, but
706 : // in SM, the array length property is hardly ordinary.)
707 246 : if ((attrs & (JSPROP_PERMANENT | JSPROP_IGNORE_PERMANENT)) == 0 ||
708 246 : (attrs & (JSPROP_ENUMERATE | JSPROP_IGNORE_ENUMERATE)) == JSPROP_ENUMERATE ||
709 246 : (attrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0 ||
710 123 : (!lengthIsWritable && (attrs & (JSPROP_READONLY | JSPROP_IGNORE_READONLY)) == 0))
711 : {
712 0 : return result.fail(JSMSG_CANT_REDEFINE_PROP);
713 : }
714 :
715 : // Steps 12-13 for arrays with non-writable length.
716 123 : if (!lengthIsWritable) {
717 0 : if (newLen == oldLen)
718 0 : return result.succeed();
719 :
720 0 : return result.fail(JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
721 : }
722 :
723 : // Step 19.
724 123 : bool succeeded = true;
725 : do {
726 : // The initialized length and capacity of an array only need updating
727 : // when non-hole elements are added or removed, which doesn't happen
728 : // when array length stays the same or increases.
729 123 : if (newLen >= oldLen)
730 112 : break;
731 :
732 : // Attempt to propagate dense-element optimization tricks, if possible,
733 : // and avoid the generic (and accordingly slow) deletion code below.
734 : // We can only do this if there are only densely-indexed elements.
735 : // Once there's a sparse indexed element, there's no good way to know,
736 : // save by enumerating all the properties to find it. But we *have* to
737 : // know in case that sparse indexed element is non-configurable, as
738 : // that element must prevent any deletions below it. Bug 586842 should
739 : // fix this inefficiency by moving indexed storage to be entirely
740 : // separate from non-indexed storage.
741 : // A second reason for this optimization to be invalid is an active
742 : // for..in iteration over the array. Keys deleted before being reached
743 : // during the iteration must not be visited, and suppressing them here
744 : // would be too costly.
745 11 : if (!arr->isIndexed() && !MaybeInIteration(arr, cx)) {
746 11 : if (!arr->maybeCopyElementsForWrite(cx))
747 0 : return false;
748 :
749 11 : uint32_t oldCapacity = arr->getDenseCapacity();
750 11 : uint32_t oldInitializedLength = arr->getDenseInitializedLength();
751 11 : MOZ_ASSERT(oldCapacity >= oldInitializedLength);
752 11 : if (oldInitializedLength > newLen)
753 0 : arr->setDenseInitializedLength(newLen);
754 11 : if (oldCapacity > newLen)
755 9 : arr->shrinkElements(cx, newLen);
756 :
757 : // We've done the work of deleting any dense elements needing
758 : // deletion, and there are no sparse elements. Thus we can skip
759 : // straight to defining the length.
760 11 : break;
761 : }
762 :
763 : // Step 15.
764 : //
765 : // Attempt to delete all elements above the new length, from greatest
766 : // to least. If any of these deletions fails, we're supposed to define
767 : // the length to one greater than the index that couldn't be deleted,
768 : // *with the property attributes specified*. This might convert the
769 : // length to be not the value specified, yet non-writable. (You may be
770 : // forgiven for thinking these are interesting semantics.) Example:
771 : //
772 : // var arr =
773 : // Object.defineProperty([0, 1, 2, 3], 1, { writable: false });
774 : // Object.defineProperty(arr, "length",
775 : // { value: 0, writable: false });
776 : //
777 : // will convert |arr| to an array of non-writable length two, then
778 : // throw a TypeError.
779 : //
780 : // We implement this behavior, in the relevant lops below, by setting
781 : // |succeeded| to false. Then we exit the loop, define the length
782 : // appropriately, and only then throw a TypeError, if necessary.
783 0 : uint32_t gap = oldLen - newLen;
784 0 : const uint32_t RemoveElementsFastLimit = 1 << 24;
785 0 : if (gap < RemoveElementsFastLimit) {
786 : // If we're removing a relatively small number of elements, just do
787 : // it exactly by the spec.
788 0 : while (newLen < oldLen) {
789 : // Step 15a.
790 0 : oldLen--;
791 :
792 : // Steps 15b-d.
793 0 : ObjectOpResult deleteSucceeded;
794 0 : if (!DeleteElement(cx, arr, oldLen, deleteSucceeded))
795 0 : return false;
796 0 : if (!deleteSucceeded) {
797 0 : newLen = oldLen + 1;
798 0 : succeeded = false;
799 0 : break;
800 : }
801 : }
802 : } else {
803 : // If we're removing a large number of elements from an array
804 : // that's probably sparse, try a different tack. Get all the own
805 : // property names, sift out the indexes in the deletion range into
806 : // a vector, sort the vector greatest to least, then delete the
807 : // indexes greatest to least using that vector. See bug 322135.
808 : //
809 : // This heuristic's kind of a huge guess -- "large number of
810 : // elements" and "probably sparse" are completely unprincipled
811 : // predictions. In the long run, bug 586842 will support the right
812 : // fix: store sparse elements in a sorted data structure that
813 : // permits fast in-reverse-order traversal and concurrent removals.
814 :
815 0 : Vector<uint32_t> indexes(cx);
816 : {
817 0 : AutoIdVector props(cx);
818 0 : if (!GetPropertyKeys(cx, arr, JSITER_OWNONLY | JSITER_HIDDEN, &props))
819 0 : return false;
820 :
821 0 : for (size_t i = 0; i < props.length(); i++) {
822 0 : if (!CheckForInterrupt(cx))
823 0 : return false;
824 :
825 : uint32_t index;
826 0 : if (!IdIsIndex(props[i], &index))
827 0 : continue;
828 :
829 0 : if (index >= newLen && index < oldLen) {
830 0 : if (!indexes.append(index))
831 0 : return false;
832 : }
833 : }
834 : }
835 :
836 0 : uint32_t count = indexes.length();
837 : {
838 : // We should use radix sort to be O(n), but this is uncommon
839 : // enough that we'll punt til someone complains.
840 0 : Vector<uint32_t> scratch(cx);
841 0 : if (!scratch.resize(count))
842 0 : return false;
843 0 : MOZ_ALWAYS_TRUE(MergeSort(indexes.begin(), count, scratch.begin(),
844 : ReverseIndexComparator()));
845 : }
846 :
847 0 : uint32_t index = UINT32_MAX;
848 0 : for (uint32_t i = 0; i < count; i++) {
849 0 : MOZ_ASSERT(indexes[i] < index, "indexes should never repeat");
850 0 : index = indexes[i];
851 :
852 : // Steps 15b-d.
853 0 : ObjectOpResult deleteSucceeded;
854 0 : if (!DeleteElement(cx, arr, index, deleteSucceeded))
855 0 : return false;
856 0 : if (!deleteSucceeded) {
857 0 : newLen = index + 1;
858 0 : succeeded = false;
859 0 : break;
860 : }
861 : }
862 : }
863 : } while (false);
864 :
865 : // Update array length. Technically we should have been doing this
866 : // throughout the loop, in step 19.d.iii.
867 123 : arr->setLength(cx, newLen);
868 :
869 : // Step 20.
870 123 : if (attrs & JSPROP_READONLY) {
871 : // Yes, we totally drop a non-stub getter/setter from a defineProperty
872 : // API call on the floor here. Given that getter/setter will go away in
873 : // the long run, with accessors replacing them both internally and at the
874 : // API level, just run with this.
875 0 : RootedShape lengthShape(cx, arr->lookup(cx, id));
876 0 : if (!NativeObject::changeProperty(cx, arr, lengthShape,
877 0 : lengthShape->attributes() | JSPROP_READONLY,
878 : array_length_getter, array_length_setter))
879 : {
880 0 : return false;
881 : }
882 : }
883 :
884 : // All operations past here until the |!succeeded| code must be infallible,
885 : // so that all element fields remain properly synchronized.
886 :
887 : // Trim the initialized length, if needed, to preserve the <= length
888 : // invariant. (Capacity was already reduced during element deletion, if
889 : // necessary.)
890 123 : ObjectElements* header = arr->getElementsHeader();
891 123 : header->initializedLength = Min(header->initializedLength, newLen);
892 :
893 123 : if (attrs & JSPROP_READONLY)
894 0 : arr->setNonWritableLength(cx);
895 :
896 123 : if (!succeeded)
897 0 : return result.fail(JSMSG_CANT_TRUNCATE_ARRAY);
898 :
899 123 : return result.succeed();
900 : }
901 :
902 : bool
903 5220 : js::WouldDefinePastNonwritableLength(HandleNativeObject obj, uint32_t index)
904 : {
905 5220 : if (!obj->is<ArrayObject>())
906 23 : return false;
907 :
908 5197 : ArrayObject* arr = &obj->as<ArrayObject>();
909 5197 : return !arr->lengthIsWritable() && index >= arr->length();
910 : }
911 :
912 : static bool
913 9517 : array_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
914 : {
915 19034 : Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
916 :
917 : uint32_t index;
918 9517 : if (!IdIsIndex(id, &index))
919 9517 : return true;
920 :
921 0 : uint32_t length = arr->length();
922 0 : if (index >= length) {
923 0 : MOZ_ASSERT(arr->lengthIsWritable(),
924 : "how'd this element get added if length is non-writable?");
925 0 : arr->setLength(cx, index + 1);
926 : }
927 0 : return true;
928 : }
929 :
930 : static inline bool
931 7687 : ObjectMayHaveExtraIndexedOwnProperties(JSObject* obj)
932 : {
933 15374 : return (!obj->isNative() && !obj->is<UnboxedArrayObject>()) ||
934 15221 : obj->isIndexed() ||
935 22825 : obj->is<TypedArrayObject>() ||
936 7569 : ClassMayResolveId(*obj->runtimeFromAnyThread()->commonNames,
937 7687 : obj->getClass(), INT_TO_JSID(0), obj);
938 : }
939 :
940 : /*
941 : * Whether obj may have indexed properties anywhere besides its dense
942 : * elements. This includes other indexed properties in its shape hierarchy, and
943 : * indexed properties or elements along its prototype chain.
944 : */
945 : bool
946 2641 : js::ObjectMayHaveExtraIndexedProperties(JSObject* obj)
947 : {
948 2641 : MOZ_ASSERT_IF(obj->hasDynamicPrototype(), !obj->isNative());
949 :
950 2641 : if (ObjectMayHaveExtraIndexedOwnProperties(obj))
951 118 : return true;
952 :
953 5046 : do {
954 7569 : MOZ_ASSERT(obj->hasStaticPrototype(),
955 : "dynamic-prototype objects must be non-native, ergo must "
956 : "have failed ObjectMayHaveExtraIndexedOwnProperties");
957 :
958 7569 : obj = obj->staticPrototype();
959 7569 : if (!obj)
960 2523 : return false; // no extra indexed properties found
961 :
962 5046 : if (ObjectMayHaveExtraIndexedOwnProperties(obj))
963 0 : return true;
964 5046 : if (GetAnyBoxedOrUnboxedInitializedLength(obj) != 0)
965 0 : return true;
966 : } while (true);
967 : }
968 :
969 : static bool
970 316 : AddLengthProperty(JSContext* cx, HandleArrayObject obj)
971 : {
972 : /*
973 : * Add the 'length' property for a newly created array,
974 : * and update the elements to be an empty array owned by the object.
975 : * The shared emptyObjectElements singleton cannot be used for slow arrays,
976 : * as accesses to 'length' will use the elements header.
977 : */
978 :
979 632 : RootedId lengthId(cx, NameToId(cx->names().length));
980 316 : MOZ_ASSERT(!obj->lookup(cx, lengthId));
981 :
982 632 : return NativeObject::addProperty(cx, obj, lengthId, array_length_getter, array_length_setter,
983 : SHAPE_INVALID_SLOT,
984 : JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_SHADOWABLE,
985 632 : 0, /* allowDictionary = */ false);
986 : }
987 :
988 : static bool
989 23 : IsArrayConstructor(const JSObject* obj)
990 : {
991 : // This must only return true if v is *the* Array constructor for the
992 : // current compartment; we rely on the fact that any other Array
993 : // constructor would be represented as a wrapper.
994 46 : return obj->is<JSFunction>() &&
995 46 : obj->as<JSFunction>().isNative() &&
996 46 : obj->as<JSFunction>().native() == ArrayConstructor;
997 : }
998 :
999 : static bool
1000 23 : IsArrayConstructor(const Value& v)
1001 : {
1002 23 : return v.isObject() && IsArrayConstructor(&v.toObject());
1003 : }
1004 :
1005 : bool
1006 151 : js::IsWrappedArrayConstructor(JSContext* cx, const Value& v, bool* result)
1007 : {
1008 151 : if (!v.isObject()) {
1009 0 : *result = false;
1010 0 : return true;
1011 : }
1012 151 : if (v.toObject().is<WrapperObject>()) {
1013 0 : JSObject* obj = CheckedUnwrap(&v.toObject());
1014 0 : if (!obj) {
1015 0 : ReportAccessDenied(cx);
1016 0 : return false;
1017 : }
1018 :
1019 0 : *result = IsArrayConstructor(obj);
1020 : } else {
1021 151 : *result = false;
1022 : }
1023 151 : return true;
1024 : }
1025 :
1026 : static bool
1027 57 : IsArraySpecies(JSContext* cx, HandleObject origArray)
1028 : {
1029 57 : if (origArray->is<NativeObject>() && !origArray->is<ArrayObject>())
1030 32 : return true;
1031 :
1032 50 : RootedValue ctor(cx);
1033 25 : if (!GetPropertyPure(cx, origArray, NameToId(cx->names().constructor), ctor.address()))
1034 2 : return false;
1035 :
1036 23 : if (!IsArrayConstructor(ctor))
1037 0 : return ctor.isUndefined();
1038 :
1039 46 : RootedObject ctorObj(cx, &ctor.toObject());
1040 46 : RootedId speciesId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().species));
1041 : JSFunction* getter;
1042 23 : if (!GetGetterPure(cx, ctorObj, speciesId, &getter))
1043 0 : return false;
1044 :
1045 23 : if (!getter)
1046 0 : return false;
1047 :
1048 23 : return IsSelfHostedFunctionWithName(getter, cx->names().ArraySpecies);
1049 : }
1050 :
1051 : static bool
1052 2 : ArraySpeciesCreate(JSContext* cx, HandleObject origArray, uint64_t length, MutableHandleObject arr)
1053 : {
1054 2 : MOZ_ASSERT(length < DOUBLE_INTEGRAL_PRECISION_LIMIT);
1055 :
1056 4 : FixedInvokeArgs<2> args(cx);
1057 :
1058 2 : args[0].setObject(*origArray);
1059 2 : args[1].set(NumberValue(length));
1060 :
1061 4 : RootedValue rval(cx);
1062 2 : if (!CallSelfHostedFunction(cx, cx->names().ArraySpeciesCreate, UndefinedHandleValue, args,
1063 : &rval))
1064 : {
1065 0 : return false;
1066 : }
1067 :
1068 2 : MOZ_ASSERT(rval.isObject());
1069 2 : arr.set(&rval.toObject());
1070 2 : return true;
1071 : }
1072 :
1073 : #if JS_HAS_TOSOURCE
1074 :
1075 : static bool
1076 2 : array_toSource(JSContext* cx, unsigned argc, Value* vp)
1077 : {
1078 2 : if (!CheckRecursionLimit(cx))
1079 0 : return false;
1080 :
1081 2 : CallArgs args = CallArgsFromVp(argc, vp);
1082 :
1083 2 : if (!args.thisv().isObject()) {
1084 0 : ReportIncompatible(cx, args);
1085 0 : return false;
1086 : }
1087 :
1088 4 : Rooted<JSObject*> obj(cx, &args.thisv().toObject());
1089 4 : RootedValue elt(cx);
1090 :
1091 4 : AutoCycleDetector detector(cx, obj);
1092 2 : if (!detector.init())
1093 0 : return false;
1094 :
1095 4 : StringBuffer sb(cx);
1096 :
1097 2 : if (detector.foundCycle()) {
1098 0 : if (!sb.append("[]"))
1099 0 : return false;
1100 0 : goto make_string;
1101 : }
1102 :
1103 2 : if (!sb.append('['))
1104 0 : return false;
1105 :
1106 : uint64_t length;
1107 2 : if (!GetLengthProperty(cx, obj, &length))
1108 0 : return false;
1109 :
1110 5 : for (uint64_t index = 0; index < length; index++) {
1111 : bool hole;
1112 15 : if (!CheckForInterrupt(cx) ||
1113 15 : !HasAndGetElement(cx, obj, index, &hole, &elt)) {
1114 0 : return false;
1115 : }
1116 :
1117 : /* Get element's character string. */
1118 : JSString* str;
1119 3 : if (hole) {
1120 0 : str = cx->runtime()->emptyString;
1121 : } else {
1122 3 : str = ValueToSource(cx, elt);
1123 3 : if (!str)
1124 0 : return false;
1125 : }
1126 :
1127 : /* Append element to buffer. */
1128 3 : if (!sb.append(str))
1129 0 : return false;
1130 3 : if (index + 1 != length) {
1131 1 : if (!sb.append(", "))
1132 0 : return false;
1133 2 : } else if (hole) {
1134 0 : if (!sb.append(','))
1135 0 : return false;
1136 : }
1137 : }
1138 :
1139 : /* Finalize the buffer. */
1140 2 : if (!sb.append(']'))
1141 0 : return false;
1142 :
1143 : make_string:
1144 2 : JSString* str = sb.finishString();
1145 2 : if (!str)
1146 0 : return false;
1147 :
1148 2 : args.rval().setString(str);
1149 2 : return true;
1150 : }
1151 :
1152 : #endif
1153 :
1154 : struct EmptySeparatorOp
1155 : {
1156 1024 : bool operator()(JSContext*, StringBuffer& sb) { return true; }
1157 : };
1158 :
1159 : template <typename CharT>
1160 : struct CharSeparatorOp
1161 : {
1162 : const CharT sep;
1163 50 : explicit CharSeparatorOp(CharT sep) : sep(sep) {}
1164 304 : bool operator()(JSContext*, StringBuffer& sb) { return sb.append(sep); }
1165 : };
1166 :
1167 : struct StringSeparatorOp
1168 : {
1169 : HandleLinearString sep;
1170 :
1171 3 : explicit StringSeparatorOp(HandleLinearString sep) : sep(sep) {}
1172 :
1173 10 : bool operator()(JSContext* cx, StringBuffer& sb) {
1174 10 : return sb.append(sep);
1175 : }
1176 : };
1177 :
1178 : template <typename SeparatorOp, JSValueType Type>
1179 : static DenseElementResult
1180 310 : ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint64_t length,
1181 : StringBuffer& sb, uint32_t* numProcessed)
1182 : {
1183 : // This loop handles all elements up to initializedLength. If
1184 : // length > initLength we rely on the second loop to add the
1185 : // other elements.
1186 310 : MOZ_ASSERT(*numProcessed == 0);
1187 310 : uint64_t initLength = Min<uint64_t>(GetBoxedOrUnboxedInitializedLength<Type>(obj), length);
1188 310 : MOZ_ASSERT(initLength <= UINT32_MAX, "initialized length shouldn't exceed UINT32_MAX");
1189 310 : uint32_t initLengthClamped = uint32_t(initLength);
1190 3606 : while (*numProcessed < initLengthClamped) {
1191 1648 : if (!CheckForInterrupt(cx))
1192 0 : return DenseElementResult::Failure;
1193 :
1194 : // Step 7.b.
1195 1648 : const Value& elem = GetBoxedOrUnboxedDenseElement<Type>(obj, *numProcessed);
1196 :
1197 : // Steps 7.c-d.
1198 1648 : if (elem.isString()) {
1199 1648 : if (!sb.append(elem.toString()))
1200 0 : return DenseElementResult::Failure;
1201 0 : } else if (elem.isNumber()) {
1202 0 : if (!NumberValueToStringBuffer(cx, elem, sb))
1203 0 : return DenseElementResult::Failure;
1204 0 : } else if (elem.isBoolean()) {
1205 0 : if (!BooleanToStringBuffer(elem.toBoolean(), sb))
1206 0 : return DenseElementResult::Failure;
1207 0 : } else if (elem.isObject() || elem.isSymbol()) {
1208 : /*
1209 : * Object stringifying could modify the initialized length or make
1210 : * the array sparse. Delegate it to a separate loop to keep this
1211 : * one tight.
1212 : *
1213 : * Symbol stringifying is a TypeError, so into the slow path
1214 : * with those as well.
1215 : */
1216 0 : break;
1217 : } else {
1218 0 : MOZ_ASSERT(elem.isMagic(JS_ELEMENTS_HOLE) || elem.isNullOrUndefined());
1219 : }
1220 :
1221 : // Steps 7.a, 7.e.
1222 1648 : if (++(*numProcessed) != length && !sepOp(cx, sb))
1223 0 : return DenseElementResult::Failure;
1224 : }
1225 :
1226 310 : return DenseElementResult::Incomplete;
1227 : }
1228 :
1229 : template <typename SeparatorOp>
1230 : struct ArrayJoinDenseKernelFunctor {
1231 : JSContext* cx;
1232 : SeparatorOp sepOp;
1233 : HandleObject obj;
1234 : uint64_t length;
1235 : StringBuffer& sb;
1236 : uint32_t* numProcessed;
1237 :
1238 310 : ArrayJoinDenseKernelFunctor(JSContext* cx, SeparatorOp sepOp, HandleObject obj,
1239 : uint64_t length, StringBuffer& sb, uint32_t* numProcessed)
1240 310 : : cx(cx), sepOp(sepOp), obj(obj), length(length), sb(sb), numProcessed(numProcessed)
1241 310 : {}
1242 :
1243 : template <JSValueType Type>
1244 310 : DenseElementResult operator()() {
1245 310 : return ArrayJoinDenseKernel<SeparatorOp, Type>(cx, sepOp, obj, length, sb, numProcessed);
1246 : }
1247 : };
1248 :
1249 : template <typename SeparatorOp>
1250 : static bool
1251 310 : ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint64_t length,
1252 : StringBuffer& sb)
1253 : {
1254 : // Step 6.
1255 310 : uint32_t numProcessed = 0;
1256 :
1257 310 : if (!ObjectMayHaveExtraIndexedProperties(obj)) {
1258 310 : ArrayJoinDenseKernelFunctor<SeparatorOp> functor(cx, sepOp, obj, length, sb, &numProcessed);
1259 310 : DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
1260 310 : if (result == DenseElementResult::Failure)
1261 0 : return false;
1262 : }
1263 :
1264 : // Step 7.
1265 310 : if (numProcessed != length) {
1266 0 : RootedValue v(cx);
1267 0 : for (uint64_t i = numProcessed; i < length; ) {
1268 0 : if (!CheckForInterrupt(cx))
1269 0 : return false;
1270 :
1271 : // Step 7.b.
1272 0 : if (!GetArrayElement(cx, obj, i, &v))
1273 0 : return false;
1274 :
1275 : // Steps 7.c-d.
1276 0 : if (!v.isNullOrUndefined()) {
1277 0 : if (!ValueToStringBuffer(cx, v, sb))
1278 0 : return false;
1279 : }
1280 :
1281 : // Steps 7.a, 7.e.
1282 0 : if (++i != length && !sepOp(cx, sb))
1283 0 : return false;
1284 : }
1285 : }
1286 :
1287 310 : return true;
1288 : }
1289 :
1290 : // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
1291 : // 22.1.3.13 Array.prototype.join ( separator )
1292 : bool
1293 319 : js::array_join(JSContext* cx, unsigned argc, Value* vp)
1294 : {
1295 319 : if (!CheckRecursionLimit(cx))
1296 0 : return false;
1297 :
1298 638 : AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.join");
1299 319 : CallArgs args = CallArgsFromVp(argc, vp);
1300 :
1301 : // Step 1.
1302 638 : RootedObject obj(cx, ToObject(cx, args.thisv()));
1303 319 : if (!obj)
1304 0 : return false;
1305 :
1306 638 : AutoCycleDetector detector(cx, obj);
1307 319 : if (!detector.init())
1308 0 : return false;
1309 :
1310 319 : if (detector.foundCycle()) {
1311 0 : args.rval().setString(cx->names().empty);
1312 0 : return true;
1313 : }
1314 :
1315 : // Step 2.
1316 : uint64_t length;
1317 319 : if (!GetLengthProperty(cx, obj, &length))
1318 0 : return false;
1319 :
1320 : // Steps 3-4.
1321 638 : RootedLinearString sepstr(cx);
1322 319 : if (args.hasDefined(0)) {
1323 319 : JSString *s = ToString<CanGC>(cx, args[0]);
1324 319 : if (!s)
1325 0 : return false;
1326 319 : sepstr = s->ensureLinear(cx);
1327 319 : if (!sepstr)
1328 0 : return false;
1329 : } else {
1330 0 : sepstr = cx->names().comma;
1331 : }
1332 :
1333 : // Steps 5-8 (When the length is zero, directly return the empty string).
1334 319 : if (length == 0) {
1335 2 : args.rval().setString(cx->emptyString());
1336 2 : return true;
1337 : }
1338 :
1339 : // An optimized version of a special case of steps 5-8: when length==1 and
1340 : // the 0th element is a string, ToString() of that element is a no-op and
1341 : // so it can be immediately returned as the result.
1342 317 : if (length == 1 && GetAnyBoxedOrUnboxedInitializedLength(obj) == 1) {
1343 7 : Value elem0 = GetAnyBoxedOrUnboxedDenseElement(obj, 0);
1344 7 : if (elem0.isString()) {
1345 7 : args.rval().set(elem0);
1346 7 : return true;
1347 : }
1348 : }
1349 :
1350 : // Step 5.
1351 620 : StringBuffer sb(cx);
1352 310 : if (sepstr->hasTwoByteChars() && !sb.ensureTwoByteChars())
1353 0 : return false;
1354 :
1355 : // The separator will be added |length - 1| times, reserve space for that
1356 : // so that we don't have to unnecessarily grow the buffer.
1357 310 : size_t seplen = sepstr->length();
1358 310 : if (seplen > 0) {
1359 53 : if (length > UINT32_MAX) {
1360 0 : ReportAllocationOverflow(cx);
1361 0 : return false;
1362 : }
1363 53 : CheckedInt<uint32_t> res = CheckedInt<uint32_t>(seplen) * (uint32_t(length) - 1);
1364 53 : if (!res.isValid()) {
1365 0 : ReportAllocationOverflow(cx);
1366 0 : return false;
1367 : }
1368 :
1369 53 : if (!sb.reserve(res.value()))
1370 0 : return false;
1371 : }
1372 :
1373 : // Various optimized versions of steps 6-7.
1374 310 : if (seplen == 0) {
1375 : EmptySeparatorOp op;
1376 257 : if (!ArrayJoinKernel(cx, op, obj, length, sb))
1377 0 : return false;
1378 53 : } else if (seplen == 1) {
1379 50 : char16_t c = sepstr->latin1OrTwoByteChar(0);
1380 50 : if (c <= JSString::MAX_LATIN1_CHAR) {
1381 50 : CharSeparatorOp<Latin1Char> op(c);
1382 50 : if (!ArrayJoinKernel(cx, op, obj, length, sb))
1383 0 : return false;
1384 : } else {
1385 0 : CharSeparatorOp<char16_t> op(c);
1386 0 : if (!ArrayJoinKernel(cx, op, obj, length, sb))
1387 0 : return false;
1388 : }
1389 : } else {
1390 3 : StringSeparatorOp op(sepstr);
1391 3 : if (!ArrayJoinKernel(cx, op, obj, length, sb))
1392 0 : return false;
1393 : }
1394 :
1395 : // Step 8.
1396 310 : JSString *str = sb.finishString();
1397 310 : if (!str)
1398 0 : return false;
1399 :
1400 310 : args.rval().setString(str);
1401 310 : return true;
1402 : }
1403 :
1404 : // ES2017 draft rev f8a9be8ea4bd97237d176907a1e3080dce20c68f
1405 : // 22.1.3.27 Array.prototype.toLocaleString ([ reserved1 [ , reserved2 ] ])
1406 : // ES2017 Intl draft rev 78bbe7d1095f5ff3760ac4017ed366026e4cb276
1407 : // 13.4.1 Array.prototype.toLocaleString ([ locales [ , options ]])
1408 : static bool
1409 0 : array_toLocaleString(JSContext* cx, unsigned argc, Value* vp)
1410 : {
1411 0 : if (!CheckRecursionLimit(cx))
1412 0 : return false;
1413 :
1414 0 : CallArgs args = CallArgsFromVp(argc, vp);
1415 :
1416 : // Step 1
1417 0 : RootedObject obj(cx, ToObject(cx, args.thisv()));
1418 0 : if (!obj)
1419 0 : return false;
1420 :
1421 : // Avoid calling into self-hosted code if the array is empty.
1422 0 : if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() == 0) {
1423 0 : args.rval().setString(cx->names().empty);
1424 0 : return true;
1425 : }
1426 0 : if (obj->is<UnboxedArrayObject>() && obj->as<UnboxedArrayObject>().length() == 0) {
1427 0 : args.rval().setString(cx->names().empty);
1428 0 : return true;
1429 : }
1430 :
1431 0 : AutoCycleDetector detector(cx, obj);
1432 0 : if (!detector.init())
1433 0 : return false;
1434 :
1435 0 : if (detector.foundCycle()) {
1436 0 : args.rval().setString(cx->names().empty);
1437 0 : return true;
1438 : }
1439 :
1440 0 : FixedInvokeArgs<2> args2(cx);
1441 :
1442 0 : args2[0].set(args.get(0));
1443 0 : args2[1].set(args.get(1));
1444 :
1445 : // Steps 2-10.
1446 0 : RootedValue thisv(cx, ObjectValue(*obj));
1447 0 : return CallSelfHostedFunction(cx, cx->names().ArrayToLocaleString, thisv, args2, args.rval());
1448 : }
1449 :
1450 : /* vector must point to rooted memory. */
1451 : static bool
1452 38 : SetArrayElements(JSContext* cx, HandleObject obj, uint64_t start,
1453 : uint32_t count, const Value* vector,
1454 : ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update)
1455 : {
1456 38 : MOZ_ASSERT(count <= MAX_ARRAY_INDEX);
1457 38 : MOZ_ASSERT(start + count < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT));
1458 :
1459 38 : if (count == 0)
1460 2 : return true;
1461 :
1462 36 : if (!ObjectMayHaveExtraIndexedProperties(obj) && start <= UINT32_MAX) {
1463 : DenseElementResult result =
1464 38 : SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, uint32_t(start), vector, count,
1465 38 : updateTypes);
1466 19 : if (result != DenseElementResult::Incomplete)
1467 19 : return result == DenseElementResult::Success;
1468 : }
1469 :
1470 34 : RootedId id(cx);
1471 17 : const Value* end = vector + count;
1472 51 : while (vector < end) {
1473 17 : if (!CheckForInterrupt(cx))
1474 0 : return false;
1475 :
1476 17 : if (!ToId(cx, start++, &id))
1477 0 : return false;
1478 :
1479 17 : if (!SetProperty(cx, obj, id, HandleValue::fromMarkedLocation(vector++)))
1480 0 : return false;
1481 : }
1482 :
1483 17 : return true;
1484 : }
1485 :
1486 : template <JSValueType Type>
1487 : DenseElementResult
1488 0 : ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length)
1489 : {
1490 : /* An empty array or an array with no elements is already reversed. */
1491 0 : if (length == 0 || GetBoxedOrUnboxedInitializedLength<Type>(obj) == 0)
1492 0 : return DenseElementResult::Success;
1493 :
1494 : if (Type == JSVAL_TYPE_MAGIC) {
1495 0 : if (obj->as<NativeObject>().denseElementsAreFrozen())
1496 0 : return DenseElementResult::Incomplete;
1497 :
1498 : /*
1499 : * It's actually surprisingly complicated to reverse an array due to the
1500 : * orthogonality of array length and array capacity while handling
1501 : * leading and trailing holes correctly. Reversing seems less likely to
1502 : * be a common operation than other array mass-mutation methods, so for
1503 : * now just take a probably-small memory hit (in the absence of too many
1504 : * holes in the array at its start) and ensure that the capacity is
1505 : * sufficient to hold all the elements in the array if it were full.
1506 : */
1507 0 : DenseElementResult result = obj->as<NativeObject>().ensureDenseElements(cx, length, 0);
1508 0 : if (result != DenseElementResult::Success)
1509 0 : return result;
1510 :
1511 : /* Fill out the array's initialized length to its proper length. */
1512 0 : obj->as<NativeObject>().ensureDenseInitializedLength(cx, length, 0);
1513 : } else {
1514 : // Unboxed arrays can only be reversed here if their initialized length
1515 : // matches their actual length. Otherwise the reversal will place holes
1516 : // at the beginning of the array, which we don't support.
1517 0 : if (length != obj->as<UnboxedArrayObject>().initializedLength())
1518 0 : return DenseElementResult::Incomplete;
1519 : }
1520 :
1521 0 : RootedValue origlo(cx), orighi(cx);
1522 :
1523 0 : uint32_t lo = 0, hi = length - 1;
1524 0 : for (; lo < hi; lo++, hi--) {
1525 0 : origlo = GetBoxedOrUnboxedDenseElement<Type>(obj, lo);
1526 0 : orighi = GetBoxedOrUnboxedDenseElement<Type>(obj, hi);
1527 0 : SetBoxedOrUnboxedDenseElementNoTypeChange<Type>(obj, lo, orighi);
1528 0 : if (orighi.isMagic(JS_ELEMENTS_HOLE) &&
1529 0 : !SuppressDeletedProperty(cx, obj, INT_TO_JSID(lo)))
1530 : {
1531 0 : return DenseElementResult::Failure;
1532 : }
1533 0 : SetBoxedOrUnboxedDenseElementNoTypeChange<Type>(obj, hi, origlo);
1534 0 : if (origlo.isMagic(JS_ELEMENTS_HOLE) &&
1535 0 : !SuppressDeletedProperty(cx, obj, INT_TO_JSID(hi)))
1536 : {
1537 0 : return DenseElementResult::Failure;
1538 : }
1539 : }
1540 :
1541 0 : return DenseElementResult::Success;
1542 : }
1543 :
1544 0 : DefineBoxedOrUnboxedFunctor3(ArrayReverseDenseKernel,
1545 : JSContext*, HandleObject, uint32_t);
1546 :
1547 : // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
1548 : // 22.1.3.21 Array.prototype.reverse ( )
1549 : bool
1550 0 : js::array_reverse(JSContext* cx, unsigned argc, Value* vp)
1551 : {
1552 0 : AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.reverse");
1553 0 : CallArgs args = CallArgsFromVp(argc, vp);
1554 :
1555 : // Step 1.
1556 0 : RootedObject obj(cx, ToObject(cx, args.thisv()));
1557 0 : if (!obj)
1558 0 : return false;
1559 :
1560 : // Step 2.
1561 : uint64_t len;
1562 0 : if (!GetLengthProperty(cx, obj, &len))
1563 0 : return false;
1564 :
1565 0 : if (!ObjectMayHaveExtraIndexedProperties(obj) && len <= UINT32_MAX) {
1566 0 : ArrayReverseDenseKernelFunctor functor(cx, obj, uint32_t(len));
1567 0 : DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
1568 0 : if (result != DenseElementResult::Incomplete) {
1569 : /*
1570 : * Per ECMA-262, don't update the length of the array, even if the new
1571 : * array has trailing holes (and thus the original array began with
1572 : * holes).
1573 : */
1574 0 : args.rval().setObject(*obj);
1575 0 : return result == DenseElementResult::Success;
1576 : }
1577 : }
1578 :
1579 : // Steps 3-5.
1580 0 : RootedValue lowval(cx), hival(cx);
1581 0 : for (uint64_t i = 0, half = len / 2; i < half; i++) {
1582 : bool hole, hole2;
1583 0 : if (!CheckForInterrupt(cx) ||
1584 0 : !HasAndGetElement(cx, obj, i, &hole, &lowval) ||
1585 0 : !HasAndGetElement(cx, obj, len - i - 1, &hole2, &hival))
1586 : {
1587 0 : return false;
1588 : }
1589 :
1590 0 : if (!hole && !hole2) {
1591 0 : if (!SetArrayElement(cx, obj, i, hival))
1592 0 : return false;
1593 0 : if (!SetArrayElement(cx, obj, len - i - 1, lowval))
1594 0 : return false;
1595 0 : } else if (hole && !hole2) {
1596 0 : if (!SetArrayElement(cx, obj, i, hival))
1597 0 : return false;
1598 0 : if (!DeletePropertyOrThrow(cx, obj, len - i - 1))
1599 0 : return false;
1600 0 : } else if (!hole && hole2) {
1601 0 : if (!DeletePropertyOrThrow(cx, obj, i))
1602 0 : return false;
1603 0 : if (!SetArrayElement(cx, obj, len - i - 1, lowval))
1604 0 : return false;
1605 : } else {
1606 : // No action required.
1607 : }
1608 : }
1609 :
1610 : // Step 6.
1611 0 : args.rval().setObject(*obj);
1612 0 : return true;
1613 : }
1614 :
1615 : static inline bool
1616 12 : CompareStringValues(JSContext* cx, const Value& a, const Value& b, bool* lessOrEqualp)
1617 : {
1618 12 : if (!CheckForInterrupt(cx))
1619 0 : return false;
1620 :
1621 12 : JSString* astr = a.toString();
1622 12 : JSString* bstr = b.toString();
1623 : int32_t result;
1624 12 : if (!CompareStrings(cx, astr, bstr, &result))
1625 0 : return false;
1626 :
1627 12 : *lessOrEqualp = (result <= 0);
1628 12 : return true;
1629 : }
1630 :
1631 : static const uint64_t powersOf10[] = {
1632 : 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 1000000000000ULL
1633 : };
1634 :
1635 : static inline unsigned
1636 0 : NumDigitsBase10(uint32_t n)
1637 : {
1638 : /*
1639 : * This is just floor_log10(n) + 1
1640 : * Algorithm taken from
1641 : * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
1642 : */
1643 0 : uint32_t log2 = CeilingLog2(n);
1644 0 : uint32_t t = log2 * 1233 >> 12;
1645 0 : return t - (n < powersOf10[t]) + 1;
1646 : }
1647 :
1648 : static inline bool
1649 0 : CompareLexicographicInt32(const Value& a, const Value& b, bool* lessOrEqualp)
1650 : {
1651 0 : int32_t aint = a.toInt32();
1652 0 : int32_t bint = b.toInt32();
1653 :
1654 : /*
1655 : * If both numbers are equal ... trivial
1656 : * If only one of both is negative --> arithmetic comparison as char code
1657 : * of '-' is always less than any other digit
1658 : * If both numbers are negative convert them to positive and continue
1659 : * handling ...
1660 : */
1661 0 : if (aint == bint) {
1662 0 : *lessOrEqualp = true;
1663 0 : } else if ((aint < 0) && (bint >= 0)) {
1664 0 : *lessOrEqualp = true;
1665 0 : } else if ((aint >= 0) && (bint < 0)) {
1666 0 : *lessOrEqualp = false;
1667 : } else {
1668 0 : uint32_t auint = Abs(aint);
1669 0 : uint32_t buint = Abs(bint);
1670 :
1671 : /*
1672 : * ... get number of digits of both integers.
1673 : * If they have the same number of digits --> arithmetic comparison.
1674 : * If digits_a > digits_b: a < b*10e(digits_a - digits_b).
1675 : * If digits_b > digits_a: a*10e(digits_b - digits_a) <= b.
1676 : */
1677 0 : unsigned digitsa = NumDigitsBase10(auint);
1678 0 : unsigned digitsb = NumDigitsBase10(buint);
1679 0 : if (digitsa == digitsb) {
1680 0 : *lessOrEqualp = (auint <= buint);
1681 0 : } else if (digitsa > digitsb) {
1682 0 : MOZ_ASSERT((digitsa - digitsb) < ArrayLength(powersOf10));
1683 0 : *lessOrEqualp = (uint64_t(auint) < uint64_t(buint) * powersOf10[digitsa - digitsb]);
1684 : } else { /* if (digitsb > digitsa) */
1685 0 : MOZ_ASSERT((digitsb - digitsa) < ArrayLength(powersOf10));
1686 0 : *lessOrEqualp = (uint64_t(auint) * powersOf10[digitsb - digitsa] <= uint64_t(buint));
1687 : }
1688 : }
1689 :
1690 0 : return true;
1691 : }
1692 :
1693 : template <typename Char1, typename Char2>
1694 : static inline bool
1695 0 : CompareSubStringValues(JSContext* cx, const Char1* s1, size_t len1, const Char2* s2, size_t len2,
1696 : bool* lessOrEqualp)
1697 : {
1698 0 : if (!CheckForInterrupt(cx))
1699 0 : return false;
1700 :
1701 0 : if (!s1 || !s2)
1702 0 : return false;
1703 :
1704 0 : int32_t result = CompareChars(s1, len1, s2, len2);
1705 0 : *lessOrEqualp = (result <= 0);
1706 0 : return true;
1707 : }
1708 :
1709 : namespace {
1710 :
1711 : struct SortComparatorStrings
1712 : {
1713 : JSContext* const cx;
1714 :
1715 1 : explicit SortComparatorStrings(JSContext* cx)
1716 1 : : cx(cx) {}
1717 :
1718 12 : bool operator()(const Value& a, const Value& b, bool* lessOrEqualp) {
1719 12 : return CompareStringValues(cx, a, b, lessOrEqualp);
1720 : }
1721 : };
1722 :
1723 : struct SortComparatorLexicographicInt32
1724 : {
1725 0 : bool operator()(const Value& a, const Value& b, bool* lessOrEqualp) {
1726 0 : return CompareLexicographicInt32(a, b, lessOrEqualp);
1727 : }
1728 : };
1729 :
1730 : struct StringifiedElement
1731 : {
1732 : size_t charsBegin;
1733 : size_t charsEnd;
1734 : size_t elementIndex;
1735 : };
1736 :
1737 : struct SortComparatorStringifiedElements
1738 : {
1739 : JSContext* const cx;
1740 : const StringBuffer& sb;
1741 :
1742 0 : SortComparatorStringifiedElements(JSContext* cx, const StringBuffer& sb)
1743 0 : : cx(cx), sb(sb) {}
1744 :
1745 0 : bool operator()(const StringifiedElement& a, const StringifiedElement& b, bool* lessOrEqualp) {
1746 0 : size_t lenA = a.charsEnd - a.charsBegin;
1747 0 : size_t lenB = b.charsEnd - b.charsBegin;
1748 :
1749 0 : if (sb.isUnderlyingBufferLatin1()) {
1750 0 : return CompareSubStringValues(cx, sb.rawLatin1Begin() + a.charsBegin, lenA,
1751 0 : sb.rawLatin1Begin() + b.charsBegin, lenB,
1752 0 : lessOrEqualp);
1753 : }
1754 :
1755 0 : return CompareSubStringValues(cx, sb.rawTwoByteBegin() + a.charsBegin, lenA,
1756 0 : sb.rawTwoByteBegin() + b.charsBegin, lenB,
1757 0 : lessOrEqualp);
1758 : }
1759 : };
1760 :
1761 : struct NumericElement
1762 : {
1763 : double dv;
1764 : size_t elementIndex;
1765 : };
1766 :
1767 : static bool
1768 0 : ComparatorNumericLeftMinusRight(const NumericElement& a, const NumericElement& b,
1769 : bool* lessOrEqualp)
1770 : {
1771 0 : *lessOrEqualp = (a.dv <= b.dv);
1772 0 : return true;
1773 : }
1774 :
1775 : static bool
1776 0 : ComparatorNumericRightMinusLeft(const NumericElement& a, const NumericElement& b,
1777 : bool* lessOrEqualp)
1778 : {
1779 0 : *lessOrEqualp = (b.dv <= a.dv);
1780 0 : return true;
1781 : }
1782 :
1783 : typedef bool (*ComparatorNumeric)(const NumericElement& a, const NumericElement& b,
1784 : bool* lessOrEqualp);
1785 :
1786 : static const ComparatorNumeric SortComparatorNumerics[] = {
1787 : nullptr,
1788 : nullptr,
1789 : ComparatorNumericLeftMinusRight,
1790 : ComparatorNumericRightMinusLeft
1791 : };
1792 :
1793 : static bool
1794 0 : ComparatorInt32LeftMinusRight(const Value& a, const Value& b, bool* lessOrEqualp)
1795 : {
1796 0 : *lessOrEqualp = (a.toInt32() <= b.toInt32());
1797 0 : return true;
1798 : }
1799 :
1800 : static bool
1801 0 : ComparatorInt32RightMinusLeft(const Value& a, const Value& b, bool* lessOrEqualp)
1802 : {
1803 0 : *lessOrEqualp = (b.toInt32() <= a.toInt32());
1804 0 : return true;
1805 : }
1806 :
1807 : typedef bool (*ComparatorInt32)(const Value& a, const Value& b, bool* lessOrEqualp);
1808 :
1809 : static const ComparatorInt32 SortComparatorInt32s[] = {
1810 : nullptr,
1811 : nullptr,
1812 : ComparatorInt32LeftMinusRight,
1813 : ComparatorInt32RightMinusLeft
1814 : };
1815 :
1816 : // Note: Values for this enum must match up with SortComparatorNumerics
1817 : // and SortComparatorInt32s.
1818 : enum ComparatorMatchResult {
1819 : Match_Failure = 0,
1820 : Match_None,
1821 : Match_LeftMinusRight,
1822 : Match_RightMinusLeft
1823 : };
1824 :
1825 : } // namespace
1826 :
1827 :
1828 : /*
1829 : * Specialize behavior for comparator functions with particular common bytecode
1830 : * patterns: namely, |return x - y| and |return y - x|.
1831 : */
1832 : static ComparatorMatchResult
1833 15 : MatchNumericComparator(JSContext* cx, const Value& v)
1834 : {
1835 15 : if (!v.isObject())
1836 1 : return Match_None;
1837 :
1838 14 : JSObject& obj = v.toObject();
1839 14 : if (!obj.is<JSFunction>())
1840 0 : return Match_None;
1841 :
1842 28 : RootedFunction fun(cx, &obj.as<JSFunction>());
1843 14 : if (!fun->isInterpreted() || fun->isClassConstructor())
1844 0 : return Match_None;
1845 :
1846 14 : JSScript* script = JSFunction::getOrCreateScript(cx, fun);
1847 14 : if (!script)
1848 0 : return Match_Failure;
1849 :
1850 14 : jsbytecode* pc = script->code();
1851 :
1852 : uint16_t arg0, arg1;
1853 14 : if (JSOp(*pc) != JSOP_GETARG)
1854 12 : return Match_None;
1855 2 : arg0 = GET_ARGNO(pc);
1856 2 : pc += JSOP_GETARG_LENGTH;
1857 :
1858 2 : if (JSOp(*pc) != JSOP_GETARG)
1859 2 : return Match_None;
1860 0 : arg1 = GET_ARGNO(pc);
1861 0 : pc += JSOP_GETARG_LENGTH;
1862 :
1863 0 : if (JSOp(*pc) != JSOP_SUB)
1864 0 : return Match_None;
1865 0 : pc += JSOP_SUB_LENGTH;
1866 :
1867 0 : if (JSOp(*pc) != JSOP_RETURN)
1868 0 : return Match_None;
1869 :
1870 0 : if (arg0 == 0 && arg1 == 1)
1871 0 : return Match_LeftMinusRight;
1872 :
1873 0 : if (arg0 == 1 && arg1 == 0)
1874 0 : return Match_RightMinusLeft;
1875 :
1876 0 : return Match_None;
1877 : }
1878 :
1879 : template <typename K, typename C>
1880 : static inline bool
1881 0 : MergeSortByKey(K keys, size_t len, K scratch, C comparator, MutableHandle<GCVector<Value>> vec)
1882 : {
1883 0 : MOZ_ASSERT(vec.length() >= len);
1884 :
1885 : /* Sort keys. */
1886 0 : if (!MergeSort(keys, len, scratch, comparator))
1887 0 : return false;
1888 :
1889 : /*
1890 : * Reorder vec by keys in-place, going element by element. When an out-of-
1891 : * place element is encountered, move that element to its proper position,
1892 : * displacing whatever element was at *that* point to its proper position,
1893 : * and so on until an element must be moved to the current position.
1894 : *
1895 : * At each outer iteration all elements up to |i| are sorted. If
1896 : * necessary each inner iteration moves some number of unsorted elements
1897 : * (including |i|) directly to sorted position. Thus on completion |*vec|
1898 : * is sorted, and out-of-position elements have moved once. Complexity is
1899 : * Θ(len) + O(len) == O(2*len), with each element visited at most twice.
1900 : */
1901 0 : for (size_t i = 0; i < len; i++) {
1902 0 : size_t j = keys[i].elementIndex;
1903 0 : if (i == j)
1904 0 : continue; // fixed point
1905 :
1906 0 : MOZ_ASSERT(j > i, "Everything less than |i| should be in the right place!");
1907 0 : Value tv = vec[j];
1908 0 : do {
1909 0 : size_t k = keys[j].elementIndex;
1910 0 : keys[j].elementIndex = j;
1911 0 : vec[j].set(vec[k]);
1912 0 : j = k;
1913 0 : } while (j != i);
1914 :
1915 : // We could assert the loop invariant that |i == keys[i].elementIndex|
1916 : // here if we synced |keys[i].elementIndex|. But doing so would render
1917 : // the assertion vacuous, so don't bother, even in debug builds.
1918 0 : vec[i].set(tv);
1919 : }
1920 :
1921 0 : return true;
1922 : }
1923 :
1924 : /*
1925 : * Sort Values as strings.
1926 : *
1927 : * To minimize #conversions, SortLexicographically() first converts all Values
1928 : * to strings at once, then sorts the elements by these cached strings.
1929 : */
1930 : static bool
1931 0 : SortLexicographically(JSContext* cx, MutableHandle<GCVector<Value>> vec, size_t len)
1932 : {
1933 0 : MOZ_ASSERT(vec.length() >= len);
1934 :
1935 0 : StringBuffer sb(cx);
1936 0 : Vector<StringifiedElement, 0, TempAllocPolicy> strElements(cx);
1937 :
1938 : /* MergeSort uses the upper half as scratch space. */
1939 0 : if (!strElements.resize(2 * len))
1940 0 : return false;
1941 :
1942 : /* Convert Values to strings. */
1943 0 : size_t cursor = 0;
1944 0 : for (size_t i = 0; i < len; i++) {
1945 0 : if (!CheckForInterrupt(cx))
1946 0 : return false;
1947 :
1948 0 : if (!ValueToStringBuffer(cx, vec[i], sb))
1949 0 : return false;
1950 :
1951 0 : strElements[i] = { cursor, sb.length(), i };
1952 0 : cursor = sb.length();
1953 : }
1954 :
1955 : /* Sort Values in vec alphabetically. */
1956 0 : return MergeSortByKey(strElements.begin(), len, strElements.begin() + len,
1957 0 : SortComparatorStringifiedElements(cx, sb), vec);
1958 : }
1959 :
1960 : /*
1961 : * Sort Values as numbers.
1962 : *
1963 : * To minimize #conversions, SortNumerically first converts all Values to
1964 : * numerics at once, then sorts the elements by these cached numerics.
1965 : */
1966 : static bool
1967 0 : SortNumerically(JSContext* cx, MutableHandle<GCVector<Value>> vec, size_t len,
1968 : ComparatorMatchResult comp)
1969 : {
1970 0 : MOZ_ASSERT(vec.length() >= len);
1971 :
1972 0 : Vector<NumericElement, 0, TempAllocPolicy> numElements(cx);
1973 :
1974 : /* MergeSort uses the upper half as scratch space. */
1975 0 : if (!numElements.resize(2 * len))
1976 0 : return false;
1977 :
1978 : /* Convert Values to numerics. */
1979 0 : for (size_t i = 0; i < len; i++) {
1980 0 : if (!CheckForInterrupt(cx))
1981 0 : return false;
1982 :
1983 : double dv;
1984 0 : if (!ToNumber(cx, vec[i], &dv))
1985 0 : return false;
1986 :
1987 0 : numElements[i] = { dv, i };
1988 : }
1989 :
1990 : /* Sort Values in vec numerically. */
1991 0 : return MergeSortByKey(numElements.begin(), len, numElements.begin() + len,
1992 0 : SortComparatorNumerics[comp], vec);
1993 : }
1994 :
1995 : static bool
1996 0 : FillWithUndefined(JSContext* cx, HandleObject obj, uint32_t start, uint32_t count)
1997 : {
1998 0 : MOZ_ASSERT(start < start + count, "count > 0 and start + count doesn't overflow");
1999 :
2000 : do {
2001 : // Unboxed arrays can't store undefined values, so don't try to
2002 : // optimize them here.
2003 0 : if (!obj->is<NativeObject>())
2004 0 : break;
2005 0 : if (ObjectMayHaveExtraIndexedProperties(obj))
2006 0 : break;
2007 :
2008 0 : NativeObject* nobj = &obj->as<NativeObject>();
2009 0 : if (nobj->denseElementsAreFrozen())
2010 0 : break;
2011 :
2012 0 : if (obj->is<ArrayObject>() &&
2013 0 : !obj->as<ArrayObject>().lengthIsWritable() &&
2014 0 : start + count >= obj->as<ArrayObject>().length())
2015 : {
2016 0 : break;
2017 : }
2018 :
2019 0 : DenseElementResult result = nobj->ensureDenseElements(cx, start, count);
2020 0 : if (result != DenseElementResult::Success) {
2021 0 : if (result == DenseElementResult::Failure)
2022 0 : return false;
2023 0 : MOZ_ASSERT(result == DenseElementResult::Incomplete);
2024 0 : break;
2025 : }
2026 :
2027 0 : if (obj->is<ArrayObject>() && start + count >= obj->as<ArrayObject>().length())
2028 0 : obj->as<ArrayObject>().setLengthInt32(start + count);
2029 :
2030 0 : for (uint32_t i = 0; i < count; i++)
2031 0 : nobj->setDenseElementWithType(cx, start + i, UndefinedHandleValue);
2032 :
2033 0 : return true;
2034 : } while (false);
2035 :
2036 0 : for (uint32_t i = 0; i < count; i++) {
2037 0 : if (!CheckForInterrupt(cx) || !SetArrayElement(cx, obj, start + i, UndefinedHandleValue))
2038 0 : return false;
2039 : }
2040 :
2041 0 : return true;
2042 : }
2043 :
2044 : bool
2045 15 : js::array_sort(JSContext* cx, unsigned argc, Value* vp)
2046 : {
2047 15 : CallArgs args = CallArgsFromVp(argc, vp);
2048 :
2049 30 : RootedValue fval(cx);
2050 :
2051 15 : if (args.hasDefined(0)) {
2052 14 : if (args[0].isPrimitive()) {
2053 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_SORT_ARG);
2054 0 : return false;
2055 : }
2056 14 : fval = args[0]; /* non-default compare function */
2057 : } else {
2058 1 : fval.setNull();
2059 : }
2060 :
2061 30 : RootedObject obj(cx, ToObject(cx, args.thisv()));
2062 15 : if (!obj)
2063 0 : return false;
2064 :
2065 15 : ComparatorMatchResult comp = MatchNumericComparator(cx, fval);
2066 15 : if (comp == Match_Failure)
2067 0 : return false;
2068 :
2069 15 : if (!fval.isNull() && comp == Match_None) {
2070 : /*
2071 : * Non-optimized user supplied comparators perform much better when
2072 : * called from within a self-hosted sorting function.
2073 : */
2074 28 : FixedInvokeArgs<1> args2(cx);
2075 14 : args2[0].set(fval);
2076 :
2077 28 : RootedValue thisv(cx, ObjectValue(*obj));
2078 14 : return CallSelfHostedFunction(cx, cx->names().ArraySort, thisv, args2, args.rval());
2079 : }
2080 :
2081 : uint64_t length;
2082 1 : if (!GetLengthProperty(cx, obj, &length))
2083 0 : return false;
2084 1 : if (length < 2) {
2085 : /* [] and [a] remain unchanged when sorted. */
2086 0 : args.rval().setObject(*obj);
2087 0 : return true;
2088 : }
2089 :
2090 1 : if (length > UINT32_MAX) {
2091 0 : ReportAllocationOverflow(cx);
2092 0 : return false;
2093 : }
2094 1 : uint32_t len = uint32_t(length);
2095 :
2096 : /*
2097 : * We need a temporary array of 2 * len Value to hold the array elements
2098 : * and the scratch space for merge sort. Check that its size does not
2099 : * overflow size_t, which would allow for indexing beyond the end of the
2100 : * malloc'd vector.
2101 : */
2102 : #if JS_BITS_PER_WORD == 32
2103 : if (size_t(len) > size_t(-1) / (2 * sizeof(Value))) {
2104 : ReportAllocationOverflow(cx);
2105 : return false;
2106 : }
2107 : #endif
2108 :
2109 : size_t n, undefs;
2110 : {
2111 2 : Rooted<GCVector<Value>> vec(cx, GCVector<Value>(cx));
2112 1 : if (!vec.reserve(2 * size_t(len)))
2113 0 : return false;
2114 :
2115 : /*
2116 : * By ECMA 262, 15.4.4.11, a property that does not exist (which we
2117 : * call a "hole") is always greater than an existing property with
2118 : * value undefined and that is always greater than any other property.
2119 : * Thus to sort holes and undefs we simply count them, sort the rest
2120 : * of elements, append undefs after them and then make holes after
2121 : * undefs.
2122 : */
2123 1 : undefs = 0;
2124 1 : bool allStrings = true;
2125 1 : bool allInts = true;
2126 1 : bool extraIndexed = ObjectMayHaveExtraIndexedProperties(obj);
2127 2 : RootedValue v(cx);
2128 7 : for (uint32_t i = 0; i < len; i++) {
2129 6 : if (!CheckForInterrupt(cx))
2130 0 : return false;
2131 :
2132 : bool hole;
2133 6 : if (!HasAndGetElement(cx, obj, i, &hole, &v))
2134 0 : return false;
2135 6 : if (hole)
2136 0 : continue;
2137 6 : if (v.isUndefined()) {
2138 0 : ++undefs;
2139 0 : continue;
2140 : }
2141 6 : vec.infallibleAppend(v);
2142 6 : allStrings = allStrings && v.isString();
2143 6 : allInts = allInts && v.isInt32();
2144 : }
2145 :
2146 : /*
2147 : * If the array only contains holes, we're done. But if it contains
2148 : * undefs, those must be sorted to the front of the array.
2149 : */
2150 1 : n = vec.length();
2151 1 : if (n == 0 && undefs == 0) {
2152 0 : args.rval().setObject(*obj);
2153 0 : return true;
2154 : }
2155 :
2156 : /* Here len == n + undefs + number_of_holes. */
2157 1 : if (fval.isNull()) {
2158 : /*
2159 : * Sort using the default comparator converting all elements to
2160 : * strings.
2161 : */
2162 1 : if (allStrings) {
2163 1 : JS_ALWAYS_TRUE(vec.resize(n * 2));
2164 1 : if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorStrings(cx)))
2165 0 : return false;
2166 0 : } else if (allInts) {
2167 0 : JS_ALWAYS_TRUE(vec.resize(n * 2));
2168 0 : if (!MergeSort(vec.begin(), n, vec.begin() + n,
2169 0 : SortComparatorLexicographicInt32())) {
2170 0 : return false;
2171 : }
2172 : } else {
2173 0 : if (!SortLexicographically(cx, &vec, n))
2174 0 : return false;
2175 : }
2176 : } else {
2177 0 : if (allInts) {
2178 0 : JS_ALWAYS_TRUE(vec.resize(n * 2));
2179 0 : if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorInt32s[comp]))
2180 0 : return false;
2181 : } else {
2182 0 : if (!SortNumerically(cx, &vec, n, comp))
2183 0 : return false;
2184 : }
2185 : }
2186 :
2187 2 : ShouldUpdateTypes updateTypes = !extraIndexed && (allStrings || allInts)
2188 2 : ? ShouldUpdateTypes::DontUpdate
2189 1 : : ShouldUpdateTypes::Update;
2190 1 : if (!SetArrayElements(cx, obj, 0, uint32_t(n), vec.begin(), updateTypes))
2191 0 : return false;
2192 : }
2193 :
2194 : /* Set undefs that sorted after the rest of elements. */
2195 1 : if (undefs > 0) {
2196 0 : if (!FillWithUndefined(cx, obj, n, undefs))
2197 0 : return false;
2198 0 : n += undefs;
2199 : }
2200 :
2201 : /* Re-create any holes that sorted to the end of the array. */
2202 1 : while (len > n) {
2203 0 : if (!CheckForInterrupt(cx) || !DeletePropertyOrThrow(cx, obj, --len))
2204 0 : return false;
2205 : }
2206 1 : args.rval().setObject(*obj);
2207 1 : return true;
2208 : }
2209 :
2210 : bool
2211 0 : js::NewbornArrayPush(JSContext* cx, HandleObject obj, const Value& v)
2212 : {
2213 0 : Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
2214 :
2215 0 : MOZ_ASSERT(!v.isMagic());
2216 0 : MOZ_ASSERT(arr->lengthIsWritable());
2217 :
2218 0 : uint32_t length = arr->length();
2219 0 : MOZ_ASSERT(length <= arr->getDenseCapacity());
2220 :
2221 0 : if (!arr->ensureElements(cx, length + 1))
2222 0 : return false;
2223 :
2224 0 : arr->setDenseInitializedLength(length + 1);
2225 0 : arr->setLengthInt32(length + 1);
2226 0 : arr->initDenseElementWithType(cx, length, v);
2227 0 : return true;
2228 : }
2229 :
2230 : // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
2231 : // 22.1.3.18 Array.prototype.push ( ...items )
2232 : bool
2233 1929 : js::array_push(JSContext* cx, unsigned argc, Value* vp)
2234 : {
2235 3858 : AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.push");
2236 1929 : CallArgs args = CallArgsFromVp(argc, vp);
2237 :
2238 : // Step 1.
2239 3858 : RootedObject obj(cx, ToObject(cx, args.thisv()));
2240 1929 : if (!obj)
2241 0 : return false;
2242 :
2243 : // Step 2.
2244 : uint64_t length;
2245 1929 : if (!GetLengthProperty(cx, obj, &length))
2246 0 : return false;
2247 :
2248 1929 : if (!ObjectMayHaveExtraIndexedProperties(obj) && length <= UINT32_MAX) {
2249 : DenseElementResult result =
2250 5787 : SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, uint32_t(length),
2251 5787 : args.array(), args.length());
2252 1929 : if (result != DenseElementResult::Incomplete) {
2253 1929 : if (result == DenseElementResult::Failure)
2254 0 : return false;
2255 :
2256 1929 : uint32_t newlength = uint32_t(length) + args.length();
2257 1929 : args.rval().setNumber(newlength);
2258 :
2259 : // SetOrExtendAnyBoxedOrUnboxedDenseElements takes care of updating the
2260 : // length for boxed and unboxed arrays. Handle updates to the length of
2261 : // non-arrays here.
2262 1929 : if (!IsBoxedOrUnboxedArray(obj)) {
2263 0 : MOZ_ASSERT(obj->is<NativeObject>());
2264 0 : return SetLengthProperty(cx, obj, newlength);
2265 : }
2266 :
2267 1929 : return true;
2268 : }
2269 : }
2270 :
2271 : // Step 5.
2272 0 : uint64_t newlength = length + args.length();
2273 0 : if (newlength >= uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT)) {
2274 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TOO_LONG_ARRAY);
2275 0 : return false;
2276 : }
2277 :
2278 : // Steps 3-6.
2279 0 : if (!SetArrayElements(cx, obj, length, args.length(), args.array()))
2280 0 : return false;
2281 :
2282 : // Steps 7-8.
2283 0 : args.rval().setNumber(double(newlength));
2284 0 : return SetLengthProperty(cx, obj, newlength);
2285 : }
2286 :
2287 : // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
2288 : // 22.1.3.17 Array.prototype.pop ( )
2289 : bool
2290 1 : js::array_pop(JSContext* cx, unsigned argc, Value* vp)
2291 : {
2292 2 : AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.pop");
2293 1 : CallArgs args = CallArgsFromVp(argc, vp);
2294 :
2295 : // Step 1.
2296 2 : RootedObject obj(cx, ToObject(cx, args.thisv()));
2297 1 : if (!obj)
2298 0 : return false;
2299 :
2300 : // Step 2.
2301 : uint64_t index;
2302 1 : if (!GetLengthProperty(cx, obj, &index))
2303 0 : return false;
2304 :
2305 : // Steps 3-4.
2306 1 : if (index == 0) {
2307 : // Step 3.b.
2308 0 : args.rval().setUndefined();
2309 : } else {
2310 : // Steps 4.a-b.
2311 1 : index--;
2312 :
2313 : // Steps 4.c, 4.f.
2314 1 : if (!GetArrayElement(cx, obj, index, args.rval()))
2315 0 : return false;
2316 :
2317 : // Steps 4.d.
2318 1 : if (!DeletePropertyOrThrow(cx, obj, index))
2319 0 : return false;
2320 : }
2321 :
2322 : // Steps 3.a, 4.e.
2323 1 : return SetLengthProperty(cx, obj, index);
2324 : }
2325 :
2326 : template <JSValueType Type>
2327 : static inline DenseElementResult
2328 0 : ShiftMoveBoxedOrUnboxedDenseElements(JSObject* obj)
2329 : {
2330 0 : MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj));
2331 :
2332 0 : size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
2333 0 : MOZ_ASSERT(initlen > 0);
2334 :
2335 : if (Type == JSVAL_TYPE_MAGIC) {
2336 0 : if (!obj->as<NativeObject>().tryShiftDenseElements(1))
2337 0 : obj->as<NativeObject>().moveDenseElementsNoPreBarrier(0, 1, initlen - 1);
2338 : } else {
2339 0 : uint8_t* data = obj->as<UnboxedArrayObject>().elements();
2340 0 : size_t elementSize = UnboxedTypeSize(Type);
2341 0 : memmove(data, data + elementSize, (initlen - 1) * elementSize);
2342 : }
2343 :
2344 0 : return DenseElementResult::Success;
2345 : }
2346 :
2347 0 : DefineBoxedOrUnboxedFunctor1(ShiftMoveBoxedOrUnboxedDenseElements, JSObject*);
2348 :
2349 : void
2350 0 : js::ArrayShiftMoveElements(JSObject* obj)
2351 : {
2352 0 : MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().lengthIsWritable());
2353 :
2354 0 : ShiftMoveBoxedOrUnboxedDenseElementsFunctor functor(obj);
2355 0 : JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success);
2356 0 : }
2357 :
2358 : template <JSValueType Type>
2359 : DenseElementResult
2360 8 : ArrayShiftDenseKernel(JSContext* cx, HandleObject obj, MutableHandleValue rval)
2361 : {
2362 8 : if (ObjectMayHaveExtraIndexedProperties(obj))
2363 0 : return DenseElementResult::Incomplete;
2364 :
2365 8 : if (MaybeInIteration(obj, cx))
2366 0 : return DenseElementResult::Incomplete;
2367 :
2368 8 : size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
2369 8 : if (initlen == 0)
2370 1 : return DenseElementResult::Incomplete;
2371 :
2372 7 : rval.set(GetBoxedOrUnboxedDenseElement<Type>(obj, 0));
2373 7 : if (rval.isMagic(JS_ELEMENTS_HOLE))
2374 4 : rval.setUndefined();
2375 :
2376 : if (Type == JSVAL_TYPE_MAGIC) {
2377 7 : if (obj->as<NativeObject>().tryShiftDenseElements(1))
2378 7 : return DenseElementResult::Success;
2379 : }
2380 :
2381 0 : DenseElementResult result = MoveBoxedOrUnboxedDenseElements<Type>(cx, obj, 0, 1, initlen - 1);
2382 0 : if (result != DenseElementResult::Success)
2383 0 : return result;
2384 :
2385 0 : SetBoxedOrUnboxedInitializedLength<Type>(cx, obj, initlen - 1);
2386 0 : return DenseElementResult::Success;
2387 : }
2388 :
2389 16 : DefineBoxedOrUnboxedFunctor3(ArrayShiftDenseKernel,
2390 : JSContext*, HandleObject, MutableHandleValue);
2391 :
2392 : // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
2393 : // 22.1.3.22 Array.prototype.shift ( )
2394 : bool
2395 8 : js::array_shift(JSContext* cx, unsigned argc, Value* vp)
2396 : {
2397 16 : AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.shift");
2398 8 : CallArgs args = CallArgsFromVp(argc, vp);
2399 :
2400 : // Step 1.
2401 16 : RootedObject obj(cx, ToObject(cx, args.thisv()));
2402 8 : if (!obj)
2403 0 : return false;
2404 :
2405 : // Step 2.
2406 : uint64_t len;
2407 8 : if (!GetLengthProperty(cx, obj, &len))
2408 0 : return false;
2409 :
2410 : // Step 3.
2411 8 : if (len == 0) {
2412 : // Step 3.a.
2413 0 : if (!SetLengthProperty(cx, obj, uint32_t(0)))
2414 0 : return false;
2415 :
2416 : // Step 3.b.
2417 0 : args.rval().setUndefined();
2418 0 : return true;
2419 : }
2420 :
2421 8 : uint64_t newlen = len - 1;
2422 :
2423 : /* Fast paths. */
2424 : uint64_t startIndex;
2425 8 : ArrayShiftDenseKernelFunctor functor(cx, obj, args.rval());
2426 8 : DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
2427 8 : if (result != DenseElementResult::Incomplete) {
2428 7 : if (result == DenseElementResult::Failure)
2429 0 : return false;
2430 :
2431 7 : if (len <= UINT32_MAX)
2432 7 : return SetLengthProperty(cx, obj, newlen);
2433 :
2434 0 : startIndex = UINT32_MAX - 1;
2435 : } else {
2436 : // Steps 4, 9.
2437 1 : if (!GetElement(cx, obj, 0, args.rval()))
2438 0 : return false;
2439 :
2440 1 : startIndex = 0;
2441 : }
2442 :
2443 : // Steps 5-6.
2444 2 : RootedValue value(cx);
2445 30 : for (uint64_t i = startIndex; i < newlen; i++) {
2446 29 : if (!CheckForInterrupt(cx))
2447 0 : return false;
2448 : bool hole;
2449 29 : if (!HasAndGetElement(cx, obj, i + 1, &hole, &value))
2450 0 : return false;
2451 29 : if (hole) {
2452 29 : if (!DeletePropertyOrThrow(cx, obj, i))
2453 0 : return false;
2454 : } else {
2455 0 : if (!SetArrayElement(cx, obj, i, value))
2456 0 : return false;
2457 : }
2458 : }
2459 :
2460 : // Step 7.
2461 1 : if (!DeletePropertyOrThrow(cx, obj, newlen))
2462 0 : return false;
2463 :
2464 : // Step 8.
2465 1 : return SetLengthProperty(cx, obj, newlen);
2466 : }
2467 :
2468 : // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
2469 : // 22.1.3.29 Array.prototype.unshift ( ...items )
2470 : bool
2471 31 : js::array_unshift(JSContext* cx, unsigned argc, Value* vp)
2472 : {
2473 62 : AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.unshift");
2474 31 : CallArgs args = CallArgsFromVp(argc, vp);
2475 :
2476 : // Step 1.
2477 62 : RootedObject obj(cx, ToObject(cx, args.thisv()));
2478 31 : if (!obj)
2479 0 : return false;
2480 :
2481 : // Step 2.
2482 : uint64_t length;
2483 31 : if (!GetLengthProperty(cx, obj, &length))
2484 0 : return false;
2485 :
2486 : // Steps 3-4.
2487 31 : if (args.length() > 0) {
2488 : // Only include a fast path for native objects. Unboxed arrays can't
2489 : // be optimized here because unshifting temporarily places holes at
2490 : // the start of the array.
2491 : // TODO: Implement unboxed array optimization similar to the one in
2492 : // array_splice_impl(), unshift() is a special version of splice():
2493 : // arr.unshift(...values) ~= arr.splice(0, 0, ...values).
2494 31 : bool optimized = false;
2495 : do {
2496 31 : if (length > UINT32_MAX)
2497 0 : break;
2498 31 : if (!obj->isNative())
2499 0 : break;
2500 31 : if (ObjectMayHaveExtraIndexedProperties(obj))
2501 17 : break;
2502 14 : if (MaybeInIteration(obj, cx))
2503 0 : break;
2504 14 : NativeObject* nobj = &obj->as<NativeObject>();
2505 14 : if (nobj->denseElementsAreFrozen())
2506 0 : break;
2507 14 : if (nobj->is<ArrayObject>() && !nobj->as<ArrayObject>().lengthIsWritable())
2508 0 : break;
2509 14 : if (!nobj->tryUnshiftDenseElements(args.length())) {
2510 14 : DenseElementResult result = nobj->ensureDenseElements(cx, uint32_t(length), args.length());
2511 14 : if (result != DenseElementResult::Success) {
2512 0 : if (result == DenseElementResult::Failure)
2513 0 : return false;
2514 0 : MOZ_ASSERT(result == DenseElementResult::Incomplete);
2515 0 : break;
2516 : }
2517 14 : if (length > 0)
2518 14 : nobj->moveDenseElements(args.length(), 0, uint32_t(length));
2519 : }
2520 28 : for (uint32_t i = 0; i < args.length(); i++)
2521 14 : nobj->setDenseElementWithType(cx, i, args[i]);
2522 14 : optimized = true;
2523 : } while (false);
2524 :
2525 31 : if (!optimized) {
2526 17 : if (length > 0) {
2527 17 : uint64_t last = length;
2528 17 : uint64_t upperIndex = last + args.length();
2529 :
2530 : // Step 4.a.
2531 17 : if (upperIndex >= uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT)) {
2532 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TOO_LONG_ARRAY);
2533 0 : return false;
2534 : }
2535 :
2536 : // Steps 4.b-c.
2537 34 : RootedValue value(cx);
2538 32 : do {
2539 49 : --last; --upperIndex;
2540 49 : if (!CheckForInterrupt(cx))
2541 0 : return false;
2542 : bool hole;
2543 49 : if (!HasAndGetElement(cx, obj, last, &hole, &value))
2544 0 : return false;
2545 49 : if (hole) {
2546 0 : if (!DeletePropertyOrThrow(cx, obj, upperIndex))
2547 0 : return false;
2548 : } else {
2549 49 : if (!SetArrayElement(cx, obj, upperIndex, value))
2550 0 : return false;
2551 : }
2552 49 : } while (last != 0);
2553 : }
2554 :
2555 : // Steps 4.d-f.
2556 : /* Copy from args to the bottom of the array. */
2557 17 : if (!SetArrayElements(cx, obj, 0, args.length(), args.array()))
2558 0 : return false;
2559 : }
2560 : }
2561 :
2562 : // Step 5.
2563 31 : uint64_t newlength = length + args.length();
2564 31 : if (!SetLengthProperty(cx, obj, newlength))
2565 0 : return false;
2566 :
2567 : // Step 6.
2568 : /* Follow Perl by returning the new array length. */
2569 31 : args.rval().setNumber(double(newlength));
2570 31 : return true;
2571 : }
2572 :
2573 : enum class ArrayAccess {
2574 : Read, Write
2575 : };
2576 :
2577 : /*
2578 : * Returns true if this is a dense or unboxed array whose properties ending at
2579 : * |endIndex| (exclusive) may be accessed (get, set, delete) directly through
2580 : * its contiguous vector of elements without fear of getters, setters, etc.
2581 : * along the prototype chain, or of enumerators requiring notification of
2582 : * modifications.
2583 : */
2584 : template <ArrayAccess Access>
2585 : static bool
2586 75 : CanOptimizeForDenseStorage(HandleObject arr, uint64_t endIndex, JSContext* cx)
2587 : {
2588 : /* If the desired properties overflow dense storage, we can't optimize. */
2589 75 : if (endIndex > UINT32_MAX)
2590 0 : return false;
2591 :
2592 : if (Access == ArrayAccess::Read) {
2593 : /*
2594 : * Dense storage read access is possible for any packed array as long
2595 : * as we only access properties within the initialized length. In all
2596 : * other cases we need to ensure there are no other indexed properties
2597 : * on this object or on the prototype chain. Callers are required to
2598 : * clamp the read length, so it doesn't exceed the initialized length.
2599 : */
2600 87 : return (IsPackedArray(arr) && endIndex <= GetAnyBoxedOrUnboxedInitializedLength(arr)) ||
2601 87 : !ObjectMayHaveExtraIndexedProperties(arr);
2602 : }
2603 :
2604 : /* There's no optimizing possible if it's not an array. */
2605 20 : if (!IsBoxedOrUnboxedArray(arr))
2606 0 : return false;
2607 :
2608 : /* If the length is non-writable, always pick the slow path */
2609 20 : if (arr->is<ArrayObject>()) {
2610 20 : if (!arr->as<ArrayObject>().lengthIsWritable())
2611 0 : return false;
2612 :
2613 20 : MOZ_ASSERT(!arr->as<ArrayObject>().denseElementsAreFrozen(),
2614 : "writable length implies elements are not frozen");
2615 : }
2616 :
2617 : /* Also pick the slow path if the object is being iterated over. */
2618 20 : if (MaybeInIteration(arr, cx))
2619 0 : return false;
2620 :
2621 : /* Or we attempt to write to indices outside the initialized length. */
2622 20 : if (endIndex > GetAnyBoxedOrUnboxedInitializedLength(arr))
2623 0 : return false;
2624 :
2625 : /*
2626 : * Now watch out for getters and setters along the prototype chain or in
2627 : * other indexed properties on the object. Packed arrays don't have any
2628 : * other indexed properties by definition.
2629 : */
2630 20 : return IsPackedArray(arr) || !ObjectMayHaveExtraIndexedProperties(arr);
2631 : }
2632 :
2633 : static JSObject*
2634 4 : CopyDenseArrayElements(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t count)
2635 : {
2636 4 : size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(obj);
2637 4 : MOZ_ASSERT(initlen <= UINT32_MAX, "initialized length shouldn't exceed UINT32_MAX");
2638 4 : uint32_t newlength = 0;
2639 4 : if (initlen > begin)
2640 2 : newlength = Min<uint32_t>(initlen - begin, count);
2641 :
2642 4 : JSObject* narr = NewFullyAllocatedArrayTryReuseGroup(cx, obj, newlength);
2643 4 : if (!narr)
2644 0 : return nullptr;
2645 4 : SetAnyBoxedOrUnboxedArrayLength(cx, narr, count);
2646 :
2647 4 : if (newlength) {
2648 : DebugOnly<DenseElementResult> result =
2649 2 : CopyAnyBoxedOrUnboxedDenseElements(cx, narr, obj, 0, begin, newlength);
2650 1 : MOZ_ASSERT(result.value == DenseElementResult::Success);
2651 : }
2652 4 : return narr;
2653 : }
2654 :
2655 : static bool
2656 32 : CopyArrayElements(JSContext* cx, HandleObject obj, uint64_t begin, uint64_t count,
2657 : HandleObject result)
2658 : {
2659 32 : MOZ_ASSERT(IsBoxedOrUnboxedArray(result), "result is a newly allocated array object");
2660 32 : MOZ_ASSERT(GetAnyBoxedOrUnboxedArrayLength(result) == count);
2661 :
2662 32 : uint64_t startIndex = 0;
2663 64 : RootedValue value(cx);
2664 :
2665 : // Use dense storage for new indexed properties where possible.
2666 32 : if (result->is<ArrayObject>()) {
2667 32 : HandleArrayObject nresult = result.as<ArrayObject>();
2668 :
2669 32 : uint32_t index = 0;
2670 32 : uint32_t limit = Min<uint32_t>(count, JSID_INT_MAX);
2671 84 : for (; index < limit; index++) {
2672 : bool hole;
2673 104 : if (!CheckForInterrupt(cx) ||
2674 104 : !HasAndGetElement(cx, obj, begin + index, &hole, &value))
2675 : {
2676 0 : return false;
2677 : }
2678 :
2679 26 : if (!hole) {
2680 26 : DenseElementResult edResult = nresult->ensureDenseElements(cx, index, 1);
2681 26 : if (edResult != DenseElementResult::Success) {
2682 0 : if (edResult == DenseElementResult::Failure)
2683 0 : return false;
2684 :
2685 0 : MOZ_ASSERT(edResult == DenseElementResult::Incomplete);
2686 0 : if (!DefineElement(cx, nresult, index, value))
2687 0 : return false;
2688 :
2689 0 : break;
2690 : }
2691 26 : nresult->setDenseElementWithType(cx, index, value);
2692 : }
2693 : }
2694 :
2695 32 : startIndex = index + 1;
2696 : }
2697 :
2698 : // Copy any remaining elements.
2699 32 : for (uint64_t i = startIndex; i < count; i++) {
2700 : bool hole;
2701 0 : if (!CheckForInterrupt(cx) ||
2702 0 : !HasAndGetElement(cx, obj, begin + i, &hole, &value))
2703 : {
2704 0 : return false;
2705 : }
2706 :
2707 0 : if (!hole && !DefineArrayElement(cx, result, i, value))
2708 0 : return false;
2709 : }
2710 32 : return true;
2711 : }
2712 :
2713 : static bool
2714 20 : array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUsed)
2715 : {
2716 40 : AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.splice");
2717 20 : CallArgs args = CallArgsFromVp(argc, vp);
2718 :
2719 : /* Step 1. */
2720 40 : RootedObject obj(cx, ToObject(cx, args.thisv()));
2721 20 : if (!obj)
2722 0 : return false;
2723 :
2724 : /* Step 2. */
2725 : uint64_t len;
2726 20 : if (!GetLengthProperty(cx, obj, &len))
2727 0 : return false;
2728 :
2729 : /* Step 3. */
2730 : double relativeStart;
2731 20 : if (!ToInteger(cx, args.get(0), &relativeStart))
2732 0 : return false;
2733 :
2734 : /* Step 4. */
2735 : uint64_t actualStart;
2736 20 : if (relativeStart < 0)
2737 1 : actualStart = Max(len + relativeStart, 0.0);
2738 : else
2739 19 : actualStart = Min(relativeStart, double(len));
2740 :
2741 : /* Step 5. */
2742 : uint64_t actualDeleteCount;
2743 20 : if (args.length() == 0) {
2744 : /* Step 5.b. */
2745 0 : actualDeleteCount = 0;
2746 20 : } else if (args.length() == 1) {
2747 : /* Step 6.b. */
2748 0 : actualDeleteCount = len - actualStart;
2749 : } else {
2750 : /* Steps 7.b. */
2751 : double deleteCountDouble;
2752 40 : RootedValue cnt(cx, args[1]);
2753 20 : if (!ToInteger(cx, cnt, &deleteCountDouble))
2754 0 : return false;
2755 :
2756 : /* Step 7.c. */
2757 20 : actualDeleteCount = Min(Max(deleteCountDouble, 0.0), double(len - actualStart));
2758 :
2759 : /* Step 8. */
2760 20 : uint32_t insertCount = args.length() - 2;
2761 20 : if (len + insertCount - actualDeleteCount >= DOUBLE_INTEGRAL_PRECISION_LIMIT) {
2762 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TOO_LONG_ARRAY);
2763 0 : return false;
2764 : }
2765 : }
2766 :
2767 20 : MOZ_ASSERT(actualStart + actualDeleteCount <= len);
2768 :
2769 40 : RootedObject arr(cx);
2770 20 : if (IsArraySpecies(cx, obj)) {
2771 20 : if (actualDeleteCount > UINT32_MAX) {
2772 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
2773 0 : return false;
2774 : }
2775 20 : uint32_t count = uint32_t(actualDeleteCount);
2776 :
2777 20 : if (CanOptimizeForDenseStorage<ArrayAccess::Read>(obj, actualStart + count, cx)) {
2778 20 : MOZ_ASSERT(actualStart <= UINT32_MAX,
2779 : "if actualStart + count <= UINT32_MAX, then actualStart <= UINT32_MAX");
2780 20 : if (returnValueIsUsed) {
2781 : /* Steps 9-12. */
2782 1 : arr = CopyDenseArrayElements(cx, obj, uint32_t(actualStart), count);
2783 1 : if (!arr)
2784 0 : return false;
2785 : }
2786 : } else {
2787 : /* Step 9. */
2788 0 : arr = NewFullyAllocatedArrayTryReuseGroup(cx, obj, count);
2789 0 : if (!arr)
2790 0 : return false;
2791 :
2792 : /* Steps 10-11. */
2793 0 : if (!CopyArrayElements(cx, obj, actualStart, count, arr))
2794 0 : return false;
2795 :
2796 : /* Step 12 (implicit). */
2797 : }
2798 : } else {
2799 : /* Steps 9. */
2800 0 : if (!ArraySpeciesCreate(cx, obj, actualDeleteCount, &arr))
2801 0 : return false;
2802 :
2803 : /* Steps 10, 11, 11.d. */
2804 0 : RootedValue fromValue(cx);
2805 0 : for (uint64_t k = 0; k < actualDeleteCount; k++) {
2806 : /* Step 11.a (implicit). */
2807 :
2808 0 : if (!CheckForInterrupt(cx))
2809 0 : return false;
2810 :
2811 : /* Steps 11.b, 11.c.i. */
2812 : bool hole;
2813 0 : if (!HasAndGetElement(cx, obj, actualStart + k, &hole, &fromValue))
2814 0 : return false;
2815 :
2816 : /* Step 11.c. */
2817 0 : if (!hole) {
2818 : /* Step 11.c.ii. */
2819 0 : if (!DefineArrayElement(cx, arr, k, fromValue))
2820 0 : return false;
2821 : }
2822 : }
2823 :
2824 : /* Step 12. */
2825 0 : if (!SetLengthProperty(cx, arr, actualDeleteCount))
2826 0 : return false;
2827 : }
2828 :
2829 : /* Step 14. */
2830 20 : uint32_t itemCount = (args.length() >= 2) ? (args.length() - 2) : 0;
2831 20 : uint64_t finalLength = len - actualDeleteCount + itemCount;
2832 :
2833 20 : if (itemCount < actualDeleteCount) {
2834 : /* Step 15: the array is being shrunk. */
2835 2 : uint64_t sourceIndex = actualStart + actualDeleteCount;
2836 2 : uint64_t targetIndex = actualStart + itemCount;
2837 :
2838 2 : if (CanOptimizeForDenseStorage<ArrayAccess::Write>(obj, len, cx)) {
2839 2 : MOZ_ASSERT(sourceIndex <= len && targetIndex <= len && len <= UINT32_MAX,
2840 : "sourceIndex and targetIndex are uint32 array indices");
2841 2 : MOZ_ASSERT(finalLength < len, "finalLength is strictly less than len");
2842 :
2843 : /* Steps 15.a-b. */
2844 2 : if (targetIndex != 0 ||
2845 2 : !obj->is<NativeObject>() ||
2846 0 : !obj->as<NativeObject>().tryShiftDenseElements(sourceIndex))
2847 : {
2848 : DenseElementResult result =
2849 4 : MoveAnyBoxedOrUnboxedDenseElements(cx, obj, uint32_t(targetIndex),
2850 : uint32_t(sourceIndex),
2851 4 : uint32_t(len - sourceIndex));
2852 2 : MOZ_ASSERT(result != DenseElementResult::Incomplete);
2853 2 : if (result == DenseElementResult::Failure)
2854 0 : return false;
2855 : }
2856 :
2857 : /* Steps 15.c-d. */
2858 2 : SetAnyBoxedOrUnboxedInitializedLength(cx, obj, uint32_t(finalLength));
2859 : } else {
2860 : /*
2861 : * This is all very slow if the length is very large. We don't yet
2862 : * have the ability to iterate in sorted order, so we just do the
2863 : * pessimistic thing and let CheckForInterrupt handle the
2864 : * fallout.
2865 : */
2866 :
2867 : /* Steps 15.a-b. */
2868 0 : RootedValue fromValue(cx);
2869 0 : for (uint64_t from = sourceIndex, to = targetIndex; from < len; from++, to++) {
2870 : /* Steps 15.b.i-ii (implicit). */
2871 :
2872 0 : if (!CheckForInterrupt(cx))
2873 0 : return false;
2874 :
2875 : /* Steps 15.b.iii, 15.b.iv.1. */
2876 : bool hole;
2877 0 : if (!HasAndGetElement(cx, obj, from, &hole, &fromValue))
2878 0 : return false;
2879 :
2880 : /* Steps 15.b.iv. */
2881 0 : if (hole) {
2882 : /* Steps 15.b.v.1. */
2883 0 : if (!DeletePropertyOrThrow(cx, obj, to))
2884 0 : return false;
2885 : } else {
2886 : /* Step 15.b.iv.2. */
2887 0 : if (!SetArrayElement(cx, obj, to, fromValue))
2888 0 : return false;
2889 : }
2890 : }
2891 :
2892 : /* Steps 15.c-d. */
2893 0 : for (uint64_t k = len; k > finalLength; k--) {
2894 : /* Steps 15.d.i-ii. */
2895 0 : if (!DeletePropertyOrThrow(cx, obj, k - 1))
2896 0 : return false;
2897 : }
2898 : }
2899 18 : } else if (itemCount > actualDeleteCount) {
2900 18 : MOZ_ASSERT(actualDeleteCount <= UINT32_MAX);
2901 18 : uint32_t deleteCount = uint32_t(actualDeleteCount);
2902 :
2903 : /* Step 16. */
2904 :
2905 : /*
2906 : * Optimize only if the array is already dense and we can extend it to
2907 : * its new length. It would be wrong to extend the elements here for a
2908 : * number of reasons.
2909 : *
2910 : * First, this could cause us to fall into the fast-path below. This
2911 : * would cause elements to be moved into places past the non-writable
2912 : * length. And when the dense initialized length is updated, that'll
2913 : * cause the |in| operator to think that those elements actually exist,
2914 : * even though, properly, setting them must fail.
2915 : *
2916 : * Second, extending the elements here will trigger assertions inside
2917 : * ensureDenseElements that the elements aren't being extended past the
2918 : * length of a non-writable array. This is because extending elements
2919 : * will extend capacity -- which might extend them past a non-writable
2920 : * length, violating the |capacity <= length| invariant for such
2921 : * arrays. And that would make the various JITted fast-path method
2922 : * implementations of [].push, [].unshift, and so on wrong.
2923 : *
2924 : * If the array length is non-writable, this method *will* throw. For
2925 : * simplicity, have the slow-path code do it. (Also note that the slow
2926 : * path may validly *not* throw -- if all the elements being moved are
2927 : * holes.)
2928 : */
2929 54 : if (obj->is<ArrayObject>() &&
2930 36 : !ObjectMayHaveExtraIndexedProperties(obj) &&
2931 18 : len <= UINT32_MAX)
2932 : {
2933 36 : Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
2934 18 : if (arr->lengthIsWritable()) {
2935 : DenseElementResult result =
2936 18 : arr->ensureDenseElements(cx, uint32_t(len), itemCount - deleteCount);
2937 18 : if (result == DenseElementResult::Failure)
2938 0 : return false;
2939 : }
2940 : }
2941 :
2942 18 : if (CanOptimizeForDenseStorage<ArrayAccess::Write>(obj, finalLength, cx)) {
2943 18 : MOZ_ASSERT((actualStart + actualDeleteCount) <= len && len <= UINT32_MAX,
2944 : "start and deleteCount are uint32 array indices");
2945 18 : MOZ_ASSERT(actualStart + itemCount <= UINT32_MAX,
2946 : "can't overflow because |len - actualDeleteCount + itemCount <= UINT32_MAX| "
2947 : "and |actualStart <= len - actualDeleteCount| are both true");
2948 18 : uint32_t start = uint32_t(actualStart);
2949 18 : uint32_t length = uint32_t(len);
2950 :
2951 : DenseElementResult result =
2952 36 : MoveAnyBoxedOrUnboxedDenseElements(cx, obj, start + itemCount,
2953 : start + deleteCount,
2954 54 : length - (start + deleteCount));
2955 18 : MOZ_ASSERT(result != DenseElementResult::Incomplete);
2956 18 : if (result == DenseElementResult::Failure)
2957 0 : return false;
2958 :
2959 : /* Steps 16.a-b. */
2960 18 : SetAnyBoxedOrUnboxedInitializedLength(cx, obj, uint32_t(finalLength));
2961 : } else {
2962 0 : RootedValue fromValue(cx);
2963 0 : for (uint64_t k = len - actualDeleteCount; k > actualStart; k--) {
2964 0 : if (!CheckForInterrupt(cx))
2965 0 : return false;
2966 :
2967 : /* Step 16.b.i. */
2968 0 : uint64_t from = k + actualDeleteCount - 1;
2969 :
2970 : /* Step 16.b.ii. */
2971 0 : uint64_t to = k + itemCount - 1;
2972 :
2973 : /* Steps 16.b.iii, 16.b.iv.1. */
2974 : bool hole;
2975 0 : if (!HasAndGetElement(cx, obj, from, &hole, &fromValue))
2976 0 : return false;
2977 :
2978 : /* Steps 16.b.iv. */
2979 0 : if (hole) {
2980 : /* Step 16.b.v.1. */
2981 0 : if (!DeletePropertyOrThrow(cx, obj, to))
2982 0 : return false;
2983 : } else {
2984 : /* Step 16.b.iv.2. */
2985 0 : if (!SetArrayElement(cx, obj, to, fromValue))
2986 0 : return false;
2987 : }
2988 : }
2989 : }
2990 : }
2991 :
2992 : /* Step 13 (reordered). */
2993 20 : Value* items = args.array() + 2;
2994 :
2995 : /* Steps 17-18. */
2996 20 : if (!SetArrayElements(cx, obj, actualStart, itemCount, items))
2997 0 : return false;
2998 :
2999 : /* Step 19. */
3000 20 : if (!SetLengthProperty(cx, obj, finalLength))
3001 0 : return false;
3002 :
3003 : /* Step 20. */
3004 20 : if (returnValueIsUsed)
3005 1 : args.rval().setObject(*arr);
3006 :
3007 20 : return true;
3008 : }
3009 :
3010 : /* ES 2016 draft Mar 25, 2016 22.1.3.26. */
3011 : bool
3012 1 : js::array_splice(JSContext* cx, unsigned argc, Value* vp)
3013 : {
3014 1 : return array_splice_impl(cx, argc, vp, true);
3015 : }
3016 :
3017 : static bool
3018 19 : array_splice_noRetVal(JSContext* cx, unsigned argc, Value* vp)
3019 : {
3020 19 : return array_splice_impl(cx, argc, vp, false);
3021 : }
3022 :
3023 : struct SortComparatorIndexes
3024 : {
3025 0 : bool operator()(uint32_t a, uint32_t b, bool* lessOrEqualp) {
3026 0 : *lessOrEqualp = (a <= b);
3027 0 : return true;
3028 : }
3029 : };
3030 :
3031 : // Returns all indexed properties in the range [begin, end) found on |obj| or
3032 : // its proto chain. This function does not handle proxies, objects with
3033 : // resolve/lookupProperty hooks or indexed getters, as those can introduce
3034 : // new properties. In those cases, *success is set to |false|.
3035 : static bool
3036 0 : GetIndexedPropertiesInRange(JSContext* cx, HandleObject obj, uint64_t begin, uint64_t end,
3037 : Vector<uint32_t>& indexes, bool* success)
3038 : {
3039 0 : *success = false;
3040 :
3041 : // TODO: Add IdIsIndex with support for large indices.
3042 0 : if (end > UINT32_MAX)
3043 0 : return true;
3044 0 : MOZ_ASSERT(begin <= UINT32_MAX);
3045 :
3046 : // First, look for proxies or class hooks that can introduce extra
3047 : // properties.
3048 0 : JSObject* pobj = obj;
3049 0 : do {
3050 0 : if (!pobj->isNative() || pobj->getClass()->getResolve() || pobj->getOpsLookupProperty())
3051 0 : return true;
3052 : } while ((pobj = pobj->staticPrototype()));
3053 :
3054 : // Collect indexed property names.
3055 0 : pobj = obj;
3056 0 : do {
3057 : // Append dense elements.
3058 0 : NativeObject* nativeObj = &pobj->as<NativeObject>();
3059 0 : uint32_t initLen = nativeObj->getDenseInitializedLength();
3060 0 : for (uint32_t i = begin; i < initLen && i < end; i++) {
3061 0 : if (nativeObj->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE))
3062 0 : continue;
3063 0 : if (!indexes.append(i))
3064 0 : return false;
3065 : }
3066 :
3067 : // Append typed array elements.
3068 0 : if (pobj->is<TypedArrayObject>()) {
3069 0 : uint32_t len = pobj->as<TypedArrayObject>().length();
3070 0 : for (uint32_t i = begin; i < len && i < end; i++) {
3071 0 : if (!indexes.append(i))
3072 0 : return false;
3073 : }
3074 : }
3075 :
3076 : // Append sparse elements.
3077 0 : if (pobj->isIndexed()) {
3078 0 : Shape::Range<NoGC> r(pobj->as<NativeObject>().lastProperty());
3079 0 : for (; !r.empty(); r.popFront()) {
3080 0 : Shape& shape = r.front();
3081 0 : jsid id = shape.propid();
3082 : uint32_t i;
3083 0 : if (!IdIsIndex(id, &i))
3084 0 : continue;
3085 :
3086 0 : if (!(begin <= i && i < end))
3087 0 : continue;
3088 :
3089 : // Watch out for getters, they can add new properties.
3090 0 : if (!shape.hasDefaultGetter())
3091 0 : return true;
3092 :
3093 0 : if (!indexes.append(i))
3094 0 : return false;
3095 : }
3096 : }
3097 : } while ((pobj = pobj->staticPrototype()));
3098 :
3099 : // Sort the indexes.
3100 0 : Vector<uint32_t> tmp(cx);
3101 0 : size_t n = indexes.length();
3102 0 : if (!tmp.resize(n))
3103 0 : return false;
3104 0 : if (!MergeSort(indexes.begin(), n, tmp.begin(), SortComparatorIndexes()))
3105 0 : return false;
3106 :
3107 : // Remove duplicates.
3108 0 : if (!indexes.empty()) {
3109 0 : uint32_t last = 0;
3110 0 : for (size_t i = 1, len = indexes.length(); i < len; i++) {
3111 0 : uint32_t elem = indexes[i];
3112 0 : if (indexes[last] != elem) {
3113 0 : last++;
3114 0 : indexes[last] = elem;
3115 : }
3116 : }
3117 0 : if (!indexes.resize(last + 1))
3118 0 : return false;
3119 : }
3120 :
3121 0 : *success = true;
3122 0 : return true;
3123 : }
3124 :
3125 : static bool
3126 0 : SliceSparse(JSContext* cx, HandleObject obj, uint64_t begin, uint64_t end, HandleObject result)
3127 : {
3128 0 : MOZ_ASSERT(begin <= end);
3129 :
3130 0 : Vector<uint32_t> indexes(cx);
3131 : bool success;
3132 0 : if (!GetIndexedPropertiesInRange(cx, obj, begin, end, indexes, &success))
3133 0 : return false;
3134 :
3135 0 : if (!success)
3136 0 : return CopyArrayElements(cx, obj, begin, end - begin, result);
3137 :
3138 0 : MOZ_ASSERT(end <= UINT32_MAX,
3139 : "indices larger than UINT32_MAX should be rejected by GetIndexedPropertiesInRange");
3140 :
3141 0 : RootedValue value(cx);
3142 0 : for (uint32_t index : indexes) {
3143 0 : MOZ_ASSERT(begin <= index && index < end);
3144 :
3145 : bool hole;
3146 0 : if (!HasAndGetElement(cx, obj, index, &hole, &value))
3147 0 : return false;
3148 :
3149 0 : if (!hole && !DefineElement(cx, result, index - uint32_t(begin), value))
3150 0 : return false;
3151 : }
3152 :
3153 0 : return true;
3154 : }
3155 :
3156 : template <typename T, typename ArrayLength>
3157 : static inline ArrayLength
3158 43 : NormalizeSliceTerm(T value, ArrayLength length)
3159 : {
3160 43 : if (value < 0) {
3161 1 : value += length;
3162 1 : if (value < 0)
3163 0 : return 0;
3164 42 : } else if (double(value) > double(length)) {
3165 1 : return length;
3166 : }
3167 42 : return ArrayLength(value);
3168 : }
3169 :
3170 : static bool
3171 35 : ArraySliceOrdinary(JSContext* cx, HandleObject obj, uint64_t begin, uint64_t end,
3172 : MutableHandleValue rval)
3173 : {
3174 35 : if (begin > end)
3175 0 : begin = end;
3176 :
3177 35 : if ((end - begin) > UINT32_MAX) {
3178 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
3179 0 : return false;
3180 : }
3181 35 : uint32_t count = uint32_t(end - begin);
3182 :
3183 35 : if (CanOptimizeForDenseStorage<ArrayAccess::Read>(obj, end, cx)) {
3184 3 : MOZ_ASSERT(begin <= UINT32_MAX, "if end <= UINT32_MAX, then begin <= UINT32_MAX");
3185 3 : JSObject* narr = CopyDenseArrayElements(cx, obj, uint32_t(begin), count);
3186 3 : if (!narr)
3187 0 : return false;
3188 :
3189 3 : rval.setObject(*narr);
3190 3 : return true;
3191 : }
3192 :
3193 64 : RootedObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, count));
3194 32 : if (!narr)
3195 0 : return false;
3196 :
3197 32 : if (end <= UINT32_MAX) {
3198 32 : if (js::GetElementsOp op = obj->getOpsGetElements()) {
3199 0 : ElementAdder adder(cx, narr, count, ElementAdder::CheckHasElemPreserveHoles);
3200 0 : if (!op(cx, obj, uint32_t(begin), uint32_t(end), &adder))
3201 0 : return false;
3202 :
3203 0 : rval.setObject(*narr);
3204 0 : return true;
3205 : }
3206 : }
3207 :
3208 32 : if (obj->isNative() && obj->isIndexed() && count > 1000) {
3209 0 : if (!SliceSparse(cx, obj, begin, end, narr))
3210 0 : return false;
3211 : } else {
3212 32 : if (!CopyArrayElements(cx, obj, begin, count, narr))
3213 0 : return false;
3214 : }
3215 :
3216 32 : rval.setObject(*narr);
3217 32 : return true;
3218 : }
3219 :
3220 : /* ES 2016 draft Mar 25, 2016 22.1.3.23. */
3221 : bool
3222 37 : js::array_slice(JSContext* cx, unsigned argc, Value* vp)
3223 : {
3224 74 : AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.slice");
3225 37 : CallArgs args = CallArgsFromVp(argc, vp);
3226 :
3227 : /* Step 1. */
3228 74 : RootedObject obj(cx, ToObject(cx, args.thisv()));
3229 37 : if (!obj)
3230 0 : return false;
3231 :
3232 : /* Step 2. */
3233 : uint64_t length;
3234 37 : if (!GetLengthProperty(cx, obj, &length))
3235 0 : return false;
3236 :
3237 37 : uint64_t k = 0;
3238 37 : uint64_t final = length;
3239 37 : if (args.length() > 0) {
3240 : double d;
3241 : /* Step 3. */
3242 37 : if (!ToInteger(cx, args[0], &d))
3243 0 : return false;
3244 :
3245 : /* Step 4. */
3246 37 : k = NormalizeSliceTerm(d, length);
3247 :
3248 37 : if (args.hasDefined(1)) {
3249 : /* Step 5. */
3250 6 : if (!ToInteger(cx, args[1], &d))
3251 0 : return false;
3252 :
3253 : /* Step 6. */
3254 6 : final = NormalizeSliceTerm(d, length);
3255 : }
3256 : }
3257 :
3258 37 : if (IsArraySpecies(cx, obj)) {
3259 : /* Steps 7-12: Optimized for ordinary array. */
3260 35 : return ArraySliceOrdinary(cx, obj, k, final, args.rval());
3261 : }
3262 :
3263 : /* Step 7. */
3264 2 : uint64_t count = final > k ? final - k : 0;
3265 :
3266 : /* Step 8. */
3267 4 : RootedObject arr(cx);
3268 2 : if (!ArraySpeciesCreate(cx, obj, count, &arr))
3269 0 : return false;
3270 :
3271 : /* Step 9. */
3272 2 : uint64_t n = 0;
3273 :
3274 : /* Step 10. */
3275 4 : RootedValue kValue(cx);
3276 2 : while (k < final) {
3277 0 : if (!CheckForInterrupt(cx))
3278 0 : return false;
3279 :
3280 : /* Steps 10.a-b, and 10.c.i. */
3281 : bool kNotPresent;
3282 0 : if (!HasAndGetElement(cx, obj, k, &kNotPresent, &kValue))
3283 0 : return false;
3284 :
3285 : /* Step 10.c. */
3286 0 : if (!kNotPresent) {
3287 : /* Steps 10.c.ii. */
3288 0 : if (!DefineArrayElement(cx, arr, n, kValue))
3289 0 : return false;
3290 : }
3291 : /* Step 10.d. */
3292 0 : k++;
3293 :
3294 : /* Step 10.e. */
3295 0 : n++;
3296 : }
3297 :
3298 : /* Step 11. */
3299 2 : if (!SetLengthProperty(cx, arr, n))
3300 0 : return false;
3301 :
3302 : /* Step 12. */
3303 2 : args.rval().setObject(*arr);
3304 2 : return true;
3305 : }
3306 :
3307 : template <JSValueType Type>
3308 : DenseElementResult
3309 0 : ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t endArg, JSObject* result)
3310 : {
3311 0 : uint32_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
3312 :
3313 0 : uint32_t begin = NormalizeSliceTerm(beginArg, length);
3314 0 : uint32_t end = NormalizeSliceTerm(endArg, length);
3315 :
3316 0 : if (begin > end)
3317 0 : begin = end;
3318 :
3319 0 : uint32_t count = end - begin;
3320 0 : size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
3321 0 : if (initlen > begin) {
3322 0 : uint32_t newlength = Min<uint32_t>(initlen - begin, count);
3323 0 : if (newlength) {
3324 0 : DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements<Type>(cx, result, newlength);
3325 0 : if (rv != DenseElementResult::Success)
3326 0 : return rv;
3327 0 : CopyBoxedOrUnboxedDenseElements<Type, Type>(cx, result, obj, 0, begin, newlength);
3328 : }
3329 : }
3330 :
3331 0 : SetAnyBoxedOrUnboxedArrayLength(cx, result, count);
3332 0 : return DenseElementResult::Success;
3333 : }
3334 :
3335 0 : DefineBoxedOrUnboxedFunctor5(ArraySliceDenseKernel,
3336 : JSContext*, JSObject*, int32_t, int32_t, JSObject*);
3337 :
3338 : JSObject*
3339 0 : js::array_slice_dense(JSContext* cx, HandleObject obj, int32_t begin, int32_t end,
3340 : HandleObject result)
3341 : {
3342 0 : if (result && IsArraySpecies(cx, obj)) {
3343 0 : ArraySliceDenseKernelFunctor functor(cx, obj, begin, end, result);
3344 0 : DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, result);
3345 0 : MOZ_ASSERT(rv != DenseElementResult::Incomplete);
3346 0 : return rv == DenseElementResult::Success ? result : nullptr;
3347 : }
3348 :
3349 : // Slower path if the JIT wasn't able to allocate an object inline.
3350 0 : JS::AutoValueArray<4> argv(cx);
3351 0 : argv[0].setUndefined();
3352 0 : argv[1].setObject(*obj);
3353 0 : argv[2].setInt32(begin);
3354 0 : argv[3].setInt32(end);
3355 0 : if (!array_slice(cx, 2, argv.begin()))
3356 0 : return nullptr;
3357 0 : return &argv[0].toObject();
3358 : }
3359 :
3360 : static bool
3361 336 : array_isArray(JSContext* cx, unsigned argc, Value* vp)
3362 : {
3363 336 : CallArgs args = CallArgsFromVp(argc, vp);
3364 336 : bool isArray = false;
3365 336 : if (args.get(0).isObject()) {
3366 76 : RootedObject obj(cx, &args[0].toObject());
3367 38 : if (!IsArray(cx, obj, &isArray))
3368 0 : return false;
3369 : }
3370 336 : args.rval().setBoolean(isArray);
3371 336 : return true;
3372 : }
3373 :
3374 : static bool
3375 24 : ArrayFromCallArgs(JSContext* cx, CallArgs& args, HandleObject proto = nullptr)
3376 : {
3377 24 : JSObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length(), proto);
3378 24 : if (!obj)
3379 0 : return false;
3380 :
3381 24 : args.rval().setObject(*obj);
3382 24 : return true;
3383 : }
3384 :
3385 : static bool
3386 0 : array_of(JSContext* cx, unsigned argc, Value* vp)
3387 : {
3388 0 : CallArgs args = CallArgsFromVp(argc, vp);
3389 :
3390 0 : if (IsArrayConstructor(args.thisv()) || !IsConstructor(args.thisv())) {
3391 : // IsArrayConstructor(this) will usually be true in practice. This is
3392 : // the most common path.
3393 0 : return ArrayFromCallArgs(cx, args);
3394 : }
3395 :
3396 : // Step 4.
3397 0 : RootedObject obj(cx);
3398 : {
3399 0 : FixedConstructArgs<1> cargs(cx);
3400 :
3401 0 : cargs[0].setNumber(args.length());
3402 :
3403 0 : if (!Construct(cx, args.thisv(), cargs, args.thisv(), &obj))
3404 0 : return false;
3405 : }
3406 :
3407 : // Step 8.
3408 0 : for (unsigned k = 0; k < args.length(); k++) {
3409 0 : if (!DefineElement(cx, obj, k, args[k]))
3410 0 : return false;
3411 : }
3412 :
3413 : // Steps 9-10.
3414 0 : if (!SetLengthProperty(cx, obj, args.length()))
3415 0 : return false;
3416 :
3417 : // Step 11.
3418 0 : args.rval().setObject(*obj);
3419 0 : return true;
3420 : }
3421 :
3422 : const JSJitInfo js::array_splice_info = {
3423 : { (JSJitGetterOp)array_splice_noRetVal },
3424 : { 0 }, /* unused */
3425 : { 0 }, /* unused */
3426 : JSJitInfo::IgnoresReturnValueNative,
3427 : JSJitInfo::AliasEverything,
3428 : JSVAL_TYPE_UNDEFINED,
3429 : };
3430 :
3431 : static const JSFunctionSpec array_methods[] = {
3432 : #if JS_HAS_TOSOURCE
3433 : JS_FN(js_toSource_str, array_toSource, 0,0),
3434 : #endif
3435 : JS_SELF_HOSTED_FN(js_toString_str, "ArrayToString", 0,0),
3436 : JS_FN(js_toLocaleString_str, array_toLocaleString, 0,0),
3437 :
3438 : /* Perl-ish methods. */
3439 : JS_INLINABLE_FN("join", array_join, 1,0, ArrayJoin),
3440 : JS_FN("reverse", array_reverse, 0,0),
3441 : JS_FN("sort", array_sort, 1,0),
3442 : JS_INLINABLE_FN("push", array_push, 1,0, ArrayPush),
3443 : JS_INLINABLE_FN("pop", array_pop, 0,0, ArrayPop),
3444 : JS_INLINABLE_FN("shift", array_shift, 0,0, ArrayShift),
3445 : JS_FN("unshift", array_unshift, 1,0),
3446 : JS_FNINFO("splice", array_splice, &array_splice_info, 2,0),
3447 :
3448 : /* Pythonic sequence methods. */
3449 : JS_SELF_HOSTED_FN("concat", "ArrayConcat", 1,0),
3450 : JS_INLINABLE_FN("slice", array_slice, 2,0, ArraySlice),
3451 :
3452 : JS_SELF_HOSTED_FN("lastIndexOf", "ArrayLastIndexOf", 1,0),
3453 : JS_SELF_HOSTED_FN("indexOf", "ArrayIndexOf", 1,0),
3454 : JS_SELF_HOSTED_FN("forEach", "ArrayForEach", 1,0),
3455 : JS_SELF_HOSTED_FN("map", "ArrayMap", 1,0),
3456 : JS_SELF_HOSTED_FN("filter", "ArrayFilter", 1,0),
3457 : JS_SELF_HOSTED_FN("reduce", "ArrayReduce", 1,0),
3458 : JS_SELF_HOSTED_FN("reduceRight", "ArrayReduceRight", 1,0),
3459 : JS_SELF_HOSTED_FN("some", "ArraySome", 1,0),
3460 : JS_SELF_HOSTED_FN("every", "ArrayEvery", 1,0),
3461 :
3462 : /* ES6 additions */
3463 : JS_SELF_HOSTED_FN("find", "ArrayFind", 1,0),
3464 : JS_SELF_HOSTED_FN("findIndex", "ArrayFindIndex", 1,0),
3465 : JS_SELF_HOSTED_FN("copyWithin", "ArrayCopyWithin", 3,0),
3466 :
3467 : JS_SELF_HOSTED_FN("fill", "ArrayFill", 3,0),
3468 :
3469 : JS_SELF_HOSTED_SYM_FN(iterator, "ArrayValues", 0,0),
3470 : JS_SELF_HOSTED_FN("entries", "ArrayEntries", 0,0),
3471 : JS_SELF_HOSTED_FN("keys", "ArrayKeys", 0,0),
3472 : #ifdef NIGHTLY_BUILD
3473 : JS_SELF_HOSTED_FN("values", "ArrayValues", 0,0),
3474 : #endif
3475 :
3476 : /* ES7 additions */
3477 : JS_SELF_HOSTED_FN("includes", "ArrayIncludes", 2,0),
3478 : JS_FS_END
3479 : };
3480 :
3481 : static const JSFunctionSpec array_static_methods[] = {
3482 : JS_INLINABLE_FN("isArray", array_isArray, 1,0, ArrayIsArray),
3483 : JS_SELF_HOSTED_FN("concat", "ArrayStaticConcat", 2,0),
3484 : JS_SELF_HOSTED_FN("lastIndexOf", "ArrayStaticLastIndexOf", 2,0),
3485 : JS_SELF_HOSTED_FN("indexOf", "ArrayStaticIndexOf", 2,0),
3486 : JS_SELF_HOSTED_FN("forEach", "ArrayStaticForEach", 2,0),
3487 : JS_SELF_HOSTED_FN("map", "ArrayStaticMap", 2,0),
3488 : JS_SELF_HOSTED_FN("filter", "ArrayStaticFilter", 2,0),
3489 : JS_SELF_HOSTED_FN("every", "ArrayStaticEvery", 2,0),
3490 : JS_SELF_HOSTED_FN("some", "ArrayStaticSome", 2,0),
3491 : JS_SELF_HOSTED_FN("reduce", "ArrayStaticReduce", 2,0),
3492 : JS_SELF_HOSTED_FN("reduceRight", "ArrayStaticReduceRight", 2,0),
3493 : JS_SELF_HOSTED_FN("join", "ArrayStaticJoin", 2,0),
3494 : JS_SELF_HOSTED_FN("reverse", "ArrayStaticReverse", 1,0),
3495 : JS_SELF_HOSTED_FN("sort", "ArrayStaticSort", 2,0),
3496 : JS_SELF_HOSTED_FN("push", "ArrayStaticPush", 2,0),
3497 : JS_SELF_HOSTED_FN("pop", "ArrayStaticPop", 1,0),
3498 : JS_SELF_HOSTED_FN("shift", "ArrayStaticShift", 1,0),
3499 : JS_SELF_HOSTED_FN("unshift", "ArrayStaticUnshift", 2,0),
3500 : JS_SELF_HOSTED_FN("splice", "ArrayStaticSplice", 3,0),
3501 : JS_SELF_HOSTED_FN("slice", "ArrayStaticSlice", 3,0),
3502 : JS_SELF_HOSTED_FN("from", "ArrayFrom", 3,0),
3503 : JS_FN("of", array_of, 0,0),
3504 :
3505 : JS_FS_END
3506 : };
3507 :
3508 : const JSPropertySpec array_static_props[] = {
3509 : JS_SELF_HOSTED_SYM_GET(species, "ArraySpecies", 0),
3510 : JS_PS_END
3511 : };
3512 :
3513 : static inline bool
3514 181 : ArrayConstructorImpl(JSContext* cx, CallArgs& args, bool isConstructor)
3515 : {
3516 362 : RootedObject proto(cx);
3517 181 : if (isConstructor) {
3518 24 : if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
3519 0 : return false;
3520 : } else {
3521 : // We're emulating |new Array(n)| with |std_Array(n)| in self-hosted JS,
3522 : // and the proto should be %ArrayPrototype% regardless of the callee.
3523 157 : proto = GlobalObject::getOrCreateArrayPrototype(cx, cx->global());
3524 157 : if (!proto)
3525 0 : return false;
3526 : }
3527 :
3528 181 : if (args.length() != 1 || !args[0].isNumber())
3529 24 : return ArrayFromCallArgs(cx, args, proto);
3530 :
3531 : uint32_t length;
3532 157 : if (args[0].isInt32()) {
3533 157 : int32_t i = args[0].toInt32();
3534 157 : if (i < 0) {
3535 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
3536 0 : return false;
3537 : }
3538 157 : length = uint32_t(i);
3539 : } else {
3540 0 : double d = args[0].toDouble();
3541 0 : length = ToUint32(d);
3542 0 : if (d != double(length)) {
3543 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
3544 0 : return false;
3545 : }
3546 : }
3547 :
3548 157 : JSObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length, proto);
3549 157 : if (!obj)
3550 0 : return false;
3551 :
3552 157 : args.rval().setObject(*obj);
3553 157 : return true;
3554 : }
3555 :
3556 : /* ES5 15.4.2 */
3557 : bool
3558 24 : js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp)
3559 : {
3560 24 : CallArgs args = CallArgsFromVp(argc, vp);
3561 24 : return ArrayConstructorImpl(cx, args, /* isConstructor = */ true);
3562 : }
3563 :
3564 : bool
3565 157 : js::array_construct(JSContext* cx, unsigned argc, Value* vp)
3566 : {
3567 157 : CallArgs args = CallArgsFromVp(argc, vp);
3568 157 : MOZ_ASSERT(!args.isConstructing());
3569 157 : MOZ_ASSERT(args.length() == 1);
3570 157 : MOZ_ASSERT(args[0].isNumber());
3571 157 : return ArrayConstructorImpl(cx, args, /* isConstructor = */ false);
3572 : }
3573 :
3574 : JSObject*
3575 0 : js::ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengthInt)
3576 : {
3577 0 : if (lengthInt < 0) {
3578 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
3579 0 : return nullptr;
3580 : }
3581 :
3582 0 : uint32_t length = uint32_t(lengthInt);
3583 0 : return NewPartlyAllocatedArrayTryUseGroup(cx, group, length);
3584 : }
3585 :
3586 : static JSObject*
3587 291 : CreateArrayPrototype(JSContext* cx, JSProtoKey key)
3588 : {
3589 291 : MOZ_ASSERT(key == JSProto_Array);
3590 582 : RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, cx->global()));
3591 291 : if (!proto)
3592 0 : return nullptr;
3593 :
3594 582 : RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &ArrayObject::class_,
3595 1164 : TaggedProto(proto)));
3596 291 : if (!group)
3597 0 : return nullptr;
3598 :
3599 582 : RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayObject::class_, TaggedProto(proto),
3600 582 : gc::AllocKind::OBJECT0));
3601 291 : if (!shape)
3602 0 : return nullptr;
3603 :
3604 582 : AutoSetNewObjectMetadata metadata(cx);
3605 582 : RootedArrayObject arrayProto(cx, ArrayObject::createArray(cx, gc::AllocKind::OBJECT4,
3606 : gc::TenuredHeap, shape, group, 0,
3607 582 : metadata));
3608 1455 : if (!arrayProto ||
3609 1746 : !JSObject::setSingleton(cx, arrayProto) ||
3610 2037 : !JSObject::setDelegate(cx, arrayProto) ||
3611 873 : !AddLengthProperty(cx, arrayProto))
3612 : {
3613 0 : return nullptr;
3614 : }
3615 :
3616 : /*
3617 : * The default 'new' group of Array.prototype is required by type inference
3618 : * to have unknown properties, to simplify handling of e.g. heterogenous
3619 : * arrays in JSON and script literals and allows setDenseArrayElement to
3620 : * be used without updating the indexed type set for such default arrays.
3621 : */
3622 291 : if (!JSObject::setNewGroupUnknown(cx, &ArrayObject::class_, arrayProto))
3623 0 : return nullptr;
3624 :
3625 291 : return arrayProto;
3626 : }
3627 :
3628 : static bool
3629 288 : array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto)
3630 : {
3631 : // Add Array.prototype[@@unscopables]. ECMA-262 draft (2016 Mar 19) 22.1.3.32.
3632 576 : RootedObject unscopables(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr, TenuredObject));
3633 288 : if (!unscopables)
3634 0 : return false;
3635 :
3636 576 : RootedValue value(cx, BooleanValue(true));
3637 2304 : if (!DefineProperty(cx, unscopables, cx->names().copyWithin, value) ||
3638 2016 : !DefineProperty(cx, unscopables, cx->names().entries, value) ||
3639 2016 : !DefineProperty(cx, unscopables, cx->names().fill, value) ||
3640 2016 : !DefineProperty(cx, unscopables, cx->names().find, value) ||
3641 2016 : !DefineProperty(cx, unscopables, cx->names().findIndex, value) ||
3642 2016 : !DefineProperty(cx, unscopables, cx->names().includes, value) ||
3643 3168 : !DefineProperty(cx, unscopables, cx->names().keys, value) ||
3644 1152 : !DefineProperty(cx, unscopables, cx->names().values, value))
3645 : {
3646 0 : return false;
3647 : }
3648 :
3649 576 : RootedId id(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().get(JS::SymbolCode::unscopables)));
3650 288 : value.setObject(*unscopables);
3651 288 : return DefineProperty(cx, proto, id, value, nullptr, nullptr, JSPROP_READONLY);
3652 : }
3653 :
3654 : static const ClassOps ArrayObjectClassOps = {
3655 : array_addProperty,
3656 : nullptr, /* delProperty */
3657 : nullptr, /* getProperty */
3658 : nullptr, /* setProperty */
3659 : nullptr, /* enumerate */
3660 : nullptr, /* resolve */
3661 : nullptr, /* mayResolve */
3662 : nullptr, /* finalize */
3663 : nullptr, /* call */
3664 : nullptr, /* hasInstance */
3665 : nullptr, /* construct */
3666 : nullptr, /* trace */
3667 : };
3668 :
3669 : static const ClassSpec ArrayObjectClassSpec = {
3670 : GenericCreateConstructor<ArrayConstructor, 1, AllocKind::FUNCTION, &jit::JitInfo_Array>,
3671 : CreateArrayPrototype,
3672 : array_static_methods,
3673 : array_static_props,
3674 : array_methods,
3675 : nullptr,
3676 : array_proto_finish
3677 : };
3678 :
3679 : const Class ArrayObject::class_ = {
3680 : "Array",
3681 : JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_DELAY_METADATA_BUILDER,
3682 : &ArrayObjectClassOps,
3683 : &ArrayObjectClassSpec
3684 : };
3685 :
3686 : /*
3687 : * Array allocation functions.
3688 : */
3689 :
3690 : static inline bool
3691 5791 : EnsureNewArrayElements(JSContext* cx, ArrayObject* obj, uint32_t length)
3692 : {
3693 : /*
3694 : * If ensureElements creates dynamically allocated slots, then having
3695 : * fixedSlots is a waste.
3696 : */
3697 11582 : DebugOnly<uint32_t> cap = obj->getDenseCapacity();
3698 :
3699 5791 : if (!obj->ensureElements(cx, length))
3700 0 : return false;
3701 :
3702 5791 : MOZ_ASSERT_IF(cap, !obj->hasDynamicElements());
3703 :
3704 5791 : return true;
3705 : }
3706 :
3707 : template <uint32_t maxLength>
3708 : static MOZ_ALWAYS_INLINE ArrayObject*
3709 5757 : NewArray(JSContext* cx, uint32_t length,
3710 : HandleObject protoArg, NewObjectKind newKind = GenericObject)
3711 : {
3712 5757 : gc::AllocKind allocKind = GuessArrayGCKind(length);
3713 5757 : MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_));
3714 5757 : allocKind = GetBackgroundAllocKind(allocKind);
3715 :
3716 11514 : RootedObject proto(cx, protoArg);
3717 5757 : if (!proto && !GetBuiltinPrototype(cx, JSProto_Array, &proto))
3718 0 : return nullptr;
3719 :
3720 11514 : Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto));
3721 5757 : bool isCachable = NewObjectWithTaggedProtoIsCachable(cx, taggedProto, newKind, &ArrayObject::class_);
3722 5757 : if (isCachable) {
3723 4166 : NewObjectCache& cache = cx->caches().newObjectCache;
3724 4166 : NewObjectCache::EntryIndex entry = -1;
3725 4166 : if (cache.lookupProto(&ArrayObject::class_, proto, allocKind, &entry)) {
3726 3277 : gc::InitialHeap heap = GetInitialHeap(newKind, &ArrayObject::class_);
3727 3277 : AutoSetNewObjectMetadata metadata(cx);
3728 3277 : JSObject* obj = cache.newObjectFromHit(cx, entry, heap);
3729 3277 : if (obj) {
3730 : /* Fixup the elements pointer and length, which may be incorrect. */
3731 3277 : ArrayObject* arr = &obj->as<ArrayObject>();
3732 3277 : arr->setFixedElements();
3733 3277 : arr->setLength(cx, length);
3734 6544 : if (maxLength > 0 &&
3735 6534 : !EnsureNewArrayElements(cx, arr, std::min(maxLength, length)))
3736 : {
3737 0 : return nullptr;
3738 : }
3739 3277 : return arr;
3740 : }
3741 : }
3742 : }
3743 :
3744 4960 : RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &ArrayObject::class_,
3745 7440 : TaggedProto(proto)));
3746 2480 : if (!group)
3747 0 : return nullptr;
3748 :
3749 : /*
3750 : * Get a shape with zero fixed slots, regardless of the size class.
3751 : * See JSObject::createArray.
3752 : */
3753 4960 : RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayObject::class_,
3754 2480 : TaggedProto(proto),
3755 4960 : gc::AllocKind::OBJECT0));
3756 2480 : if (!shape)
3757 0 : return nullptr;
3758 :
3759 4960 : AutoSetNewObjectMetadata metadata(cx);
3760 4960 : RootedArrayObject arr(cx, ArrayObject::createArray(cx, allocKind,
3761 : GetInitialHeap(newKind, &ArrayObject::class_),
3762 4960 : shape, group, length, metadata));
3763 2480 : if (!arr)
3764 0 : return nullptr;
3765 :
3766 2480 : if (shape->isEmptyShape()) {
3767 25 : if (!AddLengthProperty(cx, arr))
3768 0 : return nullptr;
3769 25 : shape = arr->lastProperty();
3770 25 : EmptyShape::insertInitialShape(cx, shape, proto);
3771 : }
3772 :
3773 2480 : if (newKind == SingletonObject && !JSObject::setSingleton(cx, arr))
3774 0 : return nullptr;
3775 :
3776 2480 : if (isCachable) {
3777 889 : NewObjectCache& cache = cx->caches().newObjectCache;
3778 889 : NewObjectCache::EntryIndex entry = -1;
3779 889 : cache.lookupProto(&ArrayObject::class_, proto, allocKind, &entry);
3780 889 : cache.fillProto(entry, &ArrayObject::class_, taggedProto, allocKind, arr);
3781 : }
3782 :
3783 2480 : if (maxLength > 0 && !EnsureNewArrayElements(cx, arr, std::min(maxLength, length)))
3784 0 : return nullptr;
3785 :
3786 2480 : probes::CreateObject(cx, arr);
3787 2480 : return arr;
3788 : }
3789 :
3790 : ArrayObject * JS_FASTCALL
3791 18 : js::NewDenseEmptyArray(JSContext* cx, HandleObject proto /* = nullptr */,
3792 : NewObjectKind newKind /* = GenericObject */)
3793 : {
3794 18 : return NewArray<0>(cx, 0, proto, newKind);
3795 : }
3796 :
3797 : ArrayObject * JS_FASTCALL
3798 2314 : js::NewDenseFullyAllocatedArray(JSContext* cx, uint32_t length,
3799 : HandleObject proto /* = nullptr */,
3800 : NewObjectKind newKind /* = GenericObject */)
3801 : {
3802 2314 : return NewArray<UINT32_MAX>(cx, length, proto, newKind);
3803 : }
3804 :
3805 : ArrayObject * JS_FASTCALL
3806 0 : js::NewDensePartlyAllocatedArray(JSContext* cx, uint32_t length,
3807 : HandleObject proto /* = nullptr */,
3808 : NewObjectKind newKind /* = GenericObject */)
3809 : {
3810 0 : return NewArray<ArrayObject::EagerAllocationMaxLength>(cx, length, proto, newKind);
3811 : }
3812 :
3813 : ArrayObject * JS_FASTCALL
3814 6 : js::NewDenseUnallocatedArray(JSContext* cx, uint32_t length,
3815 : HandleObject proto /* = nullptr */,
3816 : NewObjectKind newKind /* = GenericObject */)
3817 : {
3818 6 : return NewArray<0>(cx, length, proto, newKind);
3819 : }
3820 :
3821 : // values must point at already-rooted Value objects
3822 : ArrayObject*
3823 2223 : js::NewDenseCopiedArray(JSContext* cx, uint32_t length, const Value* values,
3824 : HandleObject proto /* = nullptr */,
3825 : NewObjectKind newKind /* = GenericObject */)
3826 : {
3827 2223 : ArrayObject* arr = NewArray<UINT32_MAX>(cx, length, proto, newKind);
3828 2223 : if (!arr)
3829 0 : return nullptr;
3830 :
3831 2223 : MOZ_ASSERT(arr->getDenseCapacity() >= length);
3832 :
3833 2223 : arr->setDenseInitializedLength(values ? length : 0);
3834 :
3835 2223 : if (values)
3836 2223 : arr->initDenseElements(0, values, length);
3837 :
3838 2223 : return arr;
3839 : }
3840 :
3841 : ArrayObject*
3842 58 : js::NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSObject* templateObject)
3843 : {
3844 116 : AutoSetNewObjectMetadata metadata(cx);
3845 58 : gc::AllocKind allocKind = GuessArrayGCKind(length);
3846 58 : MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_));
3847 58 : allocKind = GetBackgroundAllocKind(allocKind);
3848 :
3849 116 : RootedObjectGroup group(cx, templateObject->group());
3850 116 : RootedShape shape(cx, templateObject->as<ArrayObject>().lastProperty());
3851 :
3852 58 : gc::InitialHeap heap = GetInitialHeap(GenericObject, &ArrayObject::class_);
3853 116 : Rooted<ArrayObject*> arr(cx, ArrayObject::createArray(cx, allocKind,
3854 116 : heap, shape, group, length, metadata));
3855 58 : if (!arr)
3856 0 : return nullptr;
3857 :
3858 58 : if (!EnsureNewArrayElements(cx, arr, length))
3859 0 : return nullptr;
3860 :
3861 58 : probes::CreateObject(cx, arr);
3862 :
3863 58 : return arr;
3864 : }
3865 :
3866 : JSObject*
3867 839 : js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap)
3868 : {
3869 839 : MOZ_ASSERT(!gc::IsInsideNursery(templateObject));
3870 :
3871 839 : ArrayObject* arr = ArrayObject::createCopyOnWriteArray(cx, heap, templateObject);
3872 839 : if (!arr)
3873 0 : return nullptr;
3874 :
3875 839 : probes::CreateObject(cx, arr);
3876 839 : return arr;
3877 : }
3878 :
3879 : // Return a new boxed or unboxed array with the specified length and allocated
3880 : // capacity (up to maxLength), using the specified group if possible. If the
3881 : // specified group cannot be used, ensure that the created array at least has
3882 : // the given [[Prototype]].
3883 : template <uint32_t maxLength>
3884 : static inline JSObject*
3885 1163 : NewArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length,
3886 : NewObjectKind newKind = GenericObject)
3887 : {
3888 1163 : MOZ_ASSERT(newKind != SingletonObject);
3889 :
3890 1163 : if (group->maybePreliminaryObjects())
3891 48 : group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
3892 :
3893 1163 : if (group->shouldPreTenure() || group->maybePreliminaryObjects())
3894 48 : newKind = TenuredObject;
3895 :
3896 2326 : RootedObject proto(cx, group->proto().toObject());
3897 1163 : if (group->maybeUnboxedLayout()) {
3898 0 : if (length > UnboxedArrayObject::MaximumCapacity)
3899 0 : return NewArray<maxLength>(cx, length, proto, newKind);
3900 0 : return UnboxedArrayObject::create(cx, group, length, newKind, maxLength);
3901 : }
3902 :
3903 1163 : ArrayObject* res = NewArray<maxLength>(cx, length, proto, newKind);
3904 1163 : if (!res)
3905 0 : return nullptr;
3906 :
3907 1163 : res->setGroup(group);
3908 :
3909 : // If the length calculation overflowed, make sure that is marked for the
3910 : // new group.
3911 1163 : if (res->length() > INT32_MAX)
3912 0 : res->setLength(cx, res->length());
3913 :
3914 1163 : if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects())
3915 48 : preliminaryObjects->registerNewObject(res);
3916 :
3917 1163 : return res;
3918 : }
3919 :
3920 : JSObject*
3921 990 : js::NewFullyAllocatedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length,
3922 : NewObjectKind newKind)
3923 : {
3924 990 : return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length, newKind);
3925 : }
3926 :
3927 : JSObject*
3928 0 : js::NewPartlyAllocatedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length)
3929 : {
3930 0 : return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length);
3931 : }
3932 :
3933 : // Return a new array with the default prototype and specified allocated
3934 : // capacity and length. If possible, try to reuse the group of the input
3935 : // object. The resulting array will either reuse the input object's group or
3936 : // will have unknown property types. Additionally, the result will have the
3937 : // same boxed/unboxed elements representation as the input object, unless
3938 : // |length| is larger than the input object's initialized length (in which case
3939 : // UnboxedArrayObject::MaximumCapacity might be exceeded).
3940 : template <uint32_t maxLength>
3941 : static inline JSObject*
3942 42 : NewArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length,
3943 : NewObjectKind newKind = GenericObject)
3944 : {
3945 42 : if (!IsBoxedOrUnboxedArray(obj))
3946 33 : return NewArray<maxLength>(cx, length, nullptr, newKind);
3947 :
3948 9 : if (obj->staticPrototype() != cx->global()->maybeGetArrayPrototype())
3949 0 : return NewArray<maxLength>(cx, length, nullptr, newKind);
3950 :
3951 18 : RootedObjectGroup group(cx, JSObject::getGroup(cx, obj));
3952 9 : if (!group)
3953 0 : return nullptr;
3954 :
3955 9 : return NewArrayTryUseGroup<maxLength>(cx, group, length, newKind);
3956 : }
3957 :
3958 : JSObject*
3959 10 : js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length,
3960 : NewObjectKind newKind)
3961 : {
3962 10 : return NewArrayTryReuseGroup<UINT32_MAX>(cx, obj, length, newKind);
3963 : }
3964 :
3965 : JSObject*
3966 32 : js::NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length)
3967 : {
3968 32 : return NewArrayTryReuseGroup<ArrayObject::EagerAllocationMaxLength>(cx, obj, length);
3969 : }
3970 :
3971 : JSObject*
3972 7 : js::NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length,
3973 : NewObjectKind newKind)
3974 : {
3975 14 : RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
3976 7 : if (!group)
3977 0 : return nullptr;
3978 7 : return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length, newKind);
3979 : }
3980 :
3981 : JSObject*
3982 157 : js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, HandleObject proto)
3983 : {
3984 314 : RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array, proto));
3985 157 : if (!group)
3986 0 : return nullptr;
3987 157 : return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length);
3988 : }
3989 :
3990 : bool
3991 1859 : js::MaybeAnalyzeBeforeCreatingLargeArray(JSContext* cx, HandleObjectGroup group,
3992 : const Value* vp, size_t length)
3993 : {
3994 : static const size_t EagerPreliminaryObjectAnalysisThreshold = 800;
3995 :
3996 : // Force analysis to see if an unboxed array can be used when making a
3997 : // sufficiently large array, to avoid excessive analysis and copying later
3998 : // on. If this is the first array of its group that is being created, first
3999 : // make a dummy array with the initial elements of the array we are about
4000 : // to make, so there is some basis for the unboxed array analysis.
4001 1859 : if (length > EagerPreliminaryObjectAnalysisThreshold) {
4002 0 : if (PreliminaryObjectArrayWithTemplate* objects = group->maybePreliminaryObjects()) {
4003 0 : if (objects->empty()) {
4004 0 : size_t nlength = Min<size_t>(length, 100);
4005 0 : JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, nlength);
4006 0 : if (!obj)
4007 0 : return false;
4008 : DebugOnly<DenseElementResult> result =
4009 0 : SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, nlength,
4010 0 : ShouldUpdateTypes::Update);
4011 0 : MOZ_ASSERT(result.value == DenseElementResult::Success);
4012 : }
4013 0 : objects->maybeAnalyze(cx, group, /* force = */ true);
4014 : }
4015 : }
4016 1859 : return true;
4017 : }
4018 :
4019 : JSObject*
4020 990 : js::NewCopiedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group,
4021 : const Value* vp, size_t length, NewObjectKind newKind,
4022 : ShouldUpdateTypes updateTypes)
4023 : {
4024 990 : if (!MaybeAnalyzeBeforeCreatingLargeArray(cx, group, vp, length))
4025 0 : return nullptr;
4026 :
4027 990 : JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, length, newKind);
4028 990 : if (!obj)
4029 0 : return nullptr;
4030 :
4031 : DenseElementResult result =
4032 990 : SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, length, updateTypes);
4033 990 : if (result == DenseElementResult::Failure)
4034 0 : return nullptr;
4035 990 : if (result == DenseElementResult::Success)
4036 990 : return obj;
4037 :
4038 0 : MOZ_ASSERT(obj->is<UnboxedArrayObject>());
4039 0 : if (!UnboxedArrayObject::convertToNative(cx, obj))
4040 0 : return nullptr;
4041 :
4042 0 : result = SetOrExtendBoxedOrUnboxedDenseElements<JSVAL_TYPE_MAGIC>(cx, obj, 0, vp, length,
4043 0 : updateTypes);
4044 0 : MOZ_ASSERT(result != DenseElementResult::Incomplete);
4045 0 : if (result == DenseElementResult::Failure)
4046 0 : return nullptr;
4047 :
4048 0 : return obj;
4049 : }
4050 :
4051 : JSObject*
4052 24 : js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length,
4053 : HandleObject proto /* = nullptr */)
4054 : {
4055 48 : RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array, proto));
4056 24 : if (!group)
4057 0 : return nullptr;
4058 24 : return NewCopiedArrayTryUseGroup(cx, group, vp, length);
4059 : }
4060 :
4061 : bool
4062 41 : js::NewValuePair(JSContext* cx, const Value& val1, const Value& val2, MutableHandleValue rval)
4063 : {
4064 82 : JS::AutoValueArray<2> vec(cx);
4065 41 : vec[0].set(val1);
4066 41 : vec[1].set(val2);
4067 :
4068 41 : JSObject* aobj = js::NewDenseCopiedArray(cx, 2, vec.begin());
4069 41 : if (!aobj)
4070 0 : return false;
4071 41 : rval.setObject(*aobj);
4072 41 : return true;
4073 : }
4074 :
4075 : #ifdef DEBUG
4076 : bool
4077 0 : js::ArrayInfo(JSContext* cx, unsigned argc, Value* vp)
4078 : {
4079 0 : CallArgs args = CallArgsFromVp(argc, vp);
4080 : JSObject* obj;
4081 :
4082 0 : for (unsigned i = 0; i < args.length(); i++) {
4083 0 : RootedValue arg(cx, args[i]);
4084 :
4085 0 : UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, arg, nullptr);
4086 0 : if (!bytes)
4087 0 : return false;
4088 0 : if (arg.isPrimitive() ||
4089 0 : !(obj = arg.toObjectOrNull())->is<ArrayObject>()) {
4090 0 : fprintf(stderr, "%s: not array\n", bytes.get());
4091 0 : continue;
4092 : }
4093 0 : fprintf(stderr, "%s: (len %u", bytes.get(), obj->as<ArrayObject>().length());
4094 0 : fprintf(stderr, ", capacity %u", obj->as<ArrayObject>().getDenseCapacity());
4095 0 : fputs(")\n", stderr);
4096 : }
4097 :
4098 0 : args.rval().setUndefined();
4099 0 : return true;
4100 : }
4101 : #endif
|