PageRenderTime 53ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 1ms

/deps/v8/src/array.js

https://gitlab.com/GeekSir/node
JavaScript | 1555 lines | 1171 code | 208 blank | 176 comment | 362 complexity | 614448c57a1768e91c405c4ebfd0d548 MD5 | raw file
Possible License(s): 0BSD, Apache-2.0, MPL-2.0-no-copyleft-exception, JSON, WTFPL, CC-BY-SA-3.0, Unlicense, ISC, BSD-3-Clause, MIT, AGPL-3.0
  1. // Copyright 2012 the V8 project authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. "use strict";
  5. // This file relies on the fact that the following declarations have been made
  6. // in runtime.js:
  7. // var $Array = global.Array;
  8. // -------------------------------------------------------------------
  9. // Global list of arrays visited during toString, toLocaleString and
  10. // join invocations.
  11. var visited_arrays = new InternalArray();
  12. // Gets a sorted array of array keys. Useful for operations on sparse
  13. // arrays. Dupes have not been removed.
  14. function GetSortedArrayKeys(array, indices) {
  15. var keys = new InternalArray();
  16. if (IS_NUMBER(indices)) {
  17. // It's an interval
  18. var limit = indices;
  19. for (var i = 0; i < limit; ++i) {
  20. var e = array[i];
  21. if (!IS_UNDEFINED(e) || i in array) {
  22. keys.push(i);
  23. }
  24. }
  25. } else {
  26. var length = indices.length;
  27. for (var k = 0; k < length; ++k) {
  28. var key = indices[k];
  29. if (!IS_UNDEFINED(key)) {
  30. var e = array[key];
  31. if (!IS_UNDEFINED(e) || key in array) {
  32. keys.push(key);
  33. }
  34. }
  35. }
  36. %_CallFunction(keys, function(a, b) { return a - b; }, ArraySort);
  37. }
  38. return keys;
  39. }
  40. function SparseJoinWithSeparatorJS(array, len, convert, separator) {
  41. var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
  42. var totalLength = 0;
  43. var elements = new InternalArray(keys.length * 2);
  44. var previousKey = -1;
  45. for (var i = 0; i < keys.length; i++) {
  46. var key = keys[i];
  47. if (key != previousKey) { // keys may contain duplicates.
  48. var e = array[key];
  49. if (!IS_STRING(e)) e = convert(e);
  50. elements[i * 2] = key;
  51. elements[i * 2 + 1] = e;
  52. previousKey = key;
  53. }
  54. }
  55. return %SparseJoinWithSeparator(elements, len, separator);
  56. }
  57. // Optimized for sparse arrays if separator is ''.
  58. function SparseJoin(array, len, convert) {
  59. var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
  60. var last_key = -1;
  61. var keys_length = keys.length;
  62. var elements = new InternalArray(keys_length);
  63. var elements_length = 0;
  64. for (var i = 0; i < keys_length; i++) {
  65. var key = keys[i];
  66. if (key != last_key) {
  67. var e = array[key];
  68. if (!IS_STRING(e)) e = convert(e);
  69. elements[elements_length++] = e;
  70. last_key = key;
  71. }
  72. }
  73. return %StringBuilderConcat(elements, elements_length, '');
  74. }
  75. function UseSparseVariant(array, length, is_array, touched) {
  76. // Only use the sparse variant on arrays that are likely to be sparse and the
  77. // number of elements touched in the operation is relatively small compared to
  78. // the overall size of the array.
  79. if (!is_array || length < 1000 || %IsObserved(array)) {
  80. return false;
  81. }
  82. if (!%_IsSmi(length)) {
  83. return true;
  84. }
  85. var elements_threshold = length >> 2; // No more than 75% holes
  86. var estimated_elements = %EstimateNumberOfElements(array);
  87. return (estimated_elements < elements_threshold) &&
  88. (touched > estimated_elements * 4);
  89. }
  90. function Join(array, length, separator, convert) {
  91. if (length == 0) return '';
  92. var is_array = IS_ARRAY(array);
  93. if (is_array) {
  94. // If the array is cyclic, return the empty string for already
  95. // visited arrays.
  96. if (!%PushIfAbsent(visited_arrays, array)) return '';
  97. }
  98. // Attempt to convert the elements.
  99. try {
  100. if (UseSparseVariant(array, length, is_array, length)) {
  101. %NormalizeElements(array);
  102. if (separator.length == 0) {
  103. return SparseJoin(array, length, convert);
  104. } else {
  105. return SparseJoinWithSeparatorJS(array, length, convert, separator);
  106. }
  107. }
  108. // Fast case for one-element arrays.
  109. if (length == 1) {
  110. var e = array[0];
  111. if (IS_STRING(e)) return e;
  112. return convert(e);
  113. }
  114. // Construct an array for the elements.
  115. var elements = new InternalArray(length);
  116. // We pull the empty separator check outside the loop for speed!
  117. if (separator.length == 0) {
  118. var elements_length = 0;
  119. for (var i = 0; i < length; i++) {
  120. var e = array[i];
  121. if (!IS_STRING(e)) e = convert(e);
  122. elements[elements_length++] = e;
  123. }
  124. elements.length = elements_length;
  125. var result = %_FastAsciiArrayJoin(elements, '');
  126. if (!IS_UNDEFINED(result)) return result;
  127. return %StringBuilderConcat(elements, elements_length, '');
  128. }
  129. // Non-empty separator case.
  130. // If the first element is a number then use the heuristic that the
  131. // remaining elements are also likely to be numbers.
  132. if (!IS_NUMBER(array[0])) {
  133. for (var i = 0; i < length; i++) {
  134. var e = array[i];
  135. if (!IS_STRING(e)) e = convert(e);
  136. elements[i] = e;
  137. }
  138. } else {
  139. for (var i = 0; i < length; i++) {
  140. var e = array[i];
  141. if (IS_NUMBER(e)) {
  142. e = %_NumberToString(e);
  143. } else if (!IS_STRING(e)) {
  144. e = convert(e);
  145. }
  146. elements[i] = e;
  147. }
  148. }
  149. var result = %_FastAsciiArrayJoin(elements, separator);
  150. if (!IS_UNDEFINED(result)) return result;
  151. return %StringBuilderJoin(elements, length, separator);
  152. } finally {
  153. // Make sure to remove the last element of the visited array no
  154. // matter what happens.
  155. if (is_array) visited_arrays.length = visited_arrays.length - 1;
  156. }
  157. }
  158. function ConvertToString(x) {
  159. // Assumes x is a non-string.
  160. if (IS_NUMBER(x)) return %_NumberToString(x);
  161. if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
  162. return (IS_NULL_OR_UNDEFINED(x)) ? '' : %ToString(%DefaultString(x));
  163. }
  164. function ConvertToLocaleString(e) {
  165. if (IS_NULL_OR_UNDEFINED(e)) {
  166. return '';
  167. } else {
  168. // According to ES5, section 15.4.4.3, the toLocaleString conversion
  169. // must throw a TypeError if ToObject(e).toLocaleString isn't
  170. // callable.
  171. var e_obj = ToObject(e);
  172. return %ToString(e_obj.toLocaleString());
  173. }
  174. }
  175. // This function implements the optimized splice implementation that can use
  176. // special array operations to handle sparse arrays in a sensible fashion.
  177. function SmartSlice(array, start_i, del_count, len, deleted_elements) {
  178. // Move deleted elements to a new array (the return value from splice).
  179. var indices = %GetArrayKeys(array, start_i + del_count);
  180. if (IS_NUMBER(indices)) {
  181. var limit = indices;
  182. for (var i = start_i; i < limit; ++i) {
  183. var current = array[i];
  184. if (!IS_UNDEFINED(current) || i in array) {
  185. deleted_elements[i - start_i] = current;
  186. }
  187. }
  188. } else {
  189. var length = indices.length;
  190. for (var k = 0; k < length; ++k) {
  191. var key = indices[k];
  192. if (!IS_UNDEFINED(key)) {
  193. if (key >= start_i) {
  194. var current = array[key];
  195. if (!IS_UNDEFINED(current) || key in array) {
  196. deleted_elements[key - start_i] = current;
  197. }
  198. }
  199. }
  200. }
  201. }
  202. }
  203. // This function implements the optimized splice implementation that can use
  204. // special array operations to handle sparse arrays in a sensible fashion.
  205. function SmartMove(array, start_i, del_count, len, num_additional_args) {
  206. // Move data to new array.
  207. var new_array = new InternalArray(len - del_count + num_additional_args);
  208. var indices = %GetArrayKeys(array, len);
  209. if (IS_NUMBER(indices)) {
  210. var limit = indices;
  211. for (var i = 0; i < start_i && i < limit; ++i) {
  212. var current = array[i];
  213. if (!IS_UNDEFINED(current) || i in array) {
  214. new_array[i] = current;
  215. }
  216. }
  217. for (var i = start_i + del_count; i < limit; ++i) {
  218. var current = array[i];
  219. if (!IS_UNDEFINED(current) || i in array) {
  220. new_array[i - del_count + num_additional_args] = current;
  221. }
  222. }
  223. } else {
  224. var length = indices.length;
  225. for (var k = 0; k < length; ++k) {
  226. var key = indices[k];
  227. if (!IS_UNDEFINED(key)) {
  228. if (key < start_i) {
  229. var current = array[key];
  230. if (!IS_UNDEFINED(current) || key in array) {
  231. new_array[key] = current;
  232. }
  233. } else if (key >= start_i + del_count) {
  234. var current = array[key];
  235. if (!IS_UNDEFINED(current) || key in array) {
  236. new_array[key - del_count + num_additional_args] = current;
  237. }
  238. }
  239. }
  240. }
  241. }
  242. // Move contents of new_array into this array
  243. %MoveArrayContents(new_array, array);
  244. }
  245. // This is part of the old simple-minded splice. We are using it either
  246. // because the receiver is not an array (so we have no choice) or because we
  247. // know we are not deleting or moving a lot of elements.
  248. function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
  249. for (var i = 0; i < del_count; i++) {
  250. var index = start_i + i;
  251. // The spec could also be interpreted such that %HasOwnProperty
  252. // would be the appropriate test. We follow KJS in consulting the
  253. // prototype.
  254. var current = array[index];
  255. if (!IS_UNDEFINED(current) || index in array) {
  256. deleted_elements[i] = current;
  257. }
  258. }
  259. }
  260. function SimpleMove(array, start_i, del_count, len, num_additional_args) {
  261. if (num_additional_args !== del_count) {
  262. // Move the existing elements after the elements to be deleted
  263. // to the right position in the resulting array.
  264. if (num_additional_args > del_count) {
  265. for (var i = len - del_count; i > start_i; i--) {
  266. var from_index = i + del_count - 1;
  267. var to_index = i + num_additional_args - 1;
  268. // The spec could also be interpreted such that
  269. // %HasOwnProperty would be the appropriate test. We follow
  270. // KJS in consulting the prototype.
  271. var current = array[from_index];
  272. if (!IS_UNDEFINED(current) || from_index in array) {
  273. array[to_index] = current;
  274. } else {
  275. delete array[to_index];
  276. }
  277. }
  278. } else {
  279. for (var i = start_i; i < len - del_count; i++) {
  280. var from_index = i + del_count;
  281. var to_index = i + num_additional_args;
  282. // The spec could also be interpreted such that
  283. // %HasOwnProperty would be the appropriate test. We follow
  284. // KJS in consulting the prototype.
  285. var current = array[from_index];
  286. if (!IS_UNDEFINED(current) || from_index in array) {
  287. array[to_index] = current;
  288. } else {
  289. delete array[to_index];
  290. }
  291. }
  292. for (var i = len; i > len - del_count + num_additional_args; i--) {
  293. delete array[i - 1];
  294. }
  295. }
  296. }
  297. }
  298. // -------------------------------------------------------------------
  299. function ArrayToString() {
  300. var array;
  301. var func;
  302. if (IS_ARRAY(this)) {
  303. func = this.join;
  304. if (func === ArrayJoin) {
  305. return Join(this, this.length, ',', ConvertToString);
  306. }
  307. array = this;
  308. } else {
  309. array = ToObject(this);
  310. func = array.join;
  311. }
  312. if (!IS_SPEC_FUNCTION(func)) {
  313. return %_CallFunction(array, ObjectToString);
  314. }
  315. return %_CallFunction(array, func);
  316. }
  317. function ArrayToLocaleString() {
  318. var array = ToObject(this);
  319. var arrayLen = array.length;
  320. var len = TO_UINT32(arrayLen);
  321. if (len === 0) return "";
  322. return Join(array, len, ',', ConvertToLocaleString);
  323. }
  324. function ArrayJoin(separator) {
  325. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.join");
  326. var array = TO_OBJECT_INLINE(this);
  327. var length = TO_UINT32(array.length);
  328. if (IS_UNDEFINED(separator)) {
  329. separator = ',';
  330. } else if (!IS_STRING(separator)) {
  331. separator = NonStringToString(separator);
  332. }
  333. var result = %_FastAsciiArrayJoin(array, separator);
  334. if (!IS_UNDEFINED(result)) return result;
  335. return Join(array, length, separator, ConvertToString);
  336. }
  337. function ObservedArrayPop(n) {
  338. n--;
  339. var value = this[n];
  340. try {
  341. BeginPerformSplice(this);
  342. delete this[n];
  343. this.length = n;
  344. } finally {
  345. EndPerformSplice(this);
  346. EnqueueSpliceRecord(this, n, [value], 0);
  347. }
  348. return value;
  349. }
  350. // Removes the last element from the array and returns it. See
  351. // ECMA-262, section 15.4.4.6.
  352. function ArrayPop() {
  353. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.pop");
  354. var array = TO_OBJECT_INLINE(this);
  355. var n = TO_UINT32(array.length);
  356. if (n == 0) {
  357. array.length = n;
  358. return;
  359. }
  360. if (%IsObserved(array))
  361. return ObservedArrayPop.call(array, n);
  362. n--;
  363. var value = array[n];
  364. Delete(array, ToName(n), true);
  365. array.length = n;
  366. return value;
  367. }
  368. function ObservedArrayPush() {
  369. var n = TO_UINT32(this.length);
  370. var m = %_ArgumentsLength();
  371. try {
  372. BeginPerformSplice(this);
  373. for (var i = 0; i < m; i++) {
  374. this[i+n] = %_Arguments(i);
  375. }
  376. var new_length = n + m;
  377. this.length = new_length;
  378. } finally {
  379. EndPerformSplice(this);
  380. EnqueueSpliceRecord(this, n, [], m);
  381. }
  382. return new_length;
  383. }
  384. // Appends the arguments to the end of the array and returns the new
  385. // length of the array. See ECMA-262, section 15.4.4.7.
  386. function ArrayPush() {
  387. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.push");
  388. if (%IsObserved(this))
  389. return ObservedArrayPush.apply(this, arguments);
  390. var array = TO_OBJECT_INLINE(this);
  391. var n = TO_UINT32(array.length);
  392. var m = %_ArgumentsLength();
  393. for (var i = 0; i < m; i++) {
  394. array[i+n] = %_Arguments(i);
  395. }
  396. var new_length = n + m;
  397. array.length = new_length;
  398. return new_length;
  399. }
  400. // Returns an array containing the array elements of the object followed
  401. // by the array elements of each argument in order. See ECMA-262,
  402. // section 15.4.4.7.
  403. function ArrayConcatJS(arg1) { // length == 1
  404. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.concat");
  405. var array = ToObject(this);
  406. var arg_count = %_ArgumentsLength();
  407. var arrays = new InternalArray(1 + arg_count);
  408. arrays[0] = array;
  409. for (var i = 0; i < arg_count; i++) {
  410. arrays[i + 1] = %_Arguments(i);
  411. }
  412. return %ArrayConcat(arrays);
  413. }
  414. // For implementing reverse() on large, sparse arrays.
  415. function SparseReverse(array, len) {
  416. var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
  417. var high_counter = keys.length - 1;
  418. var low_counter = 0;
  419. while (low_counter <= high_counter) {
  420. var i = keys[low_counter];
  421. var j = keys[high_counter];
  422. var j_complement = len - j - 1;
  423. var low, high;
  424. if (j_complement <= i) {
  425. high = j;
  426. while (keys[--high_counter] == j) { }
  427. low = j_complement;
  428. }
  429. if (j_complement >= i) {
  430. low = i;
  431. while (keys[++low_counter] == i) { }
  432. high = len - i - 1;
  433. }
  434. var current_i = array[low];
  435. if (!IS_UNDEFINED(current_i) || low in array) {
  436. var current_j = array[high];
  437. if (!IS_UNDEFINED(current_j) || high in array) {
  438. array[low] = current_j;
  439. array[high] = current_i;
  440. } else {
  441. array[high] = current_i;
  442. delete array[low];
  443. }
  444. } else {
  445. var current_j = array[high];
  446. if (!IS_UNDEFINED(current_j) || high in array) {
  447. array[low] = current_j;
  448. delete array[high];
  449. }
  450. }
  451. }
  452. }
  453. function ArrayReverse() {
  454. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reverse");
  455. var array = TO_OBJECT_INLINE(this);
  456. var len = TO_UINT32(array.length);
  457. if (UseSparseVariant(array, len, IS_ARRAY(array), len)) {
  458. %NormalizeElements(array);
  459. SparseReverse(array, len);
  460. return array;
  461. }
  462. var j = len - 1;
  463. for (var i = 0; i < j; i++, j--) {
  464. var current_i = array[i];
  465. if (!IS_UNDEFINED(current_i) || i in array) {
  466. var current_j = array[j];
  467. if (!IS_UNDEFINED(current_j) || j in array) {
  468. array[i] = current_j;
  469. array[j] = current_i;
  470. } else {
  471. array[j] = current_i;
  472. delete array[i];
  473. }
  474. } else {
  475. var current_j = array[j];
  476. if (!IS_UNDEFINED(current_j) || j in array) {
  477. array[i] = current_j;
  478. delete array[j];
  479. }
  480. }
  481. }
  482. return array;
  483. }
  484. function ObservedArrayShift(len) {
  485. var first = this[0];
  486. try {
  487. BeginPerformSplice(this);
  488. SimpleMove(this, 0, 1, len, 0);
  489. this.length = len - 1;
  490. } finally {
  491. EndPerformSplice(this);
  492. EnqueueSpliceRecord(this, 0, [first], 0);
  493. }
  494. return first;
  495. }
  496. function ArrayShift() {
  497. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.shift");
  498. var array = TO_OBJECT_INLINE(this);
  499. var len = TO_UINT32(array.length);
  500. if (len === 0) {
  501. array.length = 0;
  502. return;
  503. }
  504. if (ObjectIsSealed(array)) {
  505. throw MakeTypeError("array_functions_change_sealed",
  506. ["Array.prototype.shift"]);
  507. }
  508. if (%IsObserved(array))
  509. return ObservedArrayShift.call(array, len);
  510. var first = array[0];
  511. if (IS_ARRAY(array)) {
  512. SmartMove(array, 0, 1, len, 0);
  513. } else {
  514. SimpleMove(array, 0, 1, len, 0);
  515. }
  516. array.length = len - 1;
  517. return first;
  518. }
  519. function ObservedArrayUnshift() {
  520. var len = TO_UINT32(this.length);
  521. var num_arguments = %_ArgumentsLength();
  522. try {
  523. BeginPerformSplice(this);
  524. SimpleMove(this, 0, 0, len, num_arguments);
  525. for (var i = 0; i < num_arguments; i++) {
  526. this[i] = %_Arguments(i);
  527. }
  528. var new_length = len + num_arguments;
  529. this.length = new_length;
  530. } finally {
  531. EndPerformSplice(this);
  532. EnqueueSpliceRecord(this, 0, [], num_arguments);
  533. }
  534. return new_length;
  535. }
  536. function ArrayUnshift(arg1) { // length == 1
  537. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.unshift");
  538. if (%IsObserved(this))
  539. return ObservedArrayUnshift.apply(this, arguments);
  540. var array = TO_OBJECT_INLINE(this);
  541. var len = TO_UINT32(array.length);
  542. var num_arguments = %_ArgumentsLength();
  543. var is_sealed = ObjectIsSealed(array);
  544. if (IS_ARRAY(array) && !is_sealed && len > 0) {
  545. SmartMove(array, 0, 0, len, num_arguments);
  546. } else {
  547. SimpleMove(array, 0, 0, len, num_arguments);
  548. }
  549. for (var i = 0; i < num_arguments; i++) {
  550. array[i] = %_Arguments(i);
  551. }
  552. var new_length = len + num_arguments;
  553. array.length = new_length;
  554. return new_length;
  555. }
  556. function ArraySlice(start, end) {
  557. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.slice");
  558. var array = TO_OBJECT_INLINE(this);
  559. var len = TO_UINT32(array.length);
  560. var start_i = TO_INTEGER(start);
  561. var end_i = len;
  562. if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end);
  563. if (start_i < 0) {
  564. start_i += len;
  565. if (start_i < 0) start_i = 0;
  566. } else {
  567. if (start_i > len) start_i = len;
  568. }
  569. if (end_i < 0) {
  570. end_i += len;
  571. if (end_i < 0) end_i = 0;
  572. } else {
  573. if (end_i > len) end_i = len;
  574. }
  575. var result = [];
  576. if (end_i < start_i) return result;
  577. if (UseSparseVariant(array, len, IS_ARRAY(array), end_i - start_i)) {
  578. %NormalizeElements(array);
  579. %NormalizeElements(result);
  580. SmartSlice(array, start_i, end_i - start_i, len, result);
  581. } else {
  582. SimpleSlice(array, start_i, end_i - start_i, len, result);
  583. }
  584. result.length = end_i - start_i;
  585. return result;
  586. }
  587. function ComputeSpliceStartIndex(start_i, len) {
  588. if (start_i < 0) {
  589. start_i += len;
  590. return start_i < 0 ? 0 : start_i;
  591. }
  592. return start_i > len ? len : start_i;
  593. }
  594. function ComputeSpliceDeleteCount(delete_count, num_arguments, len, start_i) {
  595. // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
  596. // given as a request to delete all the elements from the start.
  597. // And it differs from the case of undefined delete count.
  598. // This does not follow ECMA-262, but we do the same for
  599. // compatibility.
  600. var del_count = 0;
  601. if (num_arguments == 1)
  602. return len - start_i;
  603. del_count = TO_INTEGER(delete_count);
  604. if (del_count < 0)
  605. return 0;
  606. if (del_count > len - start_i)
  607. return len - start_i;
  608. return del_count;
  609. }
  610. function ObservedArraySplice(start, delete_count) {
  611. var num_arguments = %_ArgumentsLength();
  612. var len = TO_UINT32(this.length);
  613. var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
  614. var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
  615. start_i);
  616. var deleted_elements = [];
  617. deleted_elements.length = del_count;
  618. var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
  619. try {
  620. BeginPerformSplice(this);
  621. SimpleSlice(this, start_i, del_count, len, deleted_elements);
  622. SimpleMove(this, start_i, del_count, len, num_elements_to_add);
  623. // Insert the arguments into the resulting array in
  624. // place of the deleted elements.
  625. var i = start_i;
  626. var arguments_index = 2;
  627. var arguments_length = %_ArgumentsLength();
  628. while (arguments_index < arguments_length) {
  629. this[i++] = %_Arguments(arguments_index++);
  630. }
  631. this.length = len - del_count + num_elements_to_add;
  632. } finally {
  633. EndPerformSplice(this);
  634. if (deleted_elements.length || num_elements_to_add) {
  635. EnqueueSpliceRecord(this,
  636. start_i,
  637. deleted_elements.slice(),
  638. num_elements_to_add);
  639. }
  640. }
  641. // Return the deleted elements.
  642. return deleted_elements;
  643. }
  644. function ArraySplice(start, delete_count) {
  645. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.splice");
  646. if (%IsObserved(this))
  647. return ObservedArraySplice.apply(this, arguments);
  648. var num_arguments = %_ArgumentsLength();
  649. var array = TO_OBJECT_INLINE(this);
  650. var len = TO_UINT32(array.length);
  651. var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
  652. var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
  653. start_i);
  654. var deleted_elements = [];
  655. deleted_elements.length = del_count;
  656. var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
  657. if (del_count != num_elements_to_add && ObjectIsSealed(array)) {
  658. throw MakeTypeError("array_functions_change_sealed",
  659. ["Array.prototype.splice"]);
  660. } else if (del_count > 0 && ObjectIsFrozen(array)) {
  661. throw MakeTypeError("array_functions_on_frozen",
  662. ["Array.prototype.splice"]);
  663. }
  664. var changed_elements = del_count;
  665. if (num_elements_to_add != del_count) {
  666. // If the slice needs to do a actually move elements after the insertion
  667. // point, then include those in the estimate of changed elements.
  668. changed_elements += len - start_i - del_count;
  669. }
  670. if (UseSparseVariant(array, len, IS_ARRAY(array), changed_elements)) {
  671. %NormalizeElements(array);
  672. %NormalizeElements(deleted_elements);
  673. SmartSlice(array, start_i, del_count, len, deleted_elements);
  674. SmartMove(array, start_i, del_count, len, num_elements_to_add);
  675. } else {
  676. SimpleSlice(array, start_i, del_count, len, deleted_elements);
  677. SimpleMove(array, start_i, del_count, len, num_elements_to_add);
  678. }
  679. // Insert the arguments into the resulting array in
  680. // place of the deleted elements.
  681. var i = start_i;
  682. var arguments_index = 2;
  683. var arguments_length = %_ArgumentsLength();
  684. while (arguments_index < arguments_length) {
  685. array[i++] = %_Arguments(arguments_index++);
  686. }
  687. array.length = len - del_count + num_elements_to_add;
  688. // Return the deleted elements.
  689. return deleted_elements;
  690. }
  691. function ArraySort(comparefn) {
  692. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.sort");
  693. // In-place QuickSort algorithm.
  694. // For short (length <= 22) arrays, insertion sort is used for efficiency.
  695. if (!IS_SPEC_FUNCTION(comparefn)) {
  696. comparefn = function (x, y) {
  697. if (x === y) return 0;
  698. if (%_IsSmi(x) && %_IsSmi(y)) {
  699. return %SmiLexicographicCompare(x, y);
  700. }
  701. x = ToString(x);
  702. y = ToString(y);
  703. if (x == y) return 0;
  704. else return x < y ? -1 : 1;
  705. };
  706. }
  707. var receiver = %GetDefaultReceiver(comparefn);
  708. var InsertionSort = function InsertionSort(a, from, to) {
  709. for (var i = from + 1; i < to; i++) {
  710. var element = a[i];
  711. for (var j = i - 1; j >= from; j--) {
  712. var tmp = a[j];
  713. var order = %_CallFunction(receiver, tmp, element, comparefn);
  714. if (order > 0) {
  715. a[j + 1] = tmp;
  716. } else {
  717. break;
  718. }
  719. }
  720. a[j + 1] = element;
  721. }
  722. };
  723. var GetThirdIndex = function(a, from, to) {
  724. var t_array = [];
  725. // Use both 'from' and 'to' to determine the pivot candidates.
  726. var increment = 200 + ((to - from) & 15);
  727. for (var i = from + 1; i < to - 1; i += increment) {
  728. t_array.push([i, a[i]]);
  729. }
  730. t_array.sort(function(a, b) {
  731. return %_CallFunction(receiver, a[1], b[1], comparefn) } );
  732. var third_index = t_array[t_array.length >> 1][0];
  733. return third_index;
  734. }
  735. var QuickSort = function QuickSort(a, from, to) {
  736. var third_index = 0;
  737. while (true) {
  738. // Insertion sort is faster for short arrays.
  739. if (to - from <= 10) {
  740. InsertionSort(a, from, to);
  741. return;
  742. }
  743. if (to - from > 1000) {
  744. third_index = GetThirdIndex(a, from, to);
  745. } else {
  746. third_index = from + ((to - from) >> 1);
  747. }
  748. // Find a pivot as the median of first, last and middle element.
  749. var v0 = a[from];
  750. var v1 = a[to - 1];
  751. var v2 = a[third_index];
  752. var c01 = %_CallFunction(receiver, v0, v1, comparefn);
  753. if (c01 > 0) {
  754. // v1 < v0, so swap them.
  755. var tmp = v0;
  756. v0 = v1;
  757. v1 = tmp;
  758. } // v0 <= v1.
  759. var c02 = %_CallFunction(receiver, v0, v2, comparefn);
  760. if (c02 >= 0) {
  761. // v2 <= v0 <= v1.
  762. var tmp = v0;
  763. v0 = v2;
  764. v2 = v1;
  765. v1 = tmp;
  766. } else {
  767. // v0 <= v1 && v0 < v2
  768. var c12 = %_CallFunction(receiver, v1, v2, comparefn);
  769. if (c12 > 0) {
  770. // v0 <= v2 < v1
  771. var tmp = v1;
  772. v1 = v2;
  773. v2 = tmp;
  774. }
  775. }
  776. // v0 <= v1 <= v2
  777. a[from] = v0;
  778. a[to - 1] = v2;
  779. var pivot = v1;
  780. var low_end = from + 1; // Upper bound of elements lower than pivot.
  781. var high_start = to - 1; // Lower bound of elements greater than pivot.
  782. a[third_index] = a[low_end];
  783. a[low_end] = pivot;
  784. // From low_end to i are elements equal to pivot.
  785. // From i to high_start are elements that haven't been compared yet.
  786. partition: for (var i = low_end + 1; i < high_start; i++) {
  787. var element = a[i];
  788. var order = %_CallFunction(receiver, element, pivot, comparefn);
  789. if (order < 0) {
  790. a[i] = a[low_end];
  791. a[low_end] = element;
  792. low_end++;
  793. } else if (order > 0) {
  794. do {
  795. high_start--;
  796. if (high_start == i) break partition;
  797. var top_elem = a[high_start];
  798. order = %_CallFunction(receiver, top_elem, pivot, comparefn);
  799. } while (order > 0);
  800. a[i] = a[high_start];
  801. a[high_start] = element;
  802. if (order < 0) {
  803. element = a[i];
  804. a[i] = a[low_end];
  805. a[low_end] = element;
  806. low_end++;
  807. }
  808. }
  809. }
  810. if (to - high_start < low_end - from) {
  811. QuickSort(a, high_start, to);
  812. to = low_end;
  813. } else {
  814. QuickSort(a, from, low_end);
  815. from = high_start;
  816. }
  817. }
  818. };
  819. // Copy elements in the range 0..length from obj's prototype chain
  820. // to obj itself, if obj has holes. Return one more than the maximal index
  821. // of a prototype property.
  822. var CopyFromPrototype = function CopyFromPrototype(obj, length) {
  823. var max = 0;
  824. for (var proto = %GetPrototype(obj); proto; proto = %GetPrototype(proto)) {
  825. var indices = %GetArrayKeys(proto, length);
  826. if (IS_NUMBER(indices)) {
  827. // It's an interval.
  828. var proto_length = indices;
  829. for (var i = 0; i < proto_length; i++) {
  830. if (!obj.hasOwnProperty(i) && proto.hasOwnProperty(i)) {
  831. obj[i] = proto[i];
  832. if (i >= max) { max = i + 1; }
  833. }
  834. }
  835. } else {
  836. for (var i = 0; i < indices.length; i++) {
  837. var index = indices[i];
  838. if (!IS_UNDEFINED(index) &&
  839. !obj.hasOwnProperty(index) && proto.hasOwnProperty(index)) {
  840. obj[index] = proto[index];
  841. if (index >= max) { max = index + 1; }
  842. }
  843. }
  844. }
  845. }
  846. return max;
  847. };
  848. // Set a value of "undefined" on all indices in the range from..to
  849. // where a prototype of obj has an element. I.e., shadow all prototype
  850. // elements in that range.
  851. var ShadowPrototypeElements = function(obj, from, to) {
  852. for (var proto = %GetPrototype(obj); proto; proto = %GetPrototype(proto)) {
  853. var indices = %GetArrayKeys(proto, to);
  854. if (IS_NUMBER(indices)) {
  855. // It's an interval.
  856. var proto_length = indices;
  857. for (var i = from; i < proto_length; i++) {
  858. if (proto.hasOwnProperty(i)) {
  859. obj[i] = UNDEFINED;
  860. }
  861. }
  862. } else {
  863. for (var i = 0; i < indices.length; i++) {
  864. var index = indices[i];
  865. if (!IS_UNDEFINED(index) && from <= index &&
  866. proto.hasOwnProperty(index)) {
  867. obj[index] = UNDEFINED;
  868. }
  869. }
  870. }
  871. }
  872. };
  873. var SafeRemoveArrayHoles = function SafeRemoveArrayHoles(obj) {
  874. // Copy defined elements from the end to fill in all holes and undefineds
  875. // in the beginning of the array. Write undefineds and holes at the end
  876. // after loop is finished.
  877. var first_undefined = 0;
  878. var last_defined = length - 1;
  879. var num_holes = 0;
  880. while (first_undefined < last_defined) {
  881. // Find first undefined element.
  882. while (first_undefined < last_defined &&
  883. !IS_UNDEFINED(obj[first_undefined])) {
  884. first_undefined++;
  885. }
  886. // Maintain the invariant num_holes = the number of holes in the original
  887. // array with indices <= first_undefined or > last_defined.
  888. if (!obj.hasOwnProperty(first_undefined)) {
  889. num_holes++;
  890. }
  891. // Find last defined element.
  892. while (first_undefined < last_defined &&
  893. IS_UNDEFINED(obj[last_defined])) {
  894. if (!obj.hasOwnProperty(last_defined)) {
  895. num_holes++;
  896. }
  897. last_defined--;
  898. }
  899. if (first_undefined < last_defined) {
  900. // Fill in hole or undefined.
  901. obj[first_undefined] = obj[last_defined];
  902. obj[last_defined] = UNDEFINED;
  903. }
  904. }
  905. // If there were any undefineds in the entire array, first_undefined
  906. // points to one past the last defined element. Make this true if
  907. // there were no undefineds, as well, so that first_undefined == number
  908. // of defined elements.
  909. if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
  910. // Fill in the undefineds and the holes. There may be a hole where
  911. // an undefined should be and vice versa.
  912. var i;
  913. for (i = first_undefined; i < length - num_holes; i++) {
  914. obj[i] = UNDEFINED;
  915. }
  916. for (i = length - num_holes; i < length; i++) {
  917. // For compatability with Webkit, do not expose elements in the prototype.
  918. if (i in %GetPrototype(obj)) {
  919. obj[i] = UNDEFINED;
  920. } else {
  921. delete obj[i];
  922. }
  923. }
  924. // Return the number of defined elements.
  925. return first_undefined;
  926. };
  927. var length = TO_UINT32(this.length);
  928. if (length < 2) return this;
  929. var is_array = IS_ARRAY(this);
  930. var max_prototype_element;
  931. if (!is_array) {
  932. // For compatibility with JSC, we also sort elements inherited from
  933. // the prototype chain on non-Array objects.
  934. // We do this by copying them to this object and sorting only
  935. // own elements. This is not very efficient, but sorting with
  936. // inherited elements happens very, very rarely, if at all.
  937. // The specification allows "implementation dependent" behavior
  938. // if an element on the prototype chain has an element that
  939. // might interact with sorting.
  940. max_prototype_element = CopyFromPrototype(this, length);
  941. }
  942. // %RemoveArrayHoles returns -1 if fast removal is not supported.
  943. var num_non_undefined = %RemoveArrayHoles(this, length);
  944. if (num_non_undefined == -1) {
  945. // The array is observed, or there were indexed accessors in the array.
  946. // Move array holes and undefineds to the end using a Javascript function
  947. // that is safe in the presence of accessors and is observable.
  948. num_non_undefined = SafeRemoveArrayHoles(this);
  949. }
  950. QuickSort(this, 0, num_non_undefined);
  951. if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
  952. // For compatibility with JSC, we shadow any elements in the prototype
  953. // chain that has become exposed by sort moving a hole to its position.
  954. ShadowPrototypeElements(this, num_non_undefined, max_prototype_element);
  955. }
  956. return this;
  957. }
  958. // The following functions cannot be made efficient on sparse arrays while
  959. // preserving the semantics, since the calls to the receiver function can add
  960. // or delete elements from the array.
  961. function ArrayFilter(f, receiver) {
  962. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.filter");
  963. // Pull out the length so that modifications to the length in the
  964. // loop will not affect the looping and side effects are visible.
  965. var array = ToObject(this);
  966. var length = ToUint32(array.length);
  967. if (!IS_SPEC_FUNCTION(f)) {
  968. throw MakeTypeError('called_non_callable', [ f ]);
  969. }
  970. if (IS_NULL_OR_UNDEFINED(receiver)) {
  971. receiver = %GetDefaultReceiver(f) || receiver;
  972. } else if (!IS_SPEC_OBJECT(receiver) && %IsSloppyModeFunction(f)) {
  973. receiver = ToObject(receiver);
  974. }
  975. var result = new $Array();
  976. var accumulator = new InternalArray();
  977. var accumulator_length = 0;
  978. var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
  979. for (var i = 0; i < length; i++) {
  980. if (i in array) {
  981. var element = array[i];
  982. // Prepare break slots for debugger step in.
  983. if (stepping) %DebugPrepareStepInIfStepping(f);
  984. if (%_CallFunction(receiver, element, i, array, f)) {
  985. accumulator[accumulator_length++] = element;
  986. }
  987. }
  988. }
  989. %MoveArrayContents(accumulator, result);
  990. return result;
  991. }
  992. function ArrayForEach(f, receiver) {
  993. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.forEach");
  994. // Pull out the length so that modifications to the length in the
  995. // loop will not affect the looping and side effects are visible.
  996. var array = ToObject(this);
  997. var length = TO_UINT32(array.length);
  998. if (!IS_SPEC_FUNCTION(f)) {
  999. throw MakeTypeError('called_non_callable', [ f ]);
  1000. }
  1001. if (IS_NULL_OR_UNDEFINED(receiver)) {
  1002. receiver = %GetDefaultReceiver(f) || receiver;
  1003. } else if (!IS_SPEC_OBJECT(receiver) && %IsSloppyModeFunction(f)) {
  1004. receiver = ToObject(receiver);
  1005. }
  1006. var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
  1007. for (var i = 0; i < length; i++) {
  1008. if (i in array) {
  1009. var element = array[i];
  1010. // Prepare break slots for debugger step in.
  1011. if (stepping) %DebugPrepareStepInIfStepping(f);
  1012. %_CallFunction(receiver, element, i, array, f);
  1013. }
  1014. }
  1015. }
  1016. // Executes the function once for each element present in the
  1017. // array until it finds one where callback returns true.
  1018. function ArraySome(f, receiver) {
  1019. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.some");
  1020. // Pull out the length so that modifications to the length in the
  1021. // loop will not affect the looping and side effects are visible.
  1022. var array = ToObject(this);
  1023. var length = TO_UINT32(array.length);
  1024. if (!IS_SPEC_FUNCTION(f)) {
  1025. throw MakeTypeError('called_non_callable', [ f ]);
  1026. }
  1027. if (IS_NULL_OR_UNDEFINED(receiver)) {
  1028. receiver = %GetDefaultReceiver(f) || receiver;
  1029. } else if (!IS_SPEC_OBJECT(receiver) && %IsSloppyModeFunction(f)) {
  1030. receiver = ToObject(receiver);
  1031. }
  1032. var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
  1033. for (var i = 0; i < length; i++) {
  1034. if (i in array) {
  1035. var element = array[i];
  1036. // Prepare break slots for debugger step in.
  1037. if (stepping) %DebugPrepareStepInIfStepping(f);
  1038. if (%_CallFunction(receiver, element, i, array, f)) return true;
  1039. }
  1040. }
  1041. return false;
  1042. }
  1043. function ArrayEvery(f, receiver) {
  1044. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.every");
  1045. // Pull out the length so that modifications to the length in the
  1046. // loop will not affect the looping and side effects are visible.
  1047. var array = ToObject(this);
  1048. var length = TO_UINT32(array.length);
  1049. if (!IS_SPEC_FUNCTION(f)) {
  1050. throw MakeTypeError('called_non_callable', [ f ]);
  1051. }
  1052. if (IS_NULL_OR_UNDEFINED(receiver)) {
  1053. receiver = %GetDefaultReceiver(f) || receiver;
  1054. } else if (!IS_SPEC_OBJECT(receiver) && %IsSloppyModeFunction(f)) {
  1055. receiver = ToObject(receiver);
  1056. }
  1057. var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
  1058. for (var i = 0; i < length; i++) {
  1059. if (i in array) {
  1060. var element = array[i];
  1061. // Prepare break slots for debugger step in.
  1062. if (stepping) %DebugPrepareStepInIfStepping(f);
  1063. if (!%_CallFunction(receiver, element, i, array, f)) return false;
  1064. }
  1065. }
  1066. return true;
  1067. }
  1068. function ArrayMap(f, receiver) {
  1069. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.map");
  1070. // Pull out the length so that modifications to the length in the
  1071. // loop will not affect the looping and side effects are visible.
  1072. var array = ToObject(this);
  1073. var length = TO_UINT32(array.length);
  1074. if (!IS_SPEC_FUNCTION(f)) {
  1075. throw MakeTypeError('called_non_callable', [ f ]);
  1076. }
  1077. if (IS_NULL_OR_UNDEFINED(receiver)) {
  1078. receiver = %GetDefaultReceiver(f) || receiver;
  1079. } else if (!IS_SPEC_OBJECT(receiver) && %IsSloppyModeFunction(f)) {
  1080. receiver = ToObject(receiver);
  1081. }
  1082. var result = new $Array();
  1083. var accumulator = new InternalArray(length);
  1084. var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
  1085. for (var i = 0; i < length; i++) {
  1086. if (i in array) {
  1087. var element = array[i];
  1088. // Prepare break slots for debugger step in.
  1089. if (stepping) %DebugPrepareStepInIfStepping(f);
  1090. accumulator[i] = %_CallFunction(receiver, element, i, array, f);
  1091. }
  1092. }
  1093. %MoveArrayContents(accumulator, result);
  1094. return result;
  1095. }
  1096. function ArrayIndexOf(element, index) {
  1097. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.indexOf");
  1098. var length = TO_UINT32(this.length);
  1099. if (length == 0) return -1;
  1100. if (IS_UNDEFINED(index)) {
  1101. index = 0;
  1102. } else {
  1103. index = TO_INTEGER(index);
  1104. // If index is negative, index from the end of the array.
  1105. if (index < 0) {
  1106. index = length + index;
  1107. // If index is still negative, search the entire array.
  1108. if (index < 0) index = 0;
  1109. }
  1110. }
  1111. var min = index;
  1112. var max = length;
  1113. if (UseSparseVariant(this, length, IS_ARRAY(this), max - min)) {
  1114. %NormalizeElements(this);
  1115. var indices = %GetArrayKeys(this, length);
  1116. if (IS_NUMBER(indices)) {
  1117. // It's an interval.
  1118. max = indices; // Capped by length already.
  1119. // Fall through to loop below.
  1120. } else {
  1121. if (indices.length == 0) return -1;
  1122. // Get all the keys in sorted order.
  1123. var sortedKeys = GetSortedArrayKeys(this, indices);
  1124. var n = sortedKeys.length;
  1125. var i = 0;
  1126. while (i < n && sortedKeys[i] < index) i++;
  1127. while (i < n) {
  1128. var key = sortedKeys[i];
  1129. if (!IS_UNDEFINED(key) && this[key] === element) return key;
  1130. i++;
  1131. }
  1132. return -1;
  1133. }
  1134. }
  1135. // Lookup through the array.
  1136. if (!IS_UNDEFINED(element)) {
  1137. for (var i = min; i < max; i++) {
  1138. if (this[i] === element) return i;
  1139. }
  1140. return -1;
  1141. }
  1142. // Lookup through the array.
  1143. for (var i = min; i < max; i++) {
  1144. if (IS_UNDEFINED(this[i]) && i in this) {
  1145. return i;
  1146. }
  1147. }
  1148. return -1;
  1149. }
  1150. function ArrayLastIndexOf(element, index) {
  1151. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.lastIndexOf");
  1152. var length = TO_UINT32(this.length);
  1153. if (length == 0) return -1;
  1154. if (%_ArgumentsLength() < 2) {
  1155. index = length - 1;
  1156. } else {
  1157. index = TO_INTEGER(index);
  1158. // If index is negative, index from end of the array.
  1159. if (index < 0) index += length;
  1160. // If index is still negative, do not search the array.
  1161. if (index < 0) return -1;
  1162. else if (index >= length) index = length - 1;
  1163. }
  1164. var min = 0;
  1165. var max = index;
  1166. if (UseSparseVariant(this, length, IS_ARRAY(this), index)) {
  1167. %NormalizeElements(this);
  1168. var indices = %GetArrayKeys(this, index + 1);
  1169. if (IS_NUMBER(indices)) {
  1170. // It's an interval.
  1171. max = indices; // Capped by index already.
  1172. // Fall through to loop below.
  1173. } else {
  1174. if (indices.length == 0) return -1;
  1175. // Get all the keys in sorted order.
  1176. var sortedKeys = GetSortedArrayKeys(this, indices);
  1177. var i = sortedKeys.length - 1;
  1178. while (i >= 0) {
  1179. var key = sortedKeys[i];
  1180. if (!IS_UNDEFINED(key) && this[key] === element) return key;
  1181. i--;
  1182. }
  1183. return -1;
  1184. }
  1185. }
  1186. // Lookup through the array.
  1187. if (!IS_UNDEFINED(element)) {
  1188. for (var i = max; i >= min; i--) {
  1189. if (this[i] === element) return i;
  1190. }
  1191. return -1;
  1192. }
  1193. for (var i = max; i >= min; i--) {
  1194. if (IS_UNDEFINED(this[i]) && i in this) {
  1195. return i;
  1196. }
  1197. }
  1198. return -1;
  1199. }
  1200. function ArrayReduce(callback, current) {
  1201. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduce");
  1202. // Pull out the length so that modifications to the length in the
  1203. // loop will not affect the looping and side effects are visible.
  1204. var array = ToObject(this);
  1205. var length = ToUint32(array.length);
  1206. if (!IS_SPEC_FUNCTION(callback)) {
  1207. throw MakeTypeError('called_non_callable', [callback]);
  1208. }
  1209. var i = 0;
  1210. find_initial: if (%_ArgumentsLength() < 2) {
  1211. for (; i < length; i++) {
  1212. current = array[i];
  1213. if (!IS_UNDEFINED(current) || i in array) {
  1214. i++;
  1215. break find_initial;
  1216. }
  1217. }
  1218. throw MakeTypeError('reduce_no_initial', []);
  1219. }
  1220. var receiver = %GetDefaultReceiver(callback);
  1221. var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(callback);
  1222. for (; i < length; i++) {
  1223. if (i in array) {
  1224. var element = array[i];
  1225. // Prepare break slots for debugger step in.
  1226. if (stepping) %DebugPrepareStepInIfStepping(callback);
  1227. current = %_CallFunction(receiver, current, element, i, array, callback);
  1228. }
  1229. }
  1230. return current;
  1231. }
  1232. function ArrayReduceRight(callback, current) {
  1233. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduceRight");
  1234. // Pull out the length so that side effects are visible before the
  1235. // callback function is checked.
  1236. var array = ToObject(this);
  1237. var length = ToUint32(array.length);
  1238. if (!IS_SPEC_FUNCTION(callback)) {
  1239. throw MakeTypeError('called_non_callable', [callback]);
  1240. }
  1241. var i = length - 1;
  1242. find_initial: if (%_ArgumentsLength() < 2) {
  1243. for (; i >= 0; i--) {
  1244. current = array[i];
  1245. if (!IS_UNDEFINED(current) || i in array) {
  1246. i--;
  1247. break find_initial;
  1248. }
  1249. }
  1250. throw MakeTypeError('reduce_no_initial', []);
  1251. }
  1252. var receiver = %GetDefaultReceiver(callback);
  1253. var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(callback);
  1254. for (; i >= 0; i--) {
  1255. if (i in array) {
  1256. var element = array[i];
  1257. // Prepare break slots for debugger step in.
  1258. if (stepping) %DebugPrepareStepInIfStepping(callback);
  1259. current = %_CallFunction(receiver, current, element, i, array, callback);
  1260. }
  1261. }
  1262. return current;
  1263. }
  1264. // ES5, 15.4.3.2
  1265. function ArrayIsArray(obj) {
  1266. return IS_ARRAY(obj);
  1267. }
  1268. // -------------------------------------------------------------------
  1269. function SetUpArray() {
  1270. %CheckIsBootstrapping();
  1271. // Set up non-enumerable constructor property on the Array.prototype
  1272. // object.
  1273. %AddNamedProperty($Array.prototype, "constructor", $Array, DONT_ENUM);
  1274. // Set up unscopable properties on the Array.prototype object.
  1275. var unscopables = {
  1276. __proto__: null,
  1277. copyWithin: true,
  1278. entries: true,
  1279. fill: true,
  1280. find: true,
  1281. findIndex: true,
  1282. keys: true,
  1283. values: true,
  1284. };
  1285. %AddNamedProperty($Array.prototype, symbolUnscopables, unscopables,
  1286. DONT_ENUM | READ_ONLY);
  1287. // Set up non-enumerable functions on the Array object.
  1288. InstallFunctions($Array, DONT_ENUM, $Array(
  1289. "isArray", ArrayIsArray
  1290. ));
  1291. var specialFunctions = %SpecialArrayFunctions();
  1292. var getFunction = function(name, jsBuiltin, len) {
  1293. var f = jsBuiltin;
  1294. if (specialFunctions.hasOwnProperty(name)) {
  1295. f = specialFunctions[name];
  1296. }
  1297. if (!IS_UNDEFINED(len)) {
  1298. %FunctionSetLength(f, len);
  1299. }
  1300. return f;
  1301. };
  1302. // Set up non-enumerable functions of the Array.prototype object and
  1303. // set their names.
  1304. // Manipulate the length of some of the functions to meet
  1305. // expectations set by ECMA-262 or Mozilla.
  1306. InstallFunctions($Array.prototype, DONT_ENUM, $Array(
  1307. "toString", getFunction("toString", ArrayToString),
  1308. "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
  1309. "join", getFunction("join", ArrayJoin),
  1310. "pop", getFunction("pop", ArrayPop),
  1311. "push", getFunction("push", ArrayPush, 1),
  1312. "concat", getFunction("concat", ArrayConcatJS, 1),
  1313. "reverse", getFunction("reverse", ArrayReverse),
  1314. "shift", getFunction("shift", ArrayShift),
  1315. "unshift", getFunction("unshift", ArrayUnshift, 1),
  1316. "slice", getFunction("slice", ArraySlice, 2),
  1317. "splice", getFunction("splice", ArraySplice, 2),
  1318. "sort", getFunction("sort", ArraySort),
  1319. "filter", getFunction("filter", ArrayFilter, 1),
  1320. "forEach", getFunction("forEach", ArrayForEach, 1),
  1321. "some", getFunction("some", ArraySome, 1),
  1322. "every", getFunction("every", ArrayEvery, 1),
  1323. "map", getFunction("map", ArrayMap, 1),
  1324. "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
  1325. "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
  1326. "reduce", getFunction("reduce", ArrayReduce, 1),
  1327. "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1)
  1328. ));
  1329. %FinishArrayPrototypeSetup($Array.prototype);
  1330. // The internal Array prototype doesn't need to be fancy, since it's never
  1331. // exposed to user code.
  1332. // Adding only the functions that are actually used.
  1333. SetUpLockedPrototype(InternalArray, $Array(), $Array(
  1334. "concat", getFunction("concat", ArrayConcatJS),
  1335. "indexOf", getFunction("indexOf", ArrayIndexOf),
  1336. "join", getFunction("join", ArrayJoin),
  1337. "pop", getFunction("pop", ArrayPop),
  1338. "push", getFunction("push", ArrayPush),
  1339. "splice", getFunction("splice", ArraySplice)
  1340. ));
  1341. SetUpLockedPrototype(InternalPackedArray, $Array(), $Array(
  1342. "join", getFunction("join", ArrayJoin),
  1343. "pop", getFunction("pop", ArrayPop),
  1344. "push", getFunction("push", ArrayPush)
  1345. ));
  1346. }
  1347. SetUpArray();