|  | // Copyright 2010 the V8 project authors. All rights reserved. | 
|  | // Redistribution and use in source and binary forms, with or without | 
|  | // modification, are permitted provided that the following conditions are | 
|  | // met: | 
|  | // | 
|  | //     * Redistributions of source code must retain the above copyright | 
|  | //       notice, this list of conditions and the following disclaimer. | 
|  | //     * Redistributions in binary form must reproduce the above | 
|  | //       copyright notice, this list of conditions and the following | 
|  | //       disclaimer in the documentation and/or other materials provided | 
|  | //       with the distribution. | 
|  | //     * Neither the name of Google Inc. nor the names of its | 
|  | //       contributors may be used to endorse or promote products derived | 
|  | //       from this software without specific prior written permission. | 
|  | // | 
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  |  | 
|  | // Check that splicing array of holes keeps it as array of holes | 
|  | (function() { | 
|  | for (var i = 0; i < 7; i++) { | 
|  | var array = new Array(10); | 
|  | var spliced = array.splice(1, 1, 'one', 'two'); | 
|  | assertEquals(1, spliced.length); | 
|  | assertFalse(0 in spliced, "0 in spliced"); | 
|  |  | 
|  | assertEquals(11, array.length); | 
|  | assertFalse(0 in array, "0 in array"); | 
|  | assertTrue(1 in array); | 
|  | assertTrue(2 in array); | 
|  | assertFalse(3 in array, "3 in array"); | 
|  | } | 
|  | })(); | 
|  |  | 
|  |  | 
|  | // Check various variants of empty array's splicing. | 
|  | (function() { | 
|  | for (var i = 0; i < 7; i++) { | 
|  | assertEquals([], [].splice(0, 0)); | 
|  | assertEquals([], [].splice(1, 0)); | 
|  | assertEquals([], [].splice(0, 1)); | 
|  | assertEquals([], [].splice(-1, 0)); | 
|  | } | 
|  | })(); | 
|  |  | 
|  |  | 
|  | // Check that even if result array is empty, receiver gets sliced. | 
|  | (function() { | 
|  | for (var i = 0; i < 7; i++) { | 
|  | var a = [1, 2, 3]; | 
|  | assertEquals([], a.splice(1, 0, 'a', 'b', 'c')); | 
|  | assertEquals([1, 'a', 'b', 'c', 2, 3], a); | 
|  | } | 
|  | })(); | 
|  |  | 
|  |  | 
|  | // Check various forms of arguments omission. | 
|  | (function() { | 
|  | var array; | 
|  | for (var i = 0; i < 7; i++) { | 
|  | array = [1, 2, 3] | 
|  | assertEquals([], array.splice()); | 
|  | assertEquals([1, 2, 3], array); | 
|  |  | 
|  | // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is | 
|  | // given differently from when an undefined delete count is given. | 
|  | // This does not follow ECMA-262, but we do the same for | 
|  | // compatibility. | 
|  | array = [1, 2, 3] | 
|  | assertEquals([1, 2, 3], array.splice(0)); | 
|  | assertEquals([], array); | 
|  |  | 
|  | array = [1, 2, 3] | 
|  | assertEquals([1, 2, 3], array.splice(undefined)); | 
|  | assertEquals([], array); | 
|  |  | 
|  | array = [1, 2, 3] | 
|  | assertEquals([1, 2, 3], array.splice("foobar")); | 
|  | assertEquals([], array); | 
|  |  | 
|  | array = [1, 2, 3] | 
|  | assertEquals([], array.splice(undefined, undefined)); | 
|  | assertEquals([1, 2, 3], array); | 
|  |  | 
|  | array = [1, 2, 3] | 
|  | assertEquals([], array.splice("foobar", undefined)); | 
|  | assertEquals([1, 2, 3], array); | 
|  |  | 
|  | array = [1, 2, 3] | 
|  | assertEquals([], array.splice(undefined, "foobar")); | 
|  | assertEquals([1, 2, 3], array); | 
|  |  | 
|  | array = [1, 2, 3] | 
|  | assertEquals([], array.splice("foobar", "foobar")); | 
|  | assertEquals([1, 2, 3], array); | 
|  | } | 
|  | })(); | 
|  |  | 
|  |  | 
|  | // Check variants of negatives and positive indices. | 
|  | (function() { | 
|  | var array, spliced; | 
|  | for (var i = 0; i < 7; i++) { | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(-100); | 
|  | assertEquals([], array); | 
|  | assertEquals([1, 2, 3, 4, 5, 6, 7], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(-1e100); | 
|  | assertEquals([], array); | 
|  | assertEquals([1, 2, 3, 4, 5, 6, 7], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(-3); | 
|  | assertEquals([1, 2, 3, 4], array); | 
|  | assertEquals([5, 6, 7], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(-3.999999); | 
|  | assertEquals([1, 2, 3, 4], array); | 
|  | assertEquals([5, 6, 7], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(-3.000001); | 
|  | assertEquals([1, 2, 3, 4], array); | 
|  | assertEquals([5, 6, 7], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(4); | 
|  | assertEquals([1, 2, 3, 4], array); | 
|  | assertEquals([5, 6, 7], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(4.999999); | 
|  | assertEquals([1, 2, 3, 4], array); | 
|  | assertEquals([5, 6, 7], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(4.000001); | 
|  | assertEquals([1, 2, 3, 4], array); | 
|  | assertEquals([5, 6, 7], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(6); | 
|  | assertEquals([1, 2, 3, 4, 5, 6], array); | 
|  | assertEquals([7], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(7); | 
|  | assertEquals([1, 2, 3, 4, 5, 6, 7], array); | 
|  | assertEquals([], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(8); | 
|  | assertEquals([1, 2, 3, 4, 5, 6, 7], array); | 
|  | assertEquals([], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(100); | 
|  | assertEquals([1, 2, 3, 4, 5, 6, 7], array); | 
|  | assertEquals([], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(1e100); | 
|  | assertEquals([1, 2, 3, 4, 5, 6, 7], array); | 
|  | assertEquals([], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(0, -100); | 
|  | assertEquals([1, 2, 3, 4, 5, 6, 7], array); | 
|  | assertEquals([], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(0, -1e100); | 
|  | assertEquals([1, 2, 3, 4, 5, 6, 7], array); | 
|  | assertEquals([], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(0, -3); | 
|  | assertEquals([1, 2, 3, 4, 5, 6, 7], array); | 
|  | assertEquals([], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(0, -3.999999); | 
|  | assertEquals([1, 2, 3, 4, 5, 6, 7], array); | 
|  | assertEquals([], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(0, -3.000001); | 
|  | assertEquals([1, 2, 3, 4, 5, 6, 7], array); | 
|  | assertEquals([], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(0, 4); | 
|  | assertEquals([5, 6, 7], array); | 
|  | assertEquals([1, 2, 3, 4], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(0, 4.999999); | 
|  | assertEquals([5, 6, 7], array); | 
|  | assertEquals([1, 2, 3, 4], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(0, 4.000001); | 
|  | assertEquals([5, 6, 7], array); | 
|  | assertEquals([1, 2, 3, 4], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(0, 6); | 
|  | assertEquals([7], array); | 
|  | assertEquals([1, 2, 3, 4, 5, 6], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(0, 7); | 
|  | assertEquals([], array); | 
|  | assertEquals([1, 2, 3, 4, 5, 6, 7], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(0, 8); | 
|  | assertEquals([], array); | 
|  | assertEquals([1, 2, 3, 4, 5, 6, 7], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(0, 100); | 
|  | assertEquals([], array); | 
|  | assertEquals([1, 2, 3, 4, 5, 6, 7], spliced); | 
|  |  | 
|  | array = [1, 2, 3, 4, 5, 6, 7]; | 
|  | spliced = array.splice(0, 1e100); | 
|  | assertEquals([], array); | 
|  | assertEquals([1, 2, 3, 4, 5, 6, 7], spliced); | 
|  |  | 
|  | // Some exotic cases. | 
|  | obj = { toString: function() { throw 'Exception'; } }; | 
|  |  | 
|  | // Throwing an exception in conversion: | 
|  | try { | 
|  | [1, 2, 3].splice(obj, 3); | 
|  | throw 'Should have thrown'; | 
|  | } catch (e) { | 
|  | assertEquals('Exception', e); | 
|  | } | 
|  |  | 
|  | try { | 
|  | [1, 2, 3].splice(0, obj, 3); | 
|  | throw 'Should have thrown'; | 
|  | } catch (e) { | 
|  | assertEquals('Exception', e); | 
|  | } | 
|  |  | 
|  | array = [1, 2, 3]; | 
|  | array.splice(0, 3, obj); | 
|  | assertEquals(1, array.length); | 
|  |  | 
|  | // Custom conversion: | 
|  | array = [1, 2, 3]; | 
|  | spliced = array.splice({valueOf: function() { return 1; }}, | 
|  | {toString: function() { return 2; }}, | 
|  | 'one', 'two'); | 
|  | assertEquals([2, 3], spliced); | 
|  | assertEquals([1, 'one', 'two'], array); | 
|  | } | 
|  | })(); | 
|  |  | 
|  |  | 
|  | // Nasty: modify the array in ToInteger. | 
|  | (function() { | 
|  | var array = []; | 
|  | var spliced; | 
|  |  | 
|  | for (var i = 0; i < 13; i++) { | 
|  | bad_start = { valueOf: function() { array.push(2*i); return -1; } }; | 
|  | bad_count = { valueOf: function() { array.push(2*i + 1); return 1; } }; | 
|  | spliced = array.splice(bad_start, bad_count); | 
|  | // According to the spec (15.4.4.12), length is calculated before | 
|  | // performing ToInteger on arguments.  However, v8 ignores elements | 
|  | // we add while converting, so we need corrective pushes. | 
|  | array.push(2*i); array.push(2*i + 1); | 
|  | if (i == 0) { | 
|  | assertEquals([], spliced);  // Length was 0, nothing to get. | 
|  | assertEquals([0, 1], array); | 
|  | } else { | 
|  | // When we start splice, array is [0 .. 2*i - 1], so we get | 
|  | // as a result [2*i], this element is removed from the array, | 
|  | // but [2 * i, 2 * i + 1] are added. | 
|  | assertEquals([2 * i - 1], spliced); | 
|  | assertEquals(2 * i, array[i]); | 
|  | assertEquals(2 * i + 1, array[i + 1]); | 
|  | } | 
|  | } | 
|  | })(); | 
|  |  | 
|  | // Check the behaviour when approaching maximal values for length. | 
|  | (function() { | 
|  | for (var i = 0; i < 7; i++) { | 
|  | try { | 
|  | new Array(Math.pow(2, 32) - 3).splice(-1, 0, 1, 2, 3, 4, 5); | 
|  | throw 'Should have thrown RangeError'; | 
|  | } catch (e) { | 
|  | assertTrue(e instanceof RangeError); | 
|  | } | 
|  |  | 
|  | // Check smi boundary | 
|  | var bigNum = (1 << 30) - 3; | 
|  | var array = new Array(bigNum); | 
|  | array.splice(-1, 0, 1, 2, 3, 4, 5, 6, 7); | 
|  | assertEquals(bigNum + 7, array.length); | 
|  | } | 
|  | })(); | 
|  |  | 
|  | (function() { | 
|  | for (var i = 0; i < 7; i++) { | 
|  | var a = [7, 8, 9]; | 
|  | a.splice(0, 0, 1, 2, 3, 4, 5, 6); | 
|  | assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], a); | 
|  | assertFalse(a.hasOwnProperty(10), "a.hasOwnProperty(10)"); | 
|  | assertEquals(undefined, a[10]); | 
|  | } | 
|  | })(); | 
|  |  | 
|  | (function testSpliceDeleteDouble() { | 
|  | var a = [1.1, 1.2, 1.3, 1.4]; | 
|  | a.splice(2, 1) | 
|  | assertEquals([1.1, 1.2, 1.4], a); | 
|  | })(); | 
|  |  | 
|  | // Past this point the ArrayProtector is invalidated since we modify the | 
|  | // Array.prototype. | 
|  |  | 
|  | // Check the case of JS builtin .splice() | 
|  | (function() { | 
|  | for (var i = 0; i < 7; i++) { | 
|  | var array = [1, 2, 3, 4]; | 
|  | Array.prototype[3] = 'foo';  // To force JS builtin. | 
|  |  | 
|  | var spliced = array.splice(); | 
|  |  | 
|  | assertEquals([], spliced); | 
|  | assertEquals([1, 2, 3, 4], array); | 
|  | } | 
|  | })(); | 
|  |  | 
|  | // Now check the case with array of holes and some elements on prototype. | 
|  | (function() { | 
|  | var len = 9; | 
|  |  | 
|  | var at3 = "@3"; | 
|  | var at7 = "@7"; | 
|  |  | 
|  | for (var i = 0; i < 7; i++) { | 
|  | var array = new Array(len); | 
|  | var array_proto = []; | 
|  | array_proto[3] = at3; | 
|  | array_proto[7] = at7; | 
|  | array.__proto__ = array_proto; | 
|  |  | 
|  | var spliced = array.splice(2, 2, 'one', undefined, 'two'); | 
|  |  | 
|  | // Second hole (at index 3) of array turns into | 
|  | // value of Array.prototype[3] while copying. | 
|  | assertEquals([, at3], spliced); | 
|  | assertEquals([, , 'one', undefined, 'two', , , at7, at7, ,], array); | 
|  |  | 
|  | // ... but array[3] and array[7] is actually a hole: | 
|  | assertTrue(delete array_proto[3]); | 
|  | assertEquals(undefined, array[3]); | 
|  | assertTrue(delete array_proto[7]); | 
|  | assertEquals(undefined, array[7]); | 
|  |  | 
|  | // and now check hasOwnProperty | 
|  | assertFalse(array.hasOwnProperty(0), "array.hasOwnProperty(0)"); | 
|  | assertFalse(array.hasOwnProperty(1), "array.hasOwnProperty(1)"); | 
|  | assertTrue(array.hasOwnProperty(2)); | 
|  | assertTrue(array.hasOwnProperty(3)); | 
|  | assertTrue(array.hasOwnProperty(4)); | 
|  | assertFalse(array.hasOwnProperty(5), "array.hasOwnProperty(5)"); | 
|  | assertFalse(array.hasOwnProperty(6), "array.hasOwnProperty(6)"); | 
|  | assertFalse(array.hasOwnProperty(7), "array.hasOwnProperty(7)"); | 
|  | assertTrue(array.hasOwnProperty(8)); | 
|  | assertFalse(array.hasOwnProperty(9), "array.hasOwnProperty(9)"); | 
|  |  | 
|  | // and now check couple of indices above length. | 
|  | assertFalse(array.hasOwnProperty(10), "array.hasOwnProperty(10)"); | 
|  | assertFalse(array.hasOwnProperty(15), "array.hasOwnProperty(15)"); | 
|  | assertFalse(array.hasOwnProperty(31), "array.hasOwnProperty(31)"); | 
|  | assertFalse(array.hasOwnProperty(63), "array.hasOwnProperty(63)"); | 
|  | assertFalse(array.hasOwnProperty(Math.pow(2, 32) - 2), | 
|  | "array.hasOwnProperty(Math.pow(2, 32) - 2)"); | 
|  | } | 
|  | })(); | 
|  |  | 
|  | // Now check the case with array of holes and some elements on prototype. | 
|  | (function() { | 
|  | var len = 9; | 
|  |  | 
|  | var at3 = "@3"; | 
|  | var at7 = "@7"; | 
|  |  | 
|  | for (var i = 0; i < 7; i++) { | 
|  | var array = new Array(len); | 
|  | Array.prototype[3] = at3; | 
|  | Array.prototype[7] = at7; | 
|  |  | 
|  | var spliced = array.splice(2, 2, 'one', undefined, 'two'); | 
|  |  | 
|  | // Second hole (at index 3) of array turns into | 
|  | // value of Array.prototype[3] while copying. | 
|  | assertEquals([, at3], spliced); | 
|  | assertEquals([, , 'one', undefined, 'two', , , at7, at7, ,], array); | 
|  |  | 
|  | // ... but array[3] and array[7] is actually a hole: | 
|  | assertTrue(delete Array.prototype[3]); | 
|  | assertEquals(undefined, array[3]); | 
|  | assertTrue(delete Array.prototype[7]); | 
|  | assertEquals(undefined, array[7]); | 
|  |  | 
|  | // and now check hasOwnProperty | 
|  | assertFalse(array.hasOwnProperty(0), "array.hasOwnProperty(0)"); | 
|  | assertFalse(array.hasOwnProperty(1), "array.hasOwnProperty(1)"); | 
|  | assertTrue(array.hasOwnProperty(2)); | 
|  | assertTrue(array.hasOwnProperty(3)); | 
|  | assertTrue(array.hasOwnProperty(4)); | 
|  | assertFalse(array.hasOwnProperty(5), "array.hasOwnProperty(5)"); | 
|  | assertFalse(array.hasOwnProperty(6), "array.hasOwnProperty(6)"); | 
|  | assertFalse(array.hasOwnProperty(7), "array.hasOwnProperty(7)"); | 
|  | assertTrue(array.hasOwnProperty(8)); | 
|  | assertFalse(array.hasOwnProperty(9), "array.hasOwnProperty(9)"); | 
|  |  | 
|  | // and now check couple of indices above length. | 
|  | assertFalse(array.hasOwnProperty(10), "array.hasOwnProperty(10)"); | 
|  | assertFalse(array.hasOwnProperty(15), "array.hasOwnProperty(15)"); | 
|  | assertFalse(array.hasOwnProperty(31), "array.hasOwnProperty(31)"); | 
|  | assertFalse(array.hasOwnProperty(63), "array.hasOwnProperty(63)"); | 
|  | assertFalse(array.hasOwnProperty(Math.pow(2, 32) - 2), | 
|  | "array.hasOwnProperty(Math.pow(2, 32) - 2)"); | 
|  | } | 
|  | })(); | 
|  |  | 
|  | // Verify that fast implementations aren't confused by empty DOUBLE element arrays | 
|  | (function() { | 
|  |  | 
|  | function foo(dontAddAnything) { | 
|  | let a = []; | 
|  | if (dontAddAnything === undefined) { | 
|  | a[1] = 0.5; | 
|  | } | 
|  | return a.splice(0, 0, 3.5); | 
|  | } | 
|  |  | 
|  | // Learn via allocation site tracking to create double arrays in foo(). | 
|  | foo(); | 
|  | foo(); | 
|  | // force splice to copy the input array. | 
|  | foo(true); | 
|  | })(); |