michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* ES5 15.4.4.14. */ michael@0: function ArrayIndexOf(searchElement/*, fromIndex*/) { michael@0: /* Step 1. */ michael@0: var O = ToObject(this); michael@0: michael@0: /* Steps 2-3. */ michael@0: var len = TO_UINT32(O.length); michael@0: michael@0: /* Step 4. */ michael@0: if (len === 0) michael@0: return -1; michael@0: michael@0: /* Step 5. */ michael@0: var n = arguments.length > 1 ? ToInteger(arguments[1]) : 0; michael@0: michael@0: /* Step 6. */ michael@0: if (n >= len) michael@0: return -1; michael@0: michael@0: var k; michael@0: /* Step 7. */ michael@0: if (n >= 0) michael@0: k = n; michael@0: /* Step 8. */ michael@0: else { michael@0: /* Step a. */ michael@0: k = len + n; michael@0: /* Step b. */ michael@0: if (k < 0) michael@0: k = 0; michael@0: } michael@0: michael@0: /* Step 9. */ michael@0: if (IsPackedArray(O)) { michael@0: for (; k < len; k++) { michael@0: if (O[k] === searchElement) michael@0: return k; michael@0: } michael@0: } else { michael@0: for (; k < len; k++) { michael@0: if (k in O && O[k] === searchElement) michael@0: return k; michael@0: } michael@0: } michael@0: michael@0: /* Step 10. */ michael@0: return -1; michael@0: } michael@0: michael@0: function ArrayStaticIndexOf(list, searchElement/*, fromIndex*/) { michael@0: if (arguments.length < 1) michael@0: ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.indexOf'); michael@0: var fromIndex = arguments.length > 2 ? arguments[2] : 0; michael@0: return callFunction(ArrayIndexOf, list, searchElement, fromIndex); michael@0: } michael@0: michael@0: /* ES5 15.4.4.15. */ michael@0: function ArrayLastIndexOf(searchElement/*, fromIndex*/) { michael@0: /* Step 1. */ michael@0: var O = ToObject(this); michael@0: michael@0: /* Steps 2-3. */ michael@0: var len = TO_UINT32(O.length); michael@0: michael@0: /* Step 4. */ michael@0: if (len === 0) michael@0: return -1; michael@0: michael@0: /* Step 5. */ michael@0: var n = arguments.length > 1 ? ToInteger(arguments[1]) : len - 1; michael@0: michael@0: /* Steps 6-7. */ michael@0: var k; michael@0: if (n > len - 1) michael@0: k = len - 1; michael@0: else if (n < 0) michael@0: k = len + n; michael@0: else michael@0: k = n; michael@0: michael@0: /* Step 8. */ michael@0: if (IsPackedArray(O)) { michael@0: for (; k >= 0; k--) { michael@0: if (O[k] === searchElement) michael@0: return k; michael@0: } michael@0: } else { michael@0: for (; k >= 0; k--) { michael@0: if (k in O && O[k] === searchElement) michael@0: return k; michael@0: } michael@0: } michael@0: michael@0: /* Step 9. */ michael@0: return -1; michael@0: } michael@0: michael@0: function ArrayStaticLastIndexOf(list, searchElement/*, fromIndex*/) { michael@0: if (arguments.length < 1) michael@0: ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.lastIndexOf'); michael@0: var fromIndex; michael@0: if (arguments.length > 2) { michael@0: fromIndex = arguments[2]; michael@0: } else { michael@0: var O = ToObject(list); michael@0: var len = TO_UINT32(O.length); michael@0: fromIndex = len - 1; michael@0: } michael@0: return callFunction(ArrayLastIndexOf, list, searchElement, fromIndex); michael@0: } michael@0: michael@0: /* ES5 15.4.4.16. */ michael@0: function ArrayEvery(callbackfn/*, thisArg*/) { michael@0: /* Step 1. */ michael@0: var O = ToObject(this); michael@0: michael@0: /* Steps 2-3. */ michael@0: var len = TO_UINT32(O.length); michael@0: michael@0: /* Step 4. */ michael@0: if (arguments.length === 0) michael@0: ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.every'); michael@0: if (!IsCallable(callbackfn)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); michael@0: michael@0: /* Step 5. */ michael@0: var T = arguments.length > 1 ? arguments[1] : void 0; michael@0: michael@0: /* Steps 6-7. */ michael@0: /* Steps a (implicit), and d. */ michael@0: for (var k = 0; k < len; k++) { michael@0: /* Step b */ michael@0: if (k in O) { michael@0: /* Step c. */ michael@0: if (!callFunction(callbackfn, T, O[k], k, O)) michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: /* Step 8. */ michael@0: return true; michael@0: } michael@0: michael@0: function ArrayStaticEvery(list, callbackfn/*, thisArg*/) { michael@0: if (arguments.length < 2) michael@0: ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.every'); michael@0: if (!IsCallable(callbackfn)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); michael@0: var T = arguments.length > 2 ? arguments[2] : void 0; michael@0: return callFunction(ArrayEvery, list, callbackfn, T); michael@0: } michael@0: michael@0: /* ES5 15.4.4.17. */ michael@0: function ArraySome(callbackfn/*, thisArg*/) { michael@0: /* Step 1. */ michael@0: var O = ToObject(this); michael@0: michael@0: /* Steps 2-3. */ michael@0: var len = TO_UINT32(O.length); michael@0: michael@0: /* Step 4. */ michael@0: if (arguments.length === 0) michael@0: ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.some'); michael@0: if (!IsCallable(callbackfn)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); michael@0: michael@0: /* Step 5. */ michael@0: var T = arguments.length > 1 ? arguments[1] : void 0; michael@0: michael@0: /* Steps 6-7. */ michael@0: /* Steps a (implicit), and d. */ michael@0: for (var k = 0; k < len; k++) { michael@0: /* Step b */ michael@0: if (k in O) { michael@0: /* Step c. */ michael@0: if (callFunction(callbackfn, T, O[k], k, O)) michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: /* Step 8. */ michael@0: return false; michael@0: } michael@0: michael@0: function ArrayStaticSome(list, callbackfn/*, thisArg*/) { michael@0: if (arguments.length < 2) michael@0: ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.some'); michael@0: if (!IsCallable(callbackfn)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); michael@0: var T = arguments.length > 2 ? arguments[2] : void 0; michael@0: return callFunction(ArraySome, list, callbackfn, T); michael@0: } michael@0: michael@0: /* ES5 15.4.4.18. */ michael@0: function ArrayForEach(callbackfn/*, thisArg*/) { michael@0: /* Step 1. */ michael@0: var O = ToObject(this); michael@0: michael@0: /* Steps 2-3. */ michael@0: var len = TO_UINT32(O.length); michael@0: michael@0: /* Step 4. */ michael@0: if (arguments.length === 0) michael@0: ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.forEach'); michael@0: if (!IsCallable(callbackfn)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); michael@0: michael@0: /* Step 5. */ michael@0: var T = arguments.length > 1 ? arguments[1] : void 0; michael@0: michael@0: /* Steps 6-7. */ michael@0: /* Steps a (implicit), and d. */ michael@0: for (var k = 0; k < len; k++) { michael@0: /* Step b */ michael@0: if (k in O) { michael@0: /* Step c. */ michael@0: callFunction(callbackfn, T, O[k], k, O); michael@0: } michael@0: } michael@0: michael@0: /* Step 8. */ michael@0: return void 0; michael@0: } michael@0: michael@0: /* ES5 15.4.4.19. */ michael@0: function ArrayMap(callbackfn/*, thisArg*/) { michael@0: /* Step 1. */ michael@0: var O = ToObject(this); michael@0: michael@0: /* Step 2-3. */ michael@0: var len = TO_UINT32(O.length); michael@0: michael@0: /* Step 4. */ michael@0: if (arguments.length === 0) michael@0: ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.map'); michael@0: if (!IsCallable(callbackfn)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); michael@0: michael@0: /* Step 5. */ michael@0: var T = arguments.length > 1 ? arguments[1] : void 0; michael@0: michael@0: /* Step 6. */ michael@0: var A = NewDenseArray(len); michael@0: michael@0: /* Step 7-8. */ michael@0: /* Step a (implicit), and d. */ michael@0: for (var k = 0; k < len; k++) { michael@0: /* Step b */ michael@0: if (k in O) { michael@0: /* Step c.i-iii. */ michael@0: var mappedValue = callFunction(callbackfn, T, O[k], k, O); michael@0: // UnsafePutElements doesn't invoke setters, so we can use it here. michael@0: UnsafePutElements(A, k, mappedValue); michael@0: } michael@0: } michael@0: michael@0: /* Step 9. */ michael@0: return A; michael@0: } michael@0: michael@0: function ArrayStaticMap(list, callbackfn/*, thisArg*/) { michael@0: if (arguments.length < 2) michael@0: ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.map'); michael@0: if (!IsCallable(callbackfn)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); michael@0: var T = arguments.length > 2 ? arguments[2] : void 0; michael@0: return callFunction(ArrayMap, list, callbackfn, T); michael@0: } michael@0: michael@0: function ArrayStaticForEach(list, callbackfn/*, thisArg*/) { michael@0: if (arguments.length < 2) michael@0: ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.forEach'); michael@0: if (!IsCallable(callbackfn)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); michael@0: var T = arguments.length > 2 ? arguments[2] : void 0; michael@0: callFunction(ArrayForEach, list, callbackfn, T); michael@0: } michael@0: michael@0: /* ES5 15.4.4.21. */ michael@0: function ArrayReduce(callbackfn/*, initialValue*/) { michael@0: /* Step 1. */ michael@0: var O = ToObject(this); michael@0: michael@0: /* Steps 2-3. */ michael@0: var len = TO_UINT32(O.length); michael@0: michael@0: /* Step 4. */ michael@0: if (arguments.length === 0) michael@0: ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.reduce'); michael@0: if (!IsCallable(callbackfn)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); michael@0: michael@0: /* Step 6. */ michael@0: var k = 0; michael@0: michael@0: /* Steps 5, 7-8. */ michael@0: var accumulator; michael@0: if (arguments.length > 1) { michael@0: accumulator = arguments[1]; michael@0: } else { michael@0: /* Step 5. */ michael@0: if (len === 0) michael@0: ThrowError(JSMSG_EMPTY_ARRAY_REDUCE); michael@0: if (IsPackedArray(O)) { michael@0: accumulator = O[k++]; michael@0: } else { michael@0: var kPresent = false; michael@0: for (; k < len; k++) { michael@0: if (k in O) { michael@0: accumulator = O[k]; michael@0: kPresent = true; michael@0: k++; michael@0: break; michael@0: } michael@0: } michael@0: if (!kPresent) michael@0: ThrowError(JSMSG_EMPTY_ARRAY_REDUCE); michael@0: } michael@0: } michael@0: michael@0: /* Step 9. */ michael@0: /* Steps a (implicit), and d. */ michael@0: for (; k < len; k++) { michael@0: /* Step b */ michael@0: if (k in O) { michael@0: /* Step c. */ michael@0: accumulator = callbackfn(accumulator, O[k], k, O); michael@0: } michael@0: } michael@0: michael@0: /* Step 10. */ michael@0: return accumulator; michael@0: } michael@0: michael@0: function ArrayStaticReduce(list, callbackfn) { michael@0: if (arguments.length < 2) michael@0: ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.reduce'); michael@0: if (!IsCallable(callbackfn)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); michael@0: if (arguments.length > 2) michael@0: return callFunction(ArrayReduce, list, callbackfn, arguments[2]); michael@0: else michael@0: return callFunction(ArrayReduce, list, callbackfn); michael@0: } michael@0: michael@0: /* ES5 15.4.4.22. */ michael@0: function ArrayReduceRight(callbackfn/*, initialValue*/) { michael@0: /* Step 1. */ michael@0: var O = ToObject(this); michael@0: michael@0: /* Steps 2-3. */ michael@0: var len = TO_UINT32(O.length); michael@0: michael@0: /* Step 4. */ michael@0: if (arguments.length === 0) michael@0: ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.reduce'); michael@0: if (!IsCallable(callbackfn)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); michael@0: michael@0: /* Step 6. */ michael@0: var k = len - 1; michael@0: michael@0: /* Steps 5, 7-8. */ michael@0: var accumulator; michael@0: if (arguments.length > 1) { michael@0: accumulator = arguments[1]; michael@0: } else { michael@0: /* Step 5. */ michael@0: if (len === 0) michael@0: ThrowError(JSMSG_EMPTY_ARRAY_REDUCE); michael@0: if (IsPackedArray(O)) { michael@0: accumulator = O[k--]; michael@0: } else { michael@0: var kPresent = false; michael@0: for (; k >= 0; k--) { michael@0: if (k in O) { michael@0: accumulator = O[k]; michael@0: kPresent = true; michael@0: k--; michael@0: break; michael@0: } michael@0: } michael@0: if (!kPresent) michael@0: ThrowError(JSMSG_EMPTY_ARRAY_REDUCE); michael@0: } michael@0: } michael@0: michael@0: /* Step 9. */ michael@0: /* Steps a (implicit), and d. */ michael@0: for (; k >= 0; k--) { michael@0: /* Step b */ michael@0: if (k in O) { michael@0: /* Step c. */ michael@0: accumulator = callbackfn(accumulator, O[k], k, O); michael@0: } michael@0: } michael@0: michael@0: /* Step 10. */ michael@0: return accumulator; michael@0: } michael@0: michael@0: function ArrayStaticReduceRight(list, callbackfn) { michael@0: if (arguments.length < 2) michael@0: ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.reduceRight'); michael@0: if (!IsCallable(callbackfn)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); michael@0: if (arguments.length > 2) michael@0: return callFunction(ArrayReduceRight, list, callbackfn, arguments[2]); michael@0: else michael@0: return callFunction(ArrayReduceRight, list, callbackfn); michael@0: } michael@0: michael@0: /* ES6 draft 2013-05-14 15.4.3.23. */ michael@0: function ArrayFind(predicate/*, thisArg*/) { michael@0: /* Steps 1-2. */ michael@0: var O = ToObject(this); michael@0: michael@0: /* Steps 3-5. */ michael@0: var len = ToInteger(O.length); michael@0: michael@0: /* Step 6. */ michael@0: if (arguments.length === 0) michael@0: ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.find'); michael@0: if (!IsCallable(predicate)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, predicate)); michael@0: michael@0: /* Step 7. */ michael@0: var T = arguments.length > 1 ? arguments[1] : undefined; michael@0: michael@0: /* Steps 8-9. */ michael@0: /* Steps a (implicit), and e. */ michael@0: /* Note: this will hang in some corner-case situations, because of IEEE-754 numbers' michael@0: * imprecision for large values. Example: michael@0: * var obj = { 18014398509481984: true, length: 18014398509481988 }; michael@0: * Array.prototype.find.call(obj, () => true); michael@0: */ michael@0: for (var k = 0; k < len; k++) { michael@0: /* Steps b and c (implicit) */ michael@0: if (k in O) { michael@0: /* Step d. */ michael@0: var kValue = O[k]; michael@0: if (callFunction(predicate, T, kValue, k, O)) michael@0: return kValue; michael@0: } michael@0: } michael@0: michael@0: /* Step 10. */ michael@0: return undefined; michael@0: } michael@0: michael@0: /* ES6 draft 2013-05-14 15.4.3.23. */ michael@0: function ArrayFindIndex(predicate/*, thisArg*/) { michael@0: /* Steps 1-2. */ michael@0: var O = ToObject(this); michael@0: michael@0: /* Steps 3-5. */ michael@0: var len = ToInteger(O.length); michael@0: michael@0: /* Step 6. */ michael@0: if (arguments.length === 0) michael@0: ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.find'); michael@0: if (!IsCallable(predicate)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, predicate)); michael@0: michael@0: /* Step 7. */ michael@0: var T = arguments.length > 1 ? arguments[1] : undefined; michael@0: michael@0: /* Steps 8-9. */ michael@0: /* Steps a (implicit), and e. */ michael@0: /* Note: this will hang in some corner-case situations, because of IEEE-754 numbers' michael@0: * imprecision for large values. Example: michael@0: * var obj = { 18014398509481984: true, length: 18014398509481988 }; michael@0: * Array.prototype.find.call(obj, () => true); michael@0: */ michael@0: for (var k = 0; k < len; k++) { michael@0: /* Steps b and c (implicit) */ michael@0: if (k in O) { michael@0: /* Step d. */ michael@0: if (callFunction(predicate, T, O[k], k, O)) michael@0: return k; michael@0: } michael@0: } michael@0: michael@0: /* Step 10. */ michael@0: return -1; michael@0: } michael@0: michael@0: // ES6 draft 2014-04-05 22.1.3.6 michael@0: function ArrayFill(value, start = 0, end = undefined) { michael@0: // Steps 1-2. michael@0: var O = ToObject(this); michael@0: michael@0: // Steps 3-5. michael@0: // FIXME: Array operations should use ToLength (bug 924058). michael@0: var len = ToInteger(O.length); michael@0: michael@0: // Steps 6-7. michael@0: var relativeStart = ToInteger(start); michael@0: michael@0: // Step 8. michael@0: var k = relativeStart < 0 michael@0: ? std_Math_max(len + relativeStart, 0) michael@0: : std_Math_min(relativeStart, len); michael@0: michael@0: // Steps 9-10. michael@0: var relativeEnd = end === undefined ? len : ToInteger(end); michael@0: michael@0: // Step 11. michael@0: var final = relativeEnd < 0 michael@0: ? std_Math_max(len + relativeEnd, 0) michael@0: : std_Math_min(relativeEnd, len); michael@0: michael@0: // Step 12. michael@0: for (; k < final; k++) { michael@0: O[k] = value; michael@0: } michael@0: michael@0: // Step 13. michael@0: return O; michael@0: } michael@0: michael@0: #define ARRAY_ITERATOR_SLOT_ITERATED_OBJECT 0 michael@0: #define ARRAY_ITERATOR_SLOT_NEXT_INDEX 1 michael@0: #define ARRAY_ITERATOR_SLOT_ITEM_KIND 2 michael@0: michael@0: #define ITEM_KIND_VALUE 0 michael@0: #define ITEM_KIND_KEY_AND_VALUE 1 michael@0: #define ITEM_KIND_KEY 2 michael@0: michael@0: // ES6 draft specification, section 22.1.5.1, version 2013-09-05. michael@0: function CreateArrayIteratorAt(obj, kind, n) { michael@0: var iteratedObject = ToObject(obj); michael@0: var iterator = NewArrayIterator(); michael@0: UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_ITERATED_OBJECT, iteratedObject); michael@0: UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_NEXT_INDEX, n); michael@0: UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_ITEM_KIND, kind); michael@0: return iterator; michael@0: } michael@0: function CreateArrayIterator(obj, kind) { michael@0: return CreateArrayIteratorAt(obj, kind, 0); michael@0: } michael@0: michael@0: function ArrayIteratorIdentity() { michael@0: return this; michael@0: } michael@0: michael@0: function ArrayIteratorNext() { michael@0: // FIXME: ArrayIterator prototype should not pass this test. Bug 924059. michael@0: if (!IsObject(this) || !IsArrayIterator(this)) michael@0: ThrowError(JSMSG_INCOMPATIBLE_METHOD, "ArrayIterator", "next", ToString(this)); michael@0: michael@0: var a = UnsafeGetReservedSlot(this, ARRAY_ITERATOR_SLOT_ITERATED_OBJECT); michael@0: var index = UnsafeGetReservedSlot(this, ARRAY_ITERATOR_SLOT_NEXT_INDEX); michael@0: var itemKind = UnsafeGetReservedSlot(this, ARRAY_ITERATOR_SLOT_ITEM_KIND); michael@0: michael@0: // FIXME: This should be ToLength, which clamps at 2**53. Bug 924058. michael@0: if (index >= TO_UINT32(a.length)) { michael@0: // When the above is changed to ToLength, use +1/0 here instead michael@0: // of MAX_UINT32. michael@0: UnsafeSetReservedSlot(this, ARRAY_ITERATOR_SLOT_NEXT_INDEX, 0xffffffff); michael@0: return { value: undefined, done: true }; michael@0: } michael@0: michael@0: UnsafeSetReservedSlot(this, ARRAY_ITERATOR_SLOT_NEXT_INDEX, index + 1); michael@0: michael@0: if (itemKind === ITEM_KIND_VALUE) michael@0: return { value: a[index], done: false }; michael@0: michael@0: if (itemKind === ITEM_KIND_KEY_AND_VALUE) { michael@0: var pair = NewDenseArray(2); michael@0: pair[0] = index; michael@0: pair[1] = a[index]; michael@0: return { value: pair, done : false }; michael@0: } michael@0: michael@0: assert(itemKind === ITEM_KIND_KEY, itemKind); michael@0: return { value: index, done: false }; michael@0: } michael@0: michael@0: function ArrayValuesAt(n) { michael@0: return CreateArrayIteratorAt(this, ITEM_KIND_VALUE, n); michael@0: } michael@0: michael@0: function ArrayValues() { michael@0: return CreateArrayIterator(this, ITEM_KIND_VALUE); michael@0: } michael@0: michael@0: function ArrayEntries() { michael@0: return CreateArrayIterator(this, ITEM_KIND_KEY_AND_VALUE); michael@0: } michael@0: michael@0: function ArrayKeys() { michael@0: return CreateArrayIterator(this, ITEM_KIND_KEY); michael@0: } michael@0: michael@0: #ifdef ENABLE_PARALLEL_JS michael@0: michael@0: /* michael@0: * Strawman spec: michael@0: * http://wiki.ecmascript.org/doku.php?id=strawman:data_parallelism michael@0: */ michael@0: michael@0: /** michael@0: * Creates a new array by applying |func(e, i, self)| for each element |e| michael@0: * with index |i|. michael@0: */ michael@0: function ArrayMapPar(func, mode) { michael@0: if (!IsCallable(func)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, func)); michael@0: michael@0: var self = ToObject(this); michael@0: var length = self.length; michael@0: var buffer = NewDenseArray(length); michael@0: michael@0: parallel: for (;;) { michael@0: // Avoid parallel compilation if we are already nested in another michael@0: // parallel section or the user told us not to parallelize. The michael@0: // use of a for (;;) loop is working around some ion limitations: michael@0: // michael@0: // - Breaking out of named blocks does not currently work (bug 684384); michael@0: // - Unreachable Code Elim. can't properly handle if (a && b) (bug 669796) michael@0: if (ShouldForceSequential()) michael@0: break parallel; michael@0: if (!TRY_PARALLEL(mode)) michael@0: break parallel; michael@0: michael@0: var slicesInfo = ComputeSlicesInfo(length); michael@0: ForkJoin(mapThread, 0, slicesInfo.count, ForkJoinMode(mode)); michael@0: return buffer; michael@0: } michael@0: michael@0: // Sequential fallback: michael@0: ASSERT_SEQUENTIAL_IS_OK(mode); michael@0: for (var i = 0; i < length; i++) michael@0: UnsafePutElements(buffer, i, func(self[i], i, self)); michael@0: return buffer; michael@0: michael@0: function mapThread(workerId, sliceStart, sliceEnd) { michael@0: var sliceShift = slicesInfo.shift; michael@0: var sliceId; michael@0: while (GET_SLICE(sliceStart, sliceEnd, sliceId)) { michael@0: var indexStart = SLICE_START_INDEX(sliceShift, sliceId); michael@0: var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length); michael@0: for (var i = indexStart; i < indexEnd; i++) michael@0: UnsafePutElements(buffer, i, func(self[i], i, self)); michael@0: } michael@0: return sliceId; michael@0: } michael@0: michael@0: return undefined; michael@0: } michael@0: michael@0: /** michael@0: * Reduces the elements in an array in parallel. Order is not fixed and |func| michael@0: * is assumed to be associative. michael@0: */ michael@0: function ArrayReducePar(func, mode) { michael@0: if (!IsCallable(func)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, func)); michael@0: michael@0: var self = ToObject(this); michael@0: var length = self.length; michael@0: michael@0: if (length === 0) michael@0: ThrowError(JSMSG_EMPTY_ARRAY_REDUCE); michael@0: michael@0: parallel: for (;;) { // see ArrayMapPar() to explain why for(;;) etc michael@0: if (ShouldForceSequential()) michael@0: break parallel; michael@0: if (!TRY_PARALLEL(mode)) michael@0: break parallel; michael@0: michael@0: var slicesInfo = ComputeSlicesInfo(length); michael@0: var numSlices = slicesInfo.count; michael@0: var subreductions = NewDenseArray(numSlices); michael@0: michael@0: ForkJoin(reduceThread, 0, numSlices, ForkJoinMode(mode)); michael@0: michael@0: var accumulator = subreductions[0]; michael@0: for (var i = 1; i < numSlices; i++) michael@0: accumulator = func(accumulator, subreductions[i]); michael@0: return accumulator; michael@0: } michael@0: michael@0: // Sequential fallback: michael@0: ASSERT_SEQUENTIAL_IS_OK(mode); michael@0: var accumulator = self[0]; michael@0: for (var i = 1; i < length; i++) michael@0: accumulator = func(accumulator, self[i]); michael@0: return accumulator; michael@0: michael@0: function reduceThread(workerId, sliceStart, sliceEnd) { michael@0: var sliceShift = slicesInfo.shift; michael@0: var sliceId; michael@0: while (GET_SLICE(sliceStart, sliceEnd, sliceId)) { michael@0: var indexStart = SLICE_START_INDEX(sliceShift, sliceId); michael@0: var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length); michael@0: var accumulator = self[indexStart]; michael@0: for (var i = indexStart + 1; i < indexEnd; i++) michael@0: accumulator = func(accumulator, self[i]); michael@0: UnsafePutElements(subreductions, sliceId, accumulator); michael@0: } michael@0: return sliceId; michael@0: } michael@0: michael@0: return undefined; michael@0: } michael@0: michael@0: /** michael@0: * Returns an array [s_0, ..., s_N] where |s_i| is equal to the reduction (as michael@0: * per |reduce()|) of elements |0..i|. This is the generalization of partial michael@0: * sum. michael@0: */ michael@0: function ArrayScanPar(func, mode) { michael@0: if (!IsCallable(func)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, func)); michael@0: michael@0: var self = ToObject(this); michael@0: var length = self.length; michael@0: michael@0: if (length === 0) michael@0: ThrowError(JSMSG_EMPTY_ARRAY_REDUCE); michael@0: michael@0: var buffer = NewDenseArray(length); michael@0: michael@0: parallel: for (;;) { // see ArrayMapPar() to explain why for(;;) etc michael@0: if (ShouldForceSequential()) michael@0: break parallel; michael@0: if (!TRY_PARALLEL(mode)) michael@0: break parallel; michael@0: michael@0: var slicesInfo = ComputeSlicesInfo(length); michael@0: var numSlices = slicesInfo.count; michael@0: michael@0: // Scan slices individually (see comment on phase1()). michael@0: ForkJoin(phase1, 0, numSlices, ForkJoinMode(mode)); michael@0: michael@0: // Compute intermediates array (see comment on phase2()). michael@0: var intermediates = []; michael@0: var accumulator = buffer[finalElement(0)]; michael@0: ARRAY_PUSH(intermediates, accumulator); michael@0: for (var i = 1; i < numSlices - 1; i++) { michael@0: accumulator = func(accumulator, buffer[finalElement(i)]); michael@0: ARRAY_PUSH(intermediates, accumulator); michael@0: } michael@0: michael@0: // Complete each slice using intermediates array (see comment on phase2()). michael@0: // michael@0: // We start from slice 1 instead of 0 since there is no work to be done michael@0: // for slice 0. michael@0: if (numSlices > 1) michael@0: ForkJoin(phase2, 1, numSlices, ForkJoinMode(mode)); michael@0: return buffer; michael@0: } michael@0: michael@0: // Sequential fallback: michael@0: ASSERT_SEQUENTIAL_IS_OK(mode); michael@0: scan(self[0], 0, length); michael@0: return buffer; michael@0: michael@0: function scan(accumulator, start, end) { michael@0: UnsafePutElements(buffer, start, accumulator); michael@0: for (var i = start + 1; i < end; i++) { michael@0: accumulator = func(accumulator, self[i]); michael@0: UnsafePutElements(buffer, i, accumulator); michael@0: } michael@0: return accumulator; michael@0: } michael@0: michael@0: /** michael@0: * In phase 1, we divide the source array into |numSlices| slices and michael@0: * compute scan on each slice sequentially as if it were the entire michael@0: * array. This function is responsible for computing one of those michael@0: * slices. michael@0: * michael@0: * So, if we have an array [A,B,C,D,E,F,G,H,I], |numSlices === 3|, michael@0: * and our function |func| is sum, then we would wind up computing a michael@0: * result array like: michael@0: * michael@0: * [A, A+B, A+B+C, D, D+E, D+E+F, G, G+H, G+H+I] michael@0: * ^~~~~~~~~~~~^ ^~~~~~~~~~~~^ ^~~~~~~~~~~~~^ michael@0: * Slice 0 Slice 1 Slice 2 michael@0: * michael@0: * Read on in phase2 to see what we do next! michael@0: */ michael@0: function phase1(workerId, sliceStart, sliceEnd) { michael@0: var sliceShift = slicesInfo.shift; michael@0: var sliceId; michael@0: while (GET_SLICE(sliceStart, sliceEnd, sliceId)) { michael@0: var indexStart = SLICE_START_INDEX(sliceShift, sliceId); michael@0: var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length); michael@0: scan(self[indexStart], indexStart, indexEnd); michael@0: } michael@0: return sliceId; michael@0: } michael@0: michael@0: /** michael@0: * Computes the index of the final element computed by the slice |sliceId|. michael@0: */ michael@0: function finalElement(sliceId) { michael@0: var sliceShift = slicesInfo.shift; michael@0: return SLICE_END_INDEX(sliceShift, SLICE_START_INDEX(sliceShift, sliceId), length) - 1; michael@0: } michael@0: michael@0: /** michael@0: * After computing the phase1 results, we compute an michael@0: * |intermediates| array. |intermediates[i]| contains the result michael@0: * of reducing the final value from each preceding slice j= length) michael@0: ThrowError(JSMSG_PAR_ARRAY_SCATTER_BOUNDS); michael@0: michael@0: // It's not enough to return t, as -0 | 0 === -0. michael@0: return TO_INT32(t); michael@0: } michael@0: michael@0: return undefined; michael@0: } michael@0: michael@0: /** michael@0: * The filter operation applied in parallel. michael@0: */ michael@0: function ArrayFilterPar(func, mode) { michael@0: if (!IsCallable(func)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, func)); michael@0: michael@0: var self = ToObject(this); michael@0: var length = self.length; michael@0: michael@0: parallel: for (;;) { // see ArrayMapPar() to explain why for(;;) etc michael@0: if (ShouldForceSequential()) michael@0: break parallel; michael@0: if (!TRY_PARALLEL(mode)) michael@0: break parallel; michael@0: michael@0: var slicesInfo = ComputeSlicesInfo(length); michael@0: michael@0: // Step 1. Compute which items from each slice of the result buffer should michael@0: // be preserved. When we're done, we have a uint8 array |survivors| michael@0: // containing 0 or 1 for each source element, indicating which members of michael@0: // the chunk survived. We also keep an array |counts| containing the total michael@0: // number of items that are being preserved from within one slice. michael@0: var numSlices = slicesInfo.count; michael@0: var counts = NewDenseArray(numSlices); michael@0: for (var i = 0; i < numSlices; i++) michael@0: UnsafePutElements(counts, i, 0); michael@0: michael@0: var survivors = new Uint8Array(length); michael@0: ForkJoin(findSurvivorsThread, 0, numSlices, ForkJoinMode(mode)); michael@0: michael@0: // Step 2. Compress the slices into one contiguous set. michael@0: var count = 0; michael@0: for (var i = 0; i < numSlices; i++) michael@0: count += counts[i]; michael@0: var buffer = NewDenseArray(count); michael@0: if (count > 0) michael@0: ForkJoin(copySurvivorsThread, 0, numSlices, ForkJoinMode(mode)); michael@0: michael@0: return buffer; michael@0: } michael@0: michael@0: // Sequential fallback: michael@0: ASSERT_SEQUENTIAL_IS_OK(mode); michael@0: var buffer = []; michael@0: for (var i = 0; i < length; i++) { michael@0: var elem = self[i]; michael@0: if (func(elem, i, self)) michael@0: ARRAY_PUSH(buffer, elem); michael@0: } michael@0: return buffer; michael@0: michael@0: /** michael@0: * As described above, our goal is to determine which items we will preserve michael@0: * from a given slice, storing "to-keep" bits into 32-bit chunks. michael@0: */ michael@0: function findSurvivorsThread(workerId, sliceStart, sliceEnd) { michael@0: var sliceShift = slicesInfo.shift; michael@0: var sliceId; michael@0: while (GET_SLICE(sliceStart, sliceEnd, sliceId)) { michael@0: var count = 0; michael@0: var indexStart = SLICE_START_INDEX(sliceShift, sliceId); michael@0: var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length); michael@0: for (var indexPos = indexStart; indexPos < indexEnd; indexPos++) { michael@0: var keep = !!func(self[indexPos], indexPos, self); michael@0: UnsafePutElements(survivors, indexPos, keep); michael@0: count += keep; michael@0: } michael@0: UnsafePutElements(counts, sliceId, count); michael@0: } michael@0: return sliceId; michael@0: } michael@0: michael@0: /** michael@0: * Copies the survivors from this slice into the correct position. Note michael@0: * that this is an idempotent operation that does not invoke user michael@0: * code. Therefore, we don't expect bailouts and make an effort to proceed michael@0: * chunk by chunk or avoid duplicating work. michael@0: */ michael@0: function copySurvivorsThread(workerId, sliceStart, sliceEnd) { michael@0: var sliceShift = slicesInfo.shift; michael@0: var sliceId; michael@0: while (GET_SLICE(sliceStart, sliceEnd, sliceId)) { michael@0: // Total up the items preserved by previous slices. michael@0: var total = 0; michael@0: for (var i = 0; i < sliceId + 1; i++) michael@0: total += counts[i]; michael@0: michael@0: // Are we done? michael@0: var count = total - counts[sliceId]; michael@0: if (count === total) michael@0: continue; michael@0: michael@0: var indexStart = SLICE_START_INDEX(sliceShift, sliceId); michael@0: var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length); michael@0: for (var indexPos = indexStart; indexPos < indexEnd; indexPos++) { michael@0: if (survivors[indexPos]) { michael@0: UnsafePutElements(buffer, count++, self[indexPos]); michael@0: if (count == total) michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return sliceId; michael@0: } michael@0: michael@0: return undefined; michael@0: } michael@0: michael@0: /** michael@0: * "Comprehension form": This is the function invoked for michael@0: * |Array.{build,buildPar}(len, fn)| It creates a new array with length |len| michael@0: * where index |i| is equal to |fn(i)|. michael@0: * michael@0: * The final |mode| argument is an internal argument used only during our michael@0: * unit-testing. michael@0: */ michael@0: function ArrayStaticBuild(length, func) { michael@0: if (!IS_UINT32(length)) michael@0: ThrowError(JSMSG_BAD_ARRAY_LENGTH); michael@0: if (!IsCallable(func)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, func)); michael@0: michael@0: var buffer = NewDenseArray(length); michael@0: michael@0: for (var i = 0; i < length; i++) michael@0: UnsafePutElements(buffer, i, func(i)); michael@0: michael@0: return buffer; michael@0: } michael@0: michael@0: function ArrayStaticBuildPar(length, func, mode) { michael@0: if (!IS_UINT32(length)) michael@0: ThrowError(JSMSG_BAD_ARRAY_LENGTH); michael@0: if (!IsCallable(func)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, func)); michael@0: michael@0: var buffer = NewDenseArray(length); michael@0: michael@0: parallel: for (;;) { michael@0: if (ShouldForceSequential()) michael@0: break parallel; michael@0: if (!TRY_PARALLEL(mode)) michael@0: break parallel; michael@0: michael@0: var slicesInfo = ComputeSlicesInfo(length); michael@0: ForkJoin(constructThread, 0, slicesInfo.count, ForkJoinMode(mode)); michael@0: return buffer; michael@0: } michael@0: michael@0: // Sequential fallback: michael@0: ASSERT_SEQUENTIAL_IS_OK(mode); michael@0: for (var i = 0; i < length; i++) michael@0: UnsafePutElements(buffer, i, func(i)); michael@0: return buffer; michael@0: michael@0: function constructThread(workerId, sliceStart, sliceEnd) { michael@0: var sliceShift = slicesInfo.shift; michael@0: var sliceId; michael@0: while (GET_SLICE(sliceStart, sliceEnd, sliceId)) { michael@0: var indexStart = SLICE_START_INDEX(sliceShift, sliceId); michael@0: var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length); michael@0: for (var i = indexStart; i < indexEnd; i++) michael@0: UnsafePutElements(buffer, i, func(i)); michael@0: } michael@0: return sliceId; michael@0: } michael@0: michael@0: return undefined; michael@0: } michael@0: michael@0: /* michael@0: * Mark the main operations as clone-at-callsite for better precision. michael@0: * This is slightly overkill, as all that we really need is to michael@0: * specialize to the receiver and the elemental function, but in michael@0: * practice this is likely not so different, since element functions michael@0: * are often used in exactly one place. michael@0: */ michael@0: SetScriptHints(ArrayMapPar, { cloneAtCallsite: true }); michael@0: SetScriptHints(ArrayReducePar, { cloneAtCallsite: true }); michael@0: SetScriptHints(ArrayScanPar, { cloneAtCallsite: true }); michael@0: SetScriptHints(ArrayScatterPar, { cloneAtCallsite: true }); michael@0: SetScriptHints(ArrayFilterPar, { cloneAtCallsite: true }); michael@0: SetScriptHints(ArrayStaticBuildPar, { cloneAtCallsite: true }); michael@0: michael@0: #endif /* ENABLE_PARALLEL_JS */