PageRenderTime 111ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/deps/v8/src/array.js

http://github.com/joyent/node
JavaScript | 1665 lines | 1246 code | 210 blank | 209 comment | 405 complexity | f9319307ba560ce4b3195b4a3c2be2e5 MD5 | raw file
Possible License(s): 0BSD, BSD-3-Clause, MPL-2.0-no-copyleft-exception, GPL-2.0, ISC, Apache-2.0, MIT, AGPL-3.0

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file