| OLD | NEW |
| 1 // Copyright 2006-2008 the V8 project authors. All rights reserved. | 1 // Copyright 2006-2008 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 125 if (i != 0) builder.add(separator); | 125 if (i != 0) builder.add(separator); |
| 126 if (!IS_UNDEFINED(e) || (i in array)) { | 126 if (!IS_UNDEFINED(e) || (i in array)) { |
| 127 builder.add(convert(e)); | 127 builder.add(convert(e)); |
| 128 } | 128 } |
| 129 } | 129 } |
| 130 return builder.generate(); | 130 return builder.generate(); |
| 131 } finally { | 131 } finally { |
| 132 // Make sure to pop the visited array no matter what happens. | 132 // Make sure to pop the visited array no matter what happens. |
| 133 if (is_array) visited_arrays.pop(); | 133 if (is_array) visited_arrays.pop(); |
| 134 } | 134 } |
| 135 }; | 135 } |
| 136 | 136 |
| 137 | 137 |
| 138 function ConvertToString(e) { | 138 function ConvertToString(e) { |
| 139 if (e == null) return ''; | 139 if (e == null) return ''; |
| 140 else return ToString(e); | 140 else return ToString(e); |
| 141 }; | 141 } |
| 142 | 142 |
| 143 | 143 |
| 144 function ConvertToLocaleString(e) { | 144 function ConvertToLocaleString(e) { |
| 145 if (e == null) return ''; | 145 if (e == null) return ''; |
| 146 else { | 146 else { |
| 147 // e_obj's toLocaleString might be overwritten, check if it is a function. | 147 // e_obj's toLocaleString might be overwritten, check if it is a function. |
| 148 // Call ToString if toLocaleString is not a function. | 148 // Call ToString if toLocaleString is not a function. |
| 149 // See issue 877615. | 149 // See issue 877615. |
| 150 var e_obj = ToObject(e); | 150 var e_obj = ToObject(e); |
| 151 if (IS_FUNCTION(e_obj.toLocaleString)) | 151 if (IS_FUNCTION(e_obj.toLocaleString)) |
| 152 return e_obj.toLocaleString(); | 152 return e_obj.toLocaleString(); |
| 153 else | 153 else |
| 154 return ToString(e); | 154 return ToString(e); |
| 155 } | 155 } |
| 156 }; | 156 } |
| 157 | 157 |
| 158 | 158 |
| 159 // This function implements the optimized splice implementation that can use | 159 // This function implements the optimized splice implementation that can use |
| 160 // special array operations to handle sparse arrays in a sensible fashion. | 160 // special array operations to handle sparse arrays in a sensible fashion. |
| 161 function SmartSlice(array, start_i, del_count, len, deleted_elements) { | 161 function SmartSlice(array, start_i, del_count, len, deleted_elements) { |
| 162 // Move deleted elements to a new array (the return value from splice). | 162 // Move deleted elements to a new array (the return value from splice). |
| 163 // Intervals array can contain keys and intervals. See comment in Concat. | 163 // Intervals array can contain keys and intervals. See comment in Concat. |
| 164 var intervals = %GetArrayKeys(array, start_i + del_count); | 164 var intervals = %GetArrayKeys(array, start_i + del_count); |
| 165 var length = intervals.length; | 165 var length = intervals.length; |
| 166 for (var k = 0; k < length; k++) { | 166 for (var k = 0; k < length; k++) { |
| (...skipping 22 matching lines...) Expand all Loading... |
| 189 // appropriate test. We follow KJS in consulting the | 189 // appropriate test. We follow KJS in consulting the |
| 190 // prototype. | 190 // prototype. |
| 191 var current = array[key]; | 191 var current = array[key]; |
| 192 if (!IS_UNDEFINED(current) || key in array) { | 192 if (!IS_UNDEFINED(current) || key in array) { |
| 193 deleted_elements[key - start_i] = current; | 193 deleted_elements[key - start_i] = current; |
| 194 } | 194 } |
| 195 } | 195 } |
| 196 } | 196 } |
| 197 } | 197 } |
| 198 } | 198 } |
| 199 }; | 199 } |
| 200 | 200 |
| 201 | 201 |
| 202 // This function implements the optimized splice implementation that can use | 202 // This function implements the optimized splice implementation that can use |
| 203 // special array operations to handle sparse arrays in a sensible fashion. | 203 // special array operations to handle sparse arrays in a sensible fashion. |
| 204 function SmartMove(array, start_i, del_count, len, num_additional_args) { | 204 function SmartMove(array, start_i, del_count, len, num_additional_args) { |
| 205 // Move data to new array. | 205 // Move data to new array. |
| 206 var new_array = new $Array(len - del_count + num_additional_args); | 206 var new_array = new $Array(len - del_count + num_additional_args); |
| 207 var intervals = %GetArrayKeys(array, len); | 207 var intervals = %GetArrayKeys(array, len); |
| 208 var length = intervals.length; | 208 var length = intervals.length; |
| 209 for (var k = 0; k < length; k++) { | 209 for (var k = 0; k < length; k++) { |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 251 var current = array[key]; | 251 var current = array[key]; |
| 252 if (!IS_UNDEFINED(current) || key in array) { | 252 if (!IS_UNDEFINED(current) || key in array) { |
| 253 new_array[key - del_count + num_additional_args] = current; | 253 new_array[key - del_count + num_additional_args] = current; |
| 254 } | 254 } |
| 255 } | 255 } |
| 256 } | 256 } |
| 257 } | 257 } |
| 258 } | 258 } |
| 259 // Move contents of new_array into this array | 259 // Move contents of new_array into this array |
| 260 %MoveArrayContents(new_array, array); | 260 %MoveArrayContents(new_array, array); |
| 261 }; | 261 } |
| 262 | 262 |
| 263 | 263 |
| 264 // This is part of the old simple-minded splice. We are using it either | 264 // This is part of the old simple-minded splice. We are using it either |
| 265 // because the receiver is not an array (so we have no choice) or because we | 265 // because the receiver is not an array (so we have no choice) or because we |
| 266 // know we are not deleting or moving a lot of elements. | 266 // know we are not deleting or moving a lot of elements. |
| 267 function SimpleSlice(array, start_i, del_count, len, deleted_elements) { | 267 function SimpleSlice(array, start_i, del_count, len, deleted_elements) { |
| 268 for (var i = 0; i < del_count; i++) { | 268 for (var i = 0; i < del_count; i++) { |
| 269 var index = start_i + i; | 269 var index = start_i + i; |
| 270 // The spec could also be interpreted such that %HasLocalProperty | 270 // The spec could also be interpreted such that %HasLocalProperty |
| 271 // would be the appropriate test. We follow KJS in consulting the | 271 // would be the appropriate test. We follow KJS in consulting the |
| 272 // prototype. | 272 // prototype. |
| 273 var current = array[index]; | 273 var current = array[index]; |
| 274 if (!IS_UNDEFINED(current) || index in array) | 274 if (!IS_UNDEFINED(current) || index in array) |
| 275 deleted_elements[i] = current; | 275 deleted_elements[i] = current; |
| 276 } | 276 } |
| 277 }; | 277 } |
| 278 | 278 |
| 279 | 279 |
| 280 function SimpleMove(array, start_i, del_count, len, num_additional_args) { | 280 function SimpleMove(array, start_i, del_count, len, num_additional_args) { |
| 281 if (num_additional_args !== del_count) { | 281 if (num_additional_args !== del_count) { |
| 282 // Move the existing elements after the elements to be deleted | 282 // Move the existing elements after the elements to be deleted |
| 283 // to the right position in the resulting array. | 283 // to the right position in the resulting array. |
| 284 if (num_additional_args > del_count) { | 284 if (num_additional_args > del_count) { |
| 285 for (var i = len - del_count; i > start_i; i--) { | 285 for (var i = len - del_count; i > start_i; i--) { |
| 286 var from_index = i + del_count - 1; | 286 var from_index = i + del_count - 1; |
| 287 var to_index = i + num_additional_args - 1; | 287 var to_index = i + num_additional_args - 1; |
| (...skipping 19 matching lines...) Expand all Loading... |
| 307 array[to_index] = current; | 307 array[to_index] = current; |
| 308 } else { | 308 } else { |
| 309 delete array[to_index]; | 309 delete array[to_index]; |
| 310 } | 310 } |
| 311 } | 311 } |
| 312 for (var i = len; i > len - del_count + num_additional_args; i--) { | 312 for (var i = len; i > len - del_count + num_additional_args; i--) { |
| 313 delete array[i - 1]; | 313 delete array[i - 1]; |
| 314 } | 314 } |
| 315 } | 315 } |
| 316 } | 316 } |
| 317 }; | 317 } |
| 318 | 318 |
| 319 | 319 |
| 320 // ------------------------------------------------------------------- | 320 // ------------------------------------------------------------------- |
| 321 | 321 |
| 322 | 322 |
| 323 function ArrayToString() { | 323 function ArrayToString() { |
| 324 if (!IS_ARRAY(this)) { | 324 if (!IS_ARRAY(this)) { |
| 325 throw new $TypeError('Array.prototype.toString is not generic'); | 325 throw new $TypeError('Array.prototype.toString is not generic'); |
| 326 } | 326 } |
| 327 return Join(this, this.length, ',', ConvertToString); | 327 return Join(this, this.length, ',', ConvertToString); |
| 328 }; | 328 } |
| 329 | 329 |
| 330 | 330 |
| 331 function ArrayToLocaleString() { | 331 function ArrayToLocaleString() { |
| 332 if (!IS_ARRAY(this)) { | 332 if (!IS_ARRAY(this)) { |
| 333 throw new $TypeError('Array.prototype.toString is not generic'); | 333 throw new $TypeError('Array.prototype.toString is not generic'); |
| 334 } | 334 } |
| 335 return Join(this, this.length, ',', ConvertToLocaleString); | 335 return Join(this, this.length, ',', ConvertToLocaleString); |
| 336 }; | 336 } |
| 337 | 337 |
| 338 | 338 |
| 339 function ArrayJoin(separator) { | 339 function ArrayJoin(separator) { |
| 340 if (IS_UNDEFINED(separator)) separator = ','; | 340 if (IS_UNDEFINED(separator)) separator = ','; |
| 341 else separator = ToString(separator); | 341 else separator = ToString(separator); |
| 342 return Join(this, ToUint32(this.length), separator, ConvertToString); | 342 return Join(this, ToUint32(this.length), separator, ConvertToString); |
| 343 }; | 343 } |
| 344 | 344 |
| 345 | 345 |
| 346 // Removes the last element from the array and returns it. See | 346 // Removes the last element from the array and returns it. See |
| 347 // ECMA-262, section 15.4.4.6. | 347 // ECMA-262, section 15.4.4.6. |
| 348 function ArrayPop() { | 348 function ArrayPop() { |
| 349 var n = ToUint32(this.length); | 349 var n = ToUint32(this.length); |
| 350 if (n == 0) { | 350 if (n == 0) { |
| 351 this.length = n; | 351 this.length = n; |
| 352 return; | 352 return; |
| 353 } | 353 } |
| 354 n--; | 354 n--; |
| 355 var value = this[n]; | 355 var value = this[n]; |
| 356 this.length = n; | 356 this.length = n; |
| 357 delete this[n]; | 357 delete this[n]; |
| 358 return value; | 358 return value; |
| 359 }; | 359 } |
| 360 | 360 |
| 361 | 361 |
| 362 // Appends the arguments to the end of the array and returns the new | 362 // Appends the arguments to the end of the array and returns the new |
| 363 // length of the array. See ECMA-262, section 15.4.4.7. | 363 // length of the array. See ECMA-262, section 15.4.4.7. |
| 364 function ArrayPush() { | 364 function ArrayPush() { |
| 365 var n = ToUint32(this.length); | 365 var n = ToUint32(this.length); |
| 366 var m = %_ArgumentsLength(); | 366 var m = %_ArgumentsLength(); |
| 367 for (var i = 0; i < m; i++) { | 367 for (var i = 0; i < m; i++) { |
| 368 this[i+n] = %_Arguments(i); | 368 this[i+n] = %_Arguments(i); |
| 369 } | 369 } |
| 370 this.length = n + m; | 370 this.length = n + m; |
| 371 return this.length; | 371 return this.length; |
| 372 }; | 372 } |
| 373 | 373 |
| 374 | 374 |
| 375 function ArrayConcat(arg1) { // length == 1 | 375 function ArrayConcat(arg1) { // length == 1 |
| 376 var arg_number = 0, arg_count = %_ArgumentsLength(); | 376 var arg_number = 0, arg_count = %_ArgumentsLength(); |
| 377 var n = 0; | 377 var n = 0; |
| 378 | 378 |
| 379 var A = $Array(1 + arg_count); | 379 var A = $Array(1 + arg_count); |
| 380 var E = this; | 380 var E = this; |
| 381 | 381 |
| 382 while (true) { | 382 while (true) { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 408 n += E.length; | 408 n += E.length; |
| 409 } else { | 409 } else { |
| 410 A[n++] = E; | 410 A[n++] = E; |
| 411 } | 411 } |
| 412 if (arg_number == arg_count) break; | 412 if (arg_number == arg_count) break; |
| 413 E = %_Arguments(arg_number++); | 413 E = %_Arguments(arg_number++); |
| 414 } | 414 } |
| 415 | 415 |
| 416 A.length = n; // may contain empty arrays | 416 A.length = n; // may contain empty arrays |
| 417 return A; | 417 return A; |
| 418 }; | 418 } |
| 419 | 419 |
| 420 | 420 |
| 421 // For implementing reverse() on large, sparse arrays. | 421 // For implementing reverse() on large, sparse arrays. |
| 422 function SparseReverse(array, len) { | 422 function SparseReverse(array, len) { |
| 423 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len)); | 423 var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len)); |
| 424 var high_counter = keys.length - 1; | 424 var high_counter = keys.length - 1; |
| 425 var low_counter = 0; | 425 var low_counter = 0; |
| 426 while (low_counter <= high_counter) { | 426 while (low_counter <= high_counter) { |
| 427 var i = keys[low_counter]; | 427 var i = keys[low_counter]; |
| 428 var j = keys[high_counter]; | 428 var j = keys[high_counter]; |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 483 } | 483 } |
| 484 } else { | 484 } else { |
| 485 var current_j = this[j]; | 485 var current_j = this[j]; |
| 486 if (!IS_UNDEFINED(current_j) || j in this) { | 486 if (!IS_UNDEFINED(current_j) || j in this) { |
| 487 this[i] = current_j; | 487 this[i] = current_j; |
| 488 delete this[j]; | 488 delete this[j]; |
| 489 } | 489 } |
| 490 } | 490 } |
| 491 } | 491 } |
| 492 return this; | 492 return this; |
| 493 }; | 493 } |
| 494 | 494 |
| 495 | 495 |
| 496 function ArrayShift() { | 496 function ArrayShift() { |
| 497 var len = ToUint32(this.length); | 497 var len = ToUint32(this.length); |
| 498 | 498 |
| 499 if (len === 0) { | 499 if (len === 0) { |
| 500 this.length = 0; | 500 this.length = 0; |
| 501 return; | 501 return; |
| 502 } | 502 } |
| 503 | 503 |
| 504 var first = this[0]; | 504 var first = this[0]; |
| 505 | 505 |
| 506 if (IS_ARRAY(this)) | 506 if (IS_ARRAY(this)) |
| 507 SmartMove(this, 0, 1, len, 0); | 507 SmartMove(this, 0, 1, len, 0); |
| 508 else | 508 else |
| 509 SimpleMove(this, 0, 1, len, 0); | 509 SimpleMove(this, 0, 1, len, 0); |
| 510 | 510 |
| 511 this.length = len - 1; | 511 this.length = len - 1; |
| 512 | 512 |
| 513 return first; | 513 return first; |
| 514 }; | 514 } |
| 515 | 515 |
| 516 | 516 |
| 517 function ArrayUnshift(arg1) { // length == 1 | 517 function ArrayUnshift(arg1) { // length == 1 |
| 518 var len = ToUint32(this.length); | 518 var len = ToUint32(this.length); |
| 519 var num_arguments = %_ArgumentsLength(); | 519 var num_arguments = %_ArgumentsLength(); |
| 520 | 520 |
| 521 if (IS_ARRAY(this)) | 521 if (IS_ARRAY(this)) |
| 522 SmartMove(this, 0, 0, len, num_arguments); | 522 SmartMove(this, 0, 0, len, num_arguments); |
| 523 else | 523 else |
| 524 SimpleMove(this, 0, 0, len, num_arguments); | 524 SimpleMove(this, 0, 0, len, num_arguments); |
| 525 | 525 |
| 526 for (var i = 0; i < num_arguments; i++) { | 526 for (var i = 0; i < num_arguments; i++) { |
| 527 this[i] = %_Arguments(i); | 527 this[i] = %_Arguments(i); |
| 528 } | 528 } |
| 529 | 529 |
| 530 this.length = len + num_arguments; | 530 this.length = len + num_arguments; |
| 531 | 531 |
| 532 return len + num_arguments; | 532 return len + num_arguments; |
| 533 }; | 533 } |
| 534 | 534 |
| 535 | 535 |
| 536 function ArraySlice(start, end) { | 536 function ArraySlice(start, end) { |
| 537 var len = ToUint32(this.length); | 537 var len = ToUint32(this.length); |
| 538 var start_i = TO_INTEGER(start); | 538 var start_i = TO_INTEGER(start); |
| 539 var end_i = len; | 539 var end_i = len; |
| 540 | 540 |
| 541 if (end !== void 0) end_i = TO_INTEGER(end); | 541 if (end !== void 0) end_i = TO_INTEGER(end); |
| 542 | 542 |
| 543 if (start_i < 0) { | 543 if (start_i < 0) { |
| 544 start_i += len; | 544 start_i += len; |
| 545 if (start_i < 0) start_i = 0; | 545 if (start_i < 0) start_i = 0; |
| 546 } else { | 546 } else { |
| 547 if (start_i > len) start_i = len; | 547 if (start_i > len) start_i = len; |
| 548 } | 548 } |
| 549 | 549 |
| 550 if (end_i < 0) { | 550 if (end_i < 0) { |
| 551 end_i += len; | 551 end_i += len; |
| 552 if (end_i < 0) end_i = 0; | 552 if (end_i < 0) end_i = 0; |
| 553 } else { | 553 } else { |
| 554 if (end_i > len) end_i = len; | 554 if (end_i > len) end_i = len; |
| 555 } | 555 } |
| 556 | 556 |
| 557 var result = []; | 557 var result = []; |
| 558 | 558 |
| 559 if (end_i < start_i) | 559 if (end_i < start_i) return result; |
| 560 return result; | 560 |
| 561 | 561 if (IS_ARRAY(this)) { |
| 562 if (IS_ARRAY(this)) | |
| 563 SmartSlice(this, start_i, end_i - start_i, len, result); | 562 SmartSlice(this, start_i, end_i - start_i, len, result); |
| 564 else | 563 } else { |
| 565 SimpleSlice(this, start_i, end_i - start_i, len, result); | 564 SimpleSlice(this, start_i, end_i - start_i, len, result); |
| 566 | 565 } |
| 566 |
| 567 result.length = end_i - start_i; | 567 result.length = end_i - start_i; |
| 568 | 568 |
| 569 return result; | 569 return result; |
| 570 }; | 570 } |
| 571 | 571 |
| 572 | 572 |
| 573 function ArraySplice(start, delete_count) { | 573 function ArraySplice(start, delete_count) { |
| 574 var num_arguments = %_ArgumentsLength(); | 574 var num_arguments = %_ArgumentsLength(); |
| 575 | 575 |
| 576 // SpiderMonkey and KJS return undefined in the case where no | 576 // SpiderMonkey and KJS return undefined in the case where no |
| 577 // arguments are given instead of using the implicit undefined | 577 // arguments are given instead of using the implicit undefined |
| 578 // arguments. This does not follow ECMA-262, but we do the same for | 578 // arguments. This does not follow ECMA-262, but we do the same for |
| 579 // compatibility. | 579 // compatibility. |
| 580 if (num_arguments == 0) return; | 580 if (num_arguments == 0) return; |
| 581 | 581 |
| 582 var len = ToUint32(this.length); | 582 var len = ToUint32(this.length); |
| 583 var start_i = TO_INTEGER(start); | 583 var start_i = TO_INTEGER(start); |
| 584 | 584 |
| 585 if (start_i < 0) { | 585 if (start_i < 0) { |
| 586 start_i += len; | 586 start_i += len; |
| 587 if (start_i < 0) start_i = 0; | 587 if (start_i < 0) start_i = 0; |
| 588 } else { | 588 } else { |
| 589 if (start_i > len) start_i = len; | 589 if (start_i > len) start_i = len; |
| 590 } | 590 } |
| 591 | 591 |
| 592 // SpiderMonkey and KJS treat the case where no delete count is | 592 // SpiderMonkey and KJS treat the case where no delete count is |
| 593 // given differently from when an undefined delete count is given. | 593 // given differently from when an undefined delete count is given. |
| 594 // This does not follow ECMA-262, but we do the same for | 594 // This does not follow ECMA-262, but we do the same for |
| 595 // compatibility. | 595 // compatibility. |
| 596 var del_count = 0; | 596 var del_count = 0; |
| 597 if (num_arguments > 1) { | 597 if (num_arguments > 1) { |
| 598 del_count = TO_INTEGER(delete_count); | 598 del_count = TO_INTEGER(delete_count); |
| 599 if (del_count < 0) del_count = 0; | 599 if (del_count < 0) del_count = 0; |
| 600 if (del_count > len - start_i) del_count = len - start_i; | 600 if (del_count > len - start_i) del_count = len - start_i; |
| 601 } else { | 601 } else { |
| 602 del_count = len - start_i; | 602 del_count = len - start_i; |
| 603 } | 603 } |
| 604 | 604 |
| 605 var deleted_elements = []; | 605 var deleted_elements = []; |
| 606 deleted_elements.length = del_count; | 606 deleted_elements.length = del_count; |
| 607 | 607 |
| 608 // Number of elements to add. | 608 // Number of elements to add. |
| 609 var num_additional_args = 0; | 609 var num_additional_args = 0; |
| 610 if (num_arguments > 2) { | 610 if (num_arguments > 2) { |
| 611 num_additional_args = num_arguments - 2; | 611 num_additional_args = num_arguments - 2; |
| 612 } | 612 } |
| 613 | 613 |
| 614 var use_simple_splice = true; | 614 var use_simple_splice = true; |
| 615 | 615 |
| 616 if (IS_ARRAY(this) && num_additional_args !== del_count) { | 616 if (IS_ARRAY(this) && num_additional_args !== del_count) { |
| 617 // If we are only deleting/moving a few things near the end of the | 617 // If we are only deleting/moving a few things near the end of the |
| 618 // array then the simple version is going to be faster, because it | 618 // array then the simple version is going to be faster, because it |
| 619 // doesn't touch most of the array. | 619 // doesn't touch most of the array. |
| 620 var estimated_non_hole_elements = %EstimateNumberOfElements(this); | 620 var estimated_non_hole_elements = %EstimateNumberOfElements(this); |
| 621 if (len > 20 && (estimated_non_hole_elements >> 2) < (len - start_i)) { | 621 if (len > 20 && (estimated_non_hole_elements >> 2) < (len - start_i)) { |
| 622 use_simple_splice = false; | 622 use_simple_splice = false; |
| 623 } | 623 } |
| 624 } | 624 } |
| 625 | 625 |
| 626 if (use_simple_splice) { | 626 if (use_simple_splice) { |
| 627 SimpleSlice(this, start_i, del_count, len, deleted_elements); | 627 SimpleSlice(this, start_i, del_count, len, deleted_elements); |
| 628 SimpleMove(this, start_i, del_count, len, num_additional_args); | 628 SimpleMove(this, start_i, del_count, len, num_additional_args); |
| 629 } else { | 629 } else { |
| 630 SmartSlice(this, start_i, del_count, len, deleted_elements); | 630 SmartSlice(this, start_i, del_count, len, deleted_elements); |
| 631 SmartMove(this, start_i, del_count, len, num_additional_args); | 631 SmartMove(this, start_i, del_count, len, num_additional_args); |
| 632 } | 632 } |
| 633 | 633 |
| 634 // Insert the arguments into the resulting array in | 634 // Insert the arguments into the resulting array in |
| 635 // place of the deleted elements. | 635 // place of the deleted elements. |
| 636 var i = start_i; | 636 var i = start_i; |
| 637 var arguments_index = 2; | 637 var arguments_index = 2; |
| 638 var arguments_length = %_ArgumentsLength(); | 638 var arguments_length = %_ArgumentsLength(); |
| 639 while (arguments_index < arguments_length) { | 639 while (arguments_index < arguments_length) { |
| 640 this[i++] = %_Arguments(arguments_index++); | 640 this[i++] = %_Arguments(arguments_index++); |
| 641 } | 641 } |
| 642 this.length = len - del_count + num_additional_args; | 642 this.length = len - del_count + num_additional_args; |
| 643 | 643 |
| 644 // Return the deleted elements. | 644 // Return the deleted elements. |
| 645 return deleted_elements; | 645 return deleted_elements; |
| 646 }; | 646 } |
| 647 | 647 |
| 648 | 648 |
| 649 function ArraySort(comparefn) { | 649 function ArraySort(comparefn) { |
| 650 // In-place QuickSort algorithm. | 650 // In-place QuickSort algorithm. |
| 651 // For short (length <= 22) arrays, insertion sort is used for efficiency. | 651 // For short (length <= 22) arrays, insertion sort is used for efficiency. |
| 652 | 652 |
| 653 var custom_compare = IS_FUNCTION(comparefn); | 653 var custom_compare = IS_FUNCTION(comparefn); |
| 654 | 654 |
| 655 function Compare(x,y) { | 655 function Compare(x,y) { |
| 656 if (custom_compare) { | 656 if (custom_compare) { |
| 657 return comparefn.call(null, x, y); | 657 return comparefn.call(null, x, y); |
| 658 } | 658 } |
| 659 if (%_IsSmi(x) && %_IsSmi(y)) { | 659 if (%_IsSmi(x) && %_IsSmi(y)) { |
| 660 return %SmiLexicographicCompare(x, y); | 660 return %SmiLexicographicCompare(x, y); |
| 661 } | 661 } |
| 662 x = ToString(x); | 662 x = ToString(x); |
| 663 y = ToString(y); | 663 y = ToString(y); |
| 664 if (x == y) return 0; | 664 if (x == y) return 0; |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 753 | 753 |
| 754 // We only changed the length of the this object (in | 754 // We only changed the length of the this object (in |
| 755 // RemoveArrayHoles) if it was an array. We are not allowed to set | 755 // RemoveArrayHoles) if it was an array. We are not allowed to set |
| 756 // the length of the this object if it is not an array because this | 756 // the length of the this object if it is not an array because this |
| 757 // might introduce a new length property. | 757 // might introduce a new length property. |
| 758 if (IS_ARRAY(this)) { | 758 if (IS_ARRAY(this)) { |
| 759 this.length = old_length; | 759 this.length = old_length; |
| 760 } | 760 } |
| 761 | 761 |
| 762 return this; | 762 return this; |
| 763 }; | 763 } |
| 764 | 764 |
| 765 | 765 |
| 766 // The following functions cannot be made efficient on sparse arrays while | 766 // The following functions cannot be made efficient on sparse arrays while |
| 767 // preserving the semantics, since the calls to the receiver function can add | 767 // preserving the semantics, since the calls to the receiver function can add |
| 768 // or delete elements from the array. | 768 // or delete elements from the array. |
| 769 | |
| 770 function ArrayFilter(f, receiver) { | 769 function ArrayFilter(f, receiver) { |
| 771 if (!IS_FUNCTION(f)) { | 770 if (!IS_FUNCTION(f)) { |
| 772 throw MakeTypeError('called_non_callable', [ f ]); | 771 throw MakeTypeError('called_non_callable', [ f ]); |
| 773 } | 772 } |
| 774 // Pull out the length so that modifications to the length in the | 773 // Pull out the length so that modifications to the length in the |
| 775 // loop will not affect the looping. | 774 // loop will not affect the looping. |
| 776 var length = this.length; | 775 var length = this.length; |
| 777 var result = []; | 776 var result = []; |
| 778 for (var i = 0; i < length; i++) { | 777 for (var i = 0; i < length; i++) { |
| 779 var current = this[i]; | 778 var current = this[i]; |
| 780 if (!IS_UNDEFINED(current) || i in this) { | 779 if (!IS_UNDEFINED(current) || i in this) { |
| 781 if (f.call(receiver, current, i, this)) result.push(current); | 780 if (f.call(receiver, current, i, this)) result.push(current); |
| 782 } | 781 } |
| 783 } | 782 } |
| 784 return result; | 783 return result; |
| 785 }; | 784 } |
| 786 | 785 |
| 787 | 786 |
| 788 function ArrayForEach(f, receiver) { | 787 function ArrayForEach(f, receiver) { |
| 789 if (!IS_FUNCTION(f)) { | 788 if (!IS_FUNCTION(f)) { |
| 790 throw MakeTypeError('called_non_callable', [ f ]); | 789 throw MakeTypeError('called_non_callable', [ f ]); |
| 791 } | 790 } |
| 792 // Pull out the length so that modifications to the length in the | 791 // Pull out the length so that modifications to the length in the |
| 793 // loop will not affect the looping. | 792 // loop will not affect the looping. |
| 794 var length = this.length; | 793 var length = this.length; |
| 795 for (var i = 0; i < length; i++) { | 794 for (var i = 0; i < length; i++) { |
| 796 var current = this[i]; | 795 var current = this[i]; |
| 797 if (!IS_UNDEFINED(current) || i in this) { | 796 if (!IS_UNDEFINED(current) || i in this) { |
| 798 f.call(receiver, current, i, this); | 797 f.call(receiver, current, i, this); |
| 799 } | 798 } |
| 800 } | 799 } |
| 801 }; | 800 } |
| 802 | 801 |
| 803 | 802 |
| 804 // Executes the function once for each element present in the | 803 // Executes the function once for each element present in the |
| 805 // array until it finds one where callback returns true. | 804 // array until it finds one where callback returns true. |
| 806 function ArraySome(f, receiver) { | 805 function ArraySome(f, receiver) { |
| 807 if (!IS_FUNCTION(f)) { | 806 if (!IS_FUNCTION(f)) { |
| 808 throw MakeTypeError('called_non_callable', [ f ]); | 807 throw MakeTypeError('called_non_callable', [ f ]); |
| 809 } | 808 } |
| 810 // Pull out the length so that modifications to the length in the | 809 // Pull out the length so that modifications to the length in the |
| 811 // loop will not affect the looping. | 810 // loop will not affect the looping. |
| 812 var length = this.length; | 811 var length = this.length; |
| 813 for (var i = 0; i < length; i++) { | 812 for (var i = 0; i < length; i++) { |
| 814 var current = this[i]; | 813 var current = this[i]; |
| 815 if (!IS_UNDEFINED(current) || i in this) { | 814 if (!IS_UNDEFINED(current) || i in this) { |
| 816 if (f.call(receiver, current, i, this)) return true; | 815 if (f.call(receiver, current, i, this)) return true; |
| 817 } | 816 } |
| 818 } | 817 } |
| 819 return false; | 818 return false; |
| 820 }; | 819 } |
| 821 | 820 |
| 822 | 821 |
| 823 function ArrayEvery(f, receiver) { | 822 function ArrayEvery(f, receiver) { |
| 824 if (!IS_FUNCTION(f)) { | 823 if (!IS_FUNCTION(f)) { |
| 825 throw MakeTypeError('called_non_callable', [ f ]); | 824 throw MakeTypeError('called_non_callable', [ f ]); |
| 826 } | 825 } |
| 827 // Pull out the length so that modifications to the length in the | 826 // Pull out the length so that modifications to the length in the |
| 828 // loop will not affect the looping. | 827 // loop will not affect the looping. |
| 829 var length = this.length; | 828 var length = this.length; |
| 830 for (var i = 0; i < length; i++) { | 829 for (var i = 0; i < length; i++) { |
| 831 var current = this[i]; | 830 var current = this[i]; |
| 832 if (!IS_UNDEFINED(current) || i in this) { | 831 if (!IS_UNDEFINED(current) || i in this) { |
| 833 if (!f.call(receiver, current, i, this)) return false; | 832 if (!f.call(receiver, current, i, this)) return false; |
| 834 } | 833 } |
| 835 } | 834 } |
| 836 | 835 |
| 837 return true; | 836 return true; |
| 838 }; | 837 } |
| 839 | 838 |
| 840 | 839 |
| 841 function ArrayMap(f, receiver) { | 840 function ArrayMap(f, receiver) { |
| 842 if (!IS_FUNCTION(f)) { | 841 if (!IS_FUNCTION(f)) { |
| 843 throw MakeTypeError('called_non_callable', [ f ]); | 842 throw MakeTypeError('called_non_callable', [ f ]); |
| 844 } | 843 } |
| 845 // Pull out the length so that modifications to the length in the | 844 // Pull out the length so that modifications to the length in the |
| 846 // loop will not affect the looping. | 845 // loop will not affect the looping. |
| 847 var length = this.length; | 846 var length = this.length; |
| 848 var result = new $Array(length); | 847 var result = new $Array(length); |
| 849 for (var i = 0; i < length; i++) { | 848 for (var i = 0; i < length; i++) { |
| 850 var current = this[i]; | 849 var current = this[i]; |
| 851 if (!IS_UNDEFINED(current) || i in this) { | 850 if (!IS_UNDEFINED(current) || i in this) { |
| 852 result[i] = f.call(receiver, current, i, this); | 851 result[i] = f.call(receiver, current, i, this); |
| 853 } | 852 } |
| 854 } | 853 } |
| 855 return result; | 854 return result; |
| 856 }; | 855 } |
| 857 | 856 |
| 858 | 857 |
| 859 function ArrayIndexOf(element, index) { | 858 function ArrayIndexOf(element, index) { |
| 860 var length = this.length; | 859 var length = this.length; |
| 861 if (index == null) { | 860 if (index == null) { |
| 862 index = 0; | 861 index = 0; |
| 863 } else { | 862 } else { |
| 864 index = TO_INTEGER(index); | 863 index = TO_INTEGER(index); |
| 865 // If index is negative, index from the end of the array. | 864 // If index is negative, index from the end of the array. |
| 866 if (index < 0) index = length + index; | 865 if (index < 0) index = length + index; |
| 867 // If index is still negative, search the entire array. | 866 // If index is still negative, search the entire array. |
| 868 if (index < 0) index = 0; | 867 if (index < 0) index = 0; |
| 869 } | 868 } |
| 870 // Lookup through the array. | 869 // Lookup through the array. |
| 871 for (var i = index; i < length; i++) { | 870 for (var i = index; i < length; i++) { |
| 872 var current = this[i]; | 871 var current = this[i]; |
| 873 if (!IS_UNDEFINED(current) || i in this) { | 872 if (!IS_UNDEFINED(current) || i in this) { |
| 874 if (current === element) return i; | 873 if (current === element) return i; |
| 875 } | 874 } |
| 876 } | 875 } |
| 877 return -1; | 876 return -1; |
| 878 }; | 877 } |
| 879 | 878 |
| 880 | 879 |
| 881 function ArrayLastIndexOf(element, index) { | 880 function ArrayLastIndexOf(element, index) { |
| 882 var length = this.length; | 881 var length = this.length; |
| 883 if (index == null) { | 882 if (index == null) { |
| 884 index = length - 1; | 883 index = length - 1; |
| 885 } else { | 884 } else { |
| 886 index = TO_INTEGER(index); | 885 index = TO_INTEGER(index); |
| 887 // If index is negative, index from end of the array. | 886 // If index is negative, index from end of the array. |
| 888 if (index < 0) index = length + index; | 887 if (index < 0) index = length + index; |
| 889 // If index is still negative, do not search the array. | 888 // If index is still negative, do not search the array. |
| 890 if (index < 0) index = -1; | 889 if (index < 0) index = -1; |
| 891 else if (index >= length) index = length - 1; | 890 else if (index >= length) index = length - 1; |
| 892 } | 891 } |
| 893 // Lookup through the array. | 892 // Lookup through the array. |
| 894 for (var i = index; i >= 0; i--) { | 893 for (var i = index; i >= 0; i--) { |
| 895 var current = this[i]; | 894 var current = this[i]; |
| 896 if (!IS_UNDEFINED(current) || i in this) { | 895 if (!IS_UNDEFINED(current) || i in this) { |
| 897 if (current === element) return i; | 896 if (current === element) return i; |
| 898 } | 897 } |
| 899 } | 898 } |
| 900 return -1; | 899 return -1; |
| 901 }; | 900 } |
| 902 | 901 |
| 903 | 902 |
| 904 // ------------------------------------------------------------------- | 903 // ------------------------------------------------------------------- |
| 905 | 904 |
| 906 function InstallProperties(prototype, attributes, properties) { | 905 function InstallFunctions(prototype, attributes, functions) { |
| 907 for (var key in properties) { | 906 for (var i = 0; i < functions.length; i += 2) { |
| 908 %AddProperty(prototype, key, properties[key], attributes); | 907 var key = functions[i]; |
| 908 var f = functions[i + 1]; |
| 909 %FunctionSetName(f, key); |
| 910 %AddProperty(prototype, key, f, attributes); |
| 909 } | 911 } |
| 910 }; | 912 } |
| 911 | 913 |
| 912 | 914 |
| 913 function UpdateFunctionLengths(lengths) { | 915 function UpdateFunctionLengths(lengths) { |
| 914 for (var key in lengths) { | 916 for (var key in lengths) { |
| 915 %FunctionSetLength(this[key], lengths[key]); | 917 %FunctionSetLength(this[key], lengths[key]); |
| 916 } | 918 } |
| 917 }; | 919 } |
| 918 | 920 |
| 919 | 921 |
| 920 // ------------------------------------------------------------------- | 922 // ------------------------------------------------------------------- |
| 921 | 923 |
| 922 function SetupArray() { | 924 function SetupArray() { |
| 923 // Setup non-enumerable properties of the Array.prototype object. | 925 // Setup non-enumerable constructor property on the Array.prototype |
| 924 InstallProperties($Array.prototype, DONT_ENUM, { | 926 // object. |
| 925 constructor: $Array, | 927 %AddProperty($Array.prototype, "constructor", $Array, DONT_ENUM); |
| 926 toString: ArrayToString, | 928 |
| 927 toLocaleString: ArrayToLocaleString, | 929 // Setup non-enumerable functions of the Array.prototype object and |
| 928 join: ArrayJoin, | 930 // set their names. |
| 929 pop: ArrayPop, | 931 InstallFunctions($Array.prototype, DONT_ENUM, $Array( |
| 930 push: ArrayPush, | 932 "toString", ArrayToString, |
| 931 concat: ArrayConcat, | 933 "toLocaleString", ArrayToLocaleString, |
| 932 reverse: ArrayReverse, | 934 "join", ArrayJoin, |
| 933 shift: ArrayShift, | 935 "pop", ArrayPop, |
| 934 unshift: ArrayUnshift, | 936 "push", ArrayPush, |
| 935 slice: ArraySlice, | 937 "concat", ArrayConcat, |
| 936 splice: ArraySplice, | 938 "reverse", ArrayReverse, |
| 937 sort: ArraySort, | 939 "shift", ArrayShift, |
| 938 filter: ArrayFilter, | 940 "unshift", ArrayUnshift, |
| 939 forEach: ArrayForEach, | 941 "slice", ArraySlice, |
| 940 some: ArraySome, | 942 "splice", ArraySplice, |
| 941 every: ArrayEvery, | 943 "sort", ArraySort, |
| 942 map: ArrayMap, | 944 "filter", ArrayFilter, |
| 943 indexOf: ArrayIndexOf, | 945 "forEach", ArrayForEach, |
| 944 lastIndexOf: ArrayLastIndexOf | 946 "some", ArraySome, |
| 945 }); | 947 "every", ArrayEvery, |
| 948 "map", ArrayMap, |
| 949 "indexOf", ArrayIndexOf, |
| 950 "lastIndexOf", ArrayLastIndexOf |
| 951 )); |
| 946 | 952 |
| 947 // Manipulate the length of some of the functions to meet | 953 // Manipulate the length of some of the functions to meet |
| 948 // expectations set by ECMA-262 or Mozilla. | 954 // expectations set by ECMA-262 or Mozilla. |
| 949 UpdateFunctionLengths({ | 955 UpdateFunctionLengths({ |
| 950 ArrayFilter: 1, | 956 ArrayFilter: 1, |
| 951 ArrayForEach: 1, | 957 ArrayForEach: 1, |
| 952 ArraySome: 1, | 958 ArraySome: 1, |
| 953 ArrayEvery: 1, | 959 ArrayEvery: 1, |
| 954 ArrayMap: 1, | 960 ArrayMap: 1, |
| 955 ArrayIndexOf: 1, | 961 ArrayIndexOf: 1, |
| 956 ArrayLastIndexOf: 1, | 962 ArrayLastIndexOf: 1, |
| 957 ArrayPush: 1 | 963 ArrayPush: 1 |
| 958 }); | 964 }); |
| 959 }; | 965 } |
| 960 | 966 |
| 961 | 967 |
| 962 SetupArray(); | 968 SetupArray(); |
| OLD | NEW |