diff --git a/JSTests/microbenchmarks/array-prototype-includes-bigint.js b/JSTests/microbenchmarks/array-prototype-includes-bigint.js new file mode 100644 index 0000000000000..9ebd06001a4d5 --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-bigint.js @@ -0,0 +1,9 @@ +for (var i = 0; i < 1e5; ++i) { + [ + 0n,1n,2n,3n,4n,5n,6n,7n,8n,9n,10n,11n,12n,13n,14n,15n, + 31n,30n,29n,28n,27n,26n,25n,24n,23n,22n,21n,20n,19n, + 18n,17n,16n,32n,33n,34n,35n,36n,37n,38n,39n,40n,41n, + 42n,43n,44n,45n,46n,47n,63n,62n,61n,60n,59n,58n,57n, + 56n,55n,54n,53n,52n,51n,50n,49n, + ].includes(38n); +} diff --git a/JSTests/microbenchmarks/array-prototype-includes-contiguous.js b/JSTests/microbenchmarks/array-prototype-includes-contiguous.js new file mode 100644 index 0000000000000..6801eed8d943a --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-contiguous.js @@ -0,0 +1,21 @@ +const search = { value: 36 }; +const array = [ + { value: 0 }, { value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }, + { value: 5 }, { value: 6 }, { value: 7 }, { value: 8 }, { value: 9 }, + { value: 10 }, { value: 11 }, { value: 12 }, { value: 13 }, { value: 14 }, + { value: 15 }, { value: 31 }, { value: 30 }, { value: 29 }, { value: 28 }, + { value: 27 }, { value: 26 }, { value: 25 }, { value: 24 }, { value: 23 }, + { value: 22 }, { value: 21 }, { value: 20 }, { value: 19 }, { value: 18 }, + { value: 17 }, { value: 16 }, { value: 32 }, { value: 33 }, { value: 34 }, + { value: 35 }, search, { value: 37 }, { value: 38 }, { value: 39 }, + { value: 40 }, { value: 41 }, { value: 42 }, { value: 43 }, { value: 44 }, + { value: 45 }, { value: 46 }, { value: 47 }, { value: 63 }, { value: 62 }, + { value: 61 }, { value: 60 }, { value: 59 }, { value: 58 }, { value: 57 }, + { value: 56 }, { value: 55 }, { value: 54 }, { value: 53 }, { value: 52 }, + { value: 51 }, { value: 50 }, { value: 49 }, +]; + +for (var i = 0; i < 1e6; ++i) { + array.includes(search); +} + diff --git a/JSTests/microbenchmarks/array-prototype-includes-double-from-contiguous.js b/JSTests/microbenchmarks/array-prototype-includes-double-from-contiguous.js new file mode 100644 index 0000000000000..ada96523e6f5d --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-double-from-contiguous.js @@ -0,0 +1,20 @@ +const array = [ + { value: 0 }, { value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }, + { value: 5 }, { value: 6 }, { value: 7 }, { value: 8 }, { value: 9 }, + { value: 10 }, { value: 11 }, { value: 12 }, { value: 13 }, { value: 14 }, + { value: 15 }, { value: 31 }, { value: 30 }, { value: 29 }, { value: 28 }, + { value: 27 }, { value: 26 }, { value: 25 }, { value: 24 }, { value: 23 }, + { value: 22 }, { value: 21 }, { value: 20 }, { value: 19 }, { value: 18 }, + { value: 17 }, { value: 16 }, { value: 32 }, { value: 33 }, { value: 34 }, + { value: 35 }, 3.6, { value: 37 }, { value: 38 }, { value: 39 }, + { value: 40 }, { value: 41 }, { value: 42 }, { value: 43 }, { value: 44 }, + { value: 45 }, { value: 46 }, { value: 47 }, { value: 63 }, { value: 62 }, + { value: 61 }, { value: 60 }, { value: 59 }, { value: 58 }, { value: 57 }, + { value: 56 }, { value: 55 }, { value: 54 }, { value: 53 }, { value: 52 }, + { value: 51 }, { value: 50 }, { value: 49 }, +]; + +for (var i = 0; i < 1e6; ++i) { + array.includes(3.6); +} + diff --git a/JSTests/microbenchmarks/array-prototype-includes-double.js b/JSTests/microbenchmarks/array-prototype-includes-double.js new file mode 100644 index 0000000000000..f96e7ab91946c --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-double.js @@ -0,0 +1,9 @@ +for (var i = 0; i < 1e6; ++i) { + [ + 0.2, 1.2, 2.2, 3.2, 4.2, 5.2, 6.2, 7.2, 8.2, 9.2, 10.2, 11.2, 12.2, 13.2, 14.2, 15.2, + 31.2, 30.2, 29.2, 28.2, 27.2, 26.2, 25.2, 24.2, 23.2, 22.2, 21.2, 20.2, 19.2, 18.2, + 17.2, 16.2, 32.2, 33.2, 34.2, 35.2, 36.2, 37.2, 38.2, 39.2, 40.2, 41.2, 42.2, 43.2, + 44.2, 45.2, 46.2, 47.2, 63.2, 62.2, 61.2, 60.2, 59.2, 58.2, 57.2, 56.2, 55.2, 54.2, + 53.2, 52.2, 51.2, 50.2, 49.2, + ].includes(38.2); +} diff --git a/JSTests/microbenchmarks/array-prototype-includes-int32-from-contiguous.js b/JSTests/microbenchmarks/array-prototype-includes-int32-from-contiguous.js new file mode 100644 index 0000000000000..24c8fd3413bdc --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-int32-from-contiguous.js @@ -0,0 +1,20 @@ +const array = [ + { value: 0 }, { value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }, + { value: 5 }, { value: 6 }, { value: 7 }, { value: 8 }, { value: 9 }, + { value: 10 }, { value: 11 }, { value: 12 }, { value: 13 }, { value: 14 }, + { value: 15 }, { value: 31 }, { value: 30 }, { value: 29 }, { value: 28 }, + { value: 27 }, { value: 26 }, { value: 25 }, { value: 24 }, { value: 23 }, + { value: 22 }, { value: 21 }, { value: 20 }, { value: 19 }, { value: 18 }, + { value: 17 }, { value: 16 }, { value: 32 }, { value: 33 }, { value: 34 }, + { value: 35 }, 3, { value: 37 }, { value: 38 }, { value: 39 }, + { value: 40 }, { value: 41 }, { value: 42 }, { value: 43 }, { value: 44 }, + { value: 45 }, { value: 46 }, { value: 47 }, { value: 63 }, { value: 62 }, + { value: 61 }, { value: 60 }, { value: 59 }, { value: 58 }, { value: 57 }, + { value: 56 }, { value: 55 }, { value: 54 }, { value: 53 }, { value: 52 }, + { value: 51 }, { value: 50 }, { value: 49 }, +]; + +for (var i = 0; i < 1e6; ++i) { + array.includes(3); +} + diff --git a/JSTests/microbenchmarks/array-prototype-includes-int32.js b/JSTests/microbenchmarks/array-prototype-includes-int32.js new file mode 100644 index 0000000000000..14d020191a74c --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-int32.js @@ -0,0 +1,11 @@ +function test(array, searchElement) { + return array.includes(searchElement); +} +noInline(test); + +var array = new Array(1024); +for (var i = 0; i < array.length; i++) + array[i] = i; + +for (var i = 0; i < 1e6; ++i) + test(array, 512); diff --git a/JSTests/microbenchmarks/array-prototype-includes-string-16-const.js b/JSTests/microbenchmarks/array-prototype-includes-string-16-const.js new file mode 100644 index 0000000000000..43431b3909c40 --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-string-16-const.js @@ -0,0 +1,36 @@ +function test(array, value) { + return array.includes(value); +} +noInline(test); + +const array = [ + "あいうえおかきくけこさしすせそたちつてとなにぬねの", + "かきくけこさしすせそたちつてとなにぬねのはひふへほ", + "さしすせそたちつてとなにぬねのはひふへほまみむめも", + "たちつてとなにぬねのはひふへほまみむめもやゆよらり", + "なにぬねのはひふへほまみむめもやゆよらりるれろわを", + "はひふへほまみむめもやゆよらりるれろわをんあいうえ", + "まみむめもやゆよらりるれろわをんあいうえおかきくけ", + "やゆよらりるれろわをんあいうえおかきくけこさしすせ", + "らりるれろわをんあいうえおかきくけこさしすせそたち", + "わをんあいうえおかきくけこさしすせそたちつてとなに", + "んあいうえおかきくけこさしすせそたちつてとなにぬね", + "ひふへほまみむめもやゆよらりるれろわをんあいうえお", + "ふへほまみむめもやゆよらりるれろわをんあいうえおか", + "へほまみむめもやゆよらりるれろわをんあいうえおかき", + "ほまみむめもやゆよらりるれろわをんあいうえおかきく", + "みむめもやゆよらりるれろわをんあいうえおかきくけこ", + "むめもやゆよらりるれろわをんあいうえおかきくけこさ", + "めもやゆよらりるれろわをんあいうえおかきくけこさし", + "もやゆよらりるれろわをんあいうえおかきくけこさしす", + "ゆよらりるれろわをんあいうえおかきくけこさしすせそ", + "よらりるれろわをんあいうえおかきくけこさしすせそた", + "らりるれろわをんあいうえおかきくけこさしすせそたち", + "りるれろわをんあいうえおかきくけこさしすせそたちつ", + "るれろわをんあいうえおかきくけこさしすせそたちつて", + "れろわをんあいうえおかきくけこさしすせそたちつてと", + "ろわをんあいうえおかきくけこさしすせそたちつてとな", +]; + +for (var i = 0; i < 1e6; ++i) + test(array, "もやゆよらりるれろわをんあいうえおかきくけこさしす"); diff --git a/JSTests/microbenchmarks/array-prototype-includes-string-16.js b/JSTests/microbenchmarks/array-prototype-includes-string-16.js new file mode 100644 index 0000000000000..39bbf59ea7174 --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-string-16.js @@ -0,0 +1,36 @@ +function test(array, value) { + return array.includes(value); +} +noInline(test); + +const array = [ + "あいうえおかきくけこさしすせそたちつてとなにぬねの", + "かきくけこさしすせそたちつてとなにぬねのはひふへほ", + "さしすせそたちつてとなにぬねのはひふへほまみむめも", + "たちつてとなにぬねのはひふへほまみむめもやゆよらり", + "なにぬねのはひふへほまみむめもやゆよらりるれろわを", + "はひふへほまみむめもやゆよらりるれろわをんあいうえ", + "まみむめもやゆよらりるれろわをんあいうえおかきくけ", + "やゆよらりるれろわをんあいうえおかきくけこさしすせ", + "らりるれろわをんあいうえおかきくけこさしすせそたち", + "わをんあいうえおかきくけこさしすせそたちつてとなに", + "んあいうえおかきくけこさしすせそたちつてとなにぬね", + "ひふへほまみむめもやゆよらりるれろわをんあいうえお", + "ふへほまみむめもやゆよらりるれろわをんあいうえおか", + "へほまみむめもやゆよらりるれろわをんあいうえおかき", + "ほまみむめもやゆよらりるれろわをんあいうえおかきく", + "みむめもやゆよらりるれろわをんあいうえおかきくけこ", + "むめもやゆよらりるれろわをんあいうえおかきくけこさ", + "めもやゆよらりるれろわをんあいうえおかきくけこさし", + "もやゆよらりるれろわをんあいうえおかきくけこさしす", + "ゆよらりるれろわをんあいうえおかきくけこさしすせそ", + "よらりるれろわをんあいうえおかきくけこさしすせそた", + "らりるれろわをんあいうえおかきくけこさしすせそたち", + "りるれろわをんあいうえおかきくけこさしすせそたちつ", + "るれろわをんあいうえおかきくけこさしすせそたちつて", + "れろわをんあいうえおかきくけこさしすせそたちつてと", + "ろわをんあいうえおかきくけこさしすせそたちつてとな", +]; + +for (var i = 0; i < 1e6; ++i) + test(array, "もやゆよらりるれろわをん" + "あいうえおかきくけこさしす"); diff --git a/JSTests/microbenchmarks/array-prototype-includes-string-const.js b/JSTests/microbenchmarks/array-prototype-includes-string-const.js new file mode 100644 index 0000000000000..3baf296813689 --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-string-const.js @@ -0,0 +1,35 @@ +function test(array, index) { + return array.includes(index); +} +noInline(test); + +const array = [ + "dEXt0TxZQQQasd999!@#$%d^&", + "AAZZ!!@@**CC77zzxx1122d33", + "HelloWorldHeldloWorlddABC", + "abcABC123!@#xfyzXYZ!$d%+=", + "Zyx9!Zyx9!Zyx9!Zyx9!!d???", + "LoremIpsum1234!@#$LordemI", + "QQQQQQQQQQqqqqqqqqqq-----", + "&&&&1111%%%%2222@@@@33f33", + "TestStringasdVariousChars", + "RandomASCII~d~~~====?????", + "^^^^#####^^^f^^+++++.....", + "AZaz09!?AZaz09!?AZaz09!?%", + "Foobar##1122Foobar##1122F", + "9876543210!@d#$%^&*()_+=-", + "OneTwo3FourFive6Seven8Nin", + "Zwxy000111Zwxy000111Zwxy0", + "Spark!!!Spark???Spark%%%S", + "ShortAndSweet123??!!Short", + "BenchmarkCaseHere12345!!?", + "MultiLineString###000xxx@", + "ABCDEFGHabcdfefgh1234!!!!", + "111122223333d4444!!!!####", + "EndlessVariety?!@#$%^^^^^", + "PqrstUVWX9876pqrstUVWX987", + "MixItUpWith5omeWe!rdCHARS", + "FinallyZzYyXx!@#$4321)(*&", +]; +for (var i = 0; i < 1e6; ++i) + test(array, "BenchmarkCaseHere12345!!?"); diff --git a/JSTests/microbenchmarks/array-prototype-includes-string.js b/JSTests/microbenchmarks/array-prototype-includes-string.js new file mode 100644 index 0000000000000..d42f1c8498625 --- /dev/null +++ b/JSTests/microbenchmarks/array-prototype-includes-string.js @@ -0,0 +1,35 @@ +function test(array, index) { + return array.includes(index); +} +noInline(test); + +const array = [ + "dEXt0TxZQQQasd999!@#$%d^&", + "AAZZ!!@@**CC77zzxx1122d33", + "HelloWorldHeldloWorlddABC", + "abcABC123!@#xfyzXYZ!$d%+=", + "Zyx9!Zyx9!Zyx9!Zyx9!!d???", + "LoremIpsum1234!@#$LordemI", + "QQQQQQQQQQqqqqqqqqqq-----", + "&&&&1111%%%%2222@@@@33f33", + "TestStringasdVariousChars", + "RandomASCII~d~~~====?????", + "^^^^#####^^^f^^+++++.....", + "AZaz09!?AZaz09!?AZaz09!?%", + "Foobar##1122Foobar##1122F", + "9876543210!@d#$%^&*()_+=-", + "OneTwo3FourFive6Seven8Nin", + "Zwxy000111Zwxy000111Zwxy0", + "Spark!!!Spark???Spark%%%S", + "ShortAndSweet123??!!Short", + "BenchmarkCaseHere12345!!?", + "MultiLineString###000xxx@", + "ABCDEFGHabcdfefgh1234!!!!", + "111122223333d4444!!!!####", + "EndlessVariety?!@#$%^^^^^", + "PqrstUVWX9876pqrstUVWX987", + "MixItUpWith5omeWe!rdCHARS", + "FinallyZzYyXx!@#$4321)(*&", +]; +for (var i = 0; i < 1e6; ++i) + test(array, "Benchmark" + "CaseHere12345!!?"); diff --git a/JSTests/stress/array-prototype-includes-contiguous-nan.js b/JSTests/stress/array-prototype-includes-contiguous-nan.js new file mode 100644 index 0000000000000..c481f4e0a1556 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-contiguous-nan.js @@ -0,0 +1,10 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +const array = [{}, {}, NaN, {}, {}]; + +for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(NaN), true); +} diff --git a/JSTests/stress/array-prototype-includes-double-nan.js b/JSTests/stress/array-prototype-includes-double-nan.js new file mode 100644 index 0000000000000..251e236b4f5eb --- /dev/null +++ b/JSTests/stress/array-prototype-includes-double-nan.js @@ -0,0 +1,10 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +const array = [1.2, 2.4, NaN, 4.4, 5.4]; + +for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(NaN), true); +} diff --git a/JSTests/stress/array-prototype-includes-doublerepuse.js b/JSTests/stress/array-prototype-includes-doublerepuse.js new file mode 100644 index 0000000000000..6a8f29f98f6c4 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-doublerepuse.js @@ -0,0 +1,13 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +function test(array, searchElement, expected) { + for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(searchElement), expected); + } +} + +test([1, 2, 3.4, 4, 5, 6, 6, 4], 3.4, true); +test([1, 2, 3.4, 4, 5, 6, 6, 4], 4.5, false); diff --git a/JSTests/stress/array-prototype-includes-hole-contiguous.js b/JSTests/stress/array-prototype-includes-hole-contiguous.js new file mode 100644 index 0000000000000..33a45fbe902b6 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-hole-contiguous.js @@ -0,0 +1,10 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +const array = [{}, {}, {}, , {}]; + +for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(undefined), true); +} diff --git a/JSTests/stress/array-prototype-includes-hole-double.js b/JSTests/stress/array-prototype-includes-hole-double.js new file mode 100644 index 0000000000000..1fc5275e26f7c --- /dev/null +++ b/JSTests/stress/array-prototype-includes-hole-double.js @@ -0,0 +1,10 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +const array = [1.4, 2.4, , 4.4, 5.5]; + +for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(undefined), true); +} diff --git a/JSTests/stress/array-prototype-includes-hole-int32.js b/JSTests/stress/array-prototype-includes-hole-int32.js new file mode 100644 index 0000000000000..eaed2968a53d8 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-hole-int32.js @@ -0,0 +1,10 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +const array = [1, 2, , 4, 5]; + +for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(undefined), true); +} diff --git a/JSTests/stress/array-prototype-includes-int32-nan.js b/JSTests/stress/array-prototype-includes-int32-nan.js new file mode 100644 index 0000000000000..a77b8cf24a239 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-int32-nan.js @@ -0,0 +1,10 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +const array = [1, 2, NaN, 4, 5]; + +for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(NaN), true); +} diff --git a/JSTests/stress/array-prototype-includes-int32use.js b/JSTests/stress/array-prototype-includes-int32use.js new file mode 100644 index 0000000000000..fe536ed899e01 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-int32use.js @@ -0,0 +1,13 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +function test(array, searchElement, expected) { + for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(searchElement), expected); + } +} + +test([1, 2, 3, 4, 5, 6, 6, 4], 5, true); +test([1, 2, 3, 4, 5, 6, 6, 4], 32, false); diff --git a/JSTests/stress/array-prototype-includes-objectuse.js b/JSTests/stress/array-prototype-includes-objectuse.js new file mode 100644 index 0000000000000..5a9ce8dfdf2d5 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-objectuse.js @@ -0,0 +1,14 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +function test(array, searchElement, expected) { + for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(searchElement), expected); + } +} + +const obj = {}; +test([1, 2, 3.4, obj, 5, 6, 6, 4], obj, true); +test([1, 2, 3.4, {}, 5, 6, 6, 4], obj, false); diff --git a/JSTests/stress/array-prototype-includes-otheruse.js b/JSTests/stress/array-prototype-includes-otheruse.js new file mode 100644 index 0000000000000..4c99a83f8fe33 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-otheruse.js @@ -0,0 +1,16 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +function test(array, searchElement, expected) { + for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(searchElement), expected); + } +} + +test([1, 2, 3.4, null, 5, 6, 6, 4], null, true); +test([1, 2, 3.4, undefined, 5, 6, 6, 4], undefined, true); +test([1, 2, 3.4, null, 5, 6, 6, 4], undefined, false); +test([1, 2, 3.4, undefined, 5, 6, 6, 4], null, false); + diff --git a/JSTests/stress/array-prototype-includes-stringuse.js b/JSTests/stress/array-prototype-includes-stringuse.js new file mode 100644 index 0000000000000..2912602b824c0 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-stringuse.js @@ -0,0 +1,13 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +function test(array, searchElement, expected) { + for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(searchElement), expected); + } +} + +test([1, 2, 3.4, "foo", 5, 6, 6, 4], "foo", true); +test([1, 2, 3.4, "foo", 5, 6, 6, 4], "bar", false); diff --git a/JSTests/stress/array-prototype-includes-symboluse.js b/JSTests/stress/array-prototype-includes-symboluse.js new file mode 100644 index 0000000000000..49745684066c3 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-symboluse.js @@ -0,0 +1,14 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +function test(array, searchElement, expected) { + for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(searchElement), expected); + } +} + +const sym = Symbol("foo"); +test([1, 2, 3.4, sym, 5, 6, 6, 4], sym, true); +test([1, 2, 3.4, Symbol("bar"), 5, 6, 6, 4], sym, false); diff --git a/JSTests/stress/array-prototype-includes-untypeduse-contiguous.js b/JSTests/stress/array-prototype-includes-untypeduse-contiguous.js new file mode 100644 index 0000000000000..4ac96a1c88055 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-untypeduse-contiguous.js @@ -0,0 +1,13 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +function test(array, searchElement, expected) { + for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(searchElement), expected); + } +} + +test([{}, 3.4, 3.5, 4.5, 5.4, 6.2, 6.2, 4.5], 4.5, true); +test([{}, 3.4, 3.5, 4.5, 5.4, 6.2, 6.2, 4.5], 5.9, false); diff --git a/JSTests/stress/array-prototype-includes-untypeduse-int32.js b/JSTests/stress/array-prototype-includes-untypeduse-int32.js new file mode 100644 index 0000000000000..85f41faddd2d3 --- /dev/null +++ b/JSTests/stress/array-prototype-includes-untypeduse-int32.js @@ -0,0 +1,13 @@ +function sameValue(a, b) { + if (a !== b) + throw new Error(`Expected ${b} but got ${a}`); +} + +function test(array, searchElement, expected) { + for (let i = 0; i < testLoopCount; i++) { + sameValue(array.includes(searchElement), expected); + } +} + +test([1, 2, 3, 4, 5, 6, 6, 4], 3.4, false); +test([1, 2, 3, 4, 5, 6, 6, 4], 5, true); diff --git a/Source/JavaScriptCore/builtins/ArrayPrototype.js b/Source/JavaScriptCore/builtins/ArrayPrototype.js index f7e77db9acf42..7618310f7b6aa 100644 --- a/Source/JavaScriptCore/builtins/ArrayPrototype.js +++ b/Source/JavaScriptCore/builtins/ArrayPrototype.js @@ -273,40 +273,6 @@ function findLastIndex(callback /*, thisArg */) return -1; } -function includes(searchElement /*, fromIndex*/) -{ - "use strict"; - - var array = @toObject(this, "Array.prototype.includes requires that |this| not be null or undefined"); - var length = @toLength(array.length); - - if (length === 0) - return false; - - var fromIndex = 0; - var from = @argument(1); - if (from !== @undefined) - fromIndex = @toIntegerOrInfinity(from); - - var index; - if (fromIndex >= 0) - index = fromIndex; - else - index = length + fromIndex; - - if (index < 0) - index = 0; - - var currentElement; - for (; index < length; ++index) { - currentElement = array[index]; - // Use SameValueZero comparison, rather than just StrictEquals. - if (searchElement === currentElement || (searchElement !== searchElement && currentElement !== currentElement)) - return true; - } - return false; -} - @linkTimeConstant function maxWithPositives(a, b) { diff --git a/Source/JavaScriptCore/builtins/BuiltinNames.h b/Source/JavaScriptCore/builtins/BuiltinNames.h index 3ecf7c0a084cc..ee15b67193d10 100644 --- a/Source/JavaScriptCore/builtins/BuiltinNames.h +++ b/Source/JavaScriptCore/builtins/BuiltinNames.h @@ -219,6 +219,7 @@ namespace JSC { macro(regExpStringIteratorCreate) \ macro(iteratorHelperCreate) \ macro(syncIterator) \ + macro(includes) \ namespace Symbols { diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h index 4834aa0ff6a59..5391fbba0aaef 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h +++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h @@ -2946,6 +2946,10 @@ bool AbstractInterpreter::executeEffects(unsigned clobberLimi makeBytecodeTopForNode(node); break; + case ArrayIncludes: + setNonCellTypeForNode(node, SpecBoolean); + break; + case ArrayIndexOf: { setNonCellTypeForNode(node, SpecInt32Only); break; diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp index 866df01f8490a..25df3adb82f83 100644 --- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp +++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp @@ -2749,6 +2749,7 @@ auto ByteCodeParser::handleIntrinsicCall(Node* callee, Operand resultOperand, Ca return CallOptimizationResult::Inlined; } + case ArrayIncludesIntrinsic: case ArrayIndexOfIntrinsic: { if (argumentCountIncludingThis < 2) return CallOptimizationResult::DidNothing; @@ -2792,7 +2793,9 @@ auto ByteCodeParser::handleIntrinsicCall(Node* callee, Operand resultOperand, Ca addVarArgChild(get(virtualRegisterForArgumentIncludingThis(2, registerOffset))); // Start index. addVarArgChild(nullptr); - Node* node = addToGraph(Node::VarArg, ArrayIndexOf, OpInfo(arrayMode.asWord()), OpInfo()); + Node* node = intrinsic == ArrayIncludesIntrinsic + ? addToGraph(Node::VarArg, ArrayIncludes, OpInfo(arrayMode.asWord()), OpInfo()) + : addToGraph(Node::VarArg, ArrayIndexOf, OpInfo(arrayMode.asWord()), OpInfo()); setResult(node); return CallOptimizationResult::Inlined; } diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h index f81b0c909a7d8..7852ae140bd93 100644 --- a/Source/JavaScriptCore/dfg/DFGClobberize.h +++ b/Source/JavaScriptCore/dfg/DFGClobberize.h @@ -167,6 +167,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu case ArrayifyToStructure: case ArrayPush: case ArrayPop: + case ArrayIncludes: case ArrayIndexOf: case HasIndexedProperty: case AtomicsAdd: @@ -684,6 +685,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu write(HeapObjectCount); return; + case ArrayIncludes: case ArrayIndexOf: { // FIXME: Should support a CSE rule. // https://bugs.webkit.org/show_bug.cgi?id=173173 diff --git a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp index 2fe7e4b0a0435..1f755e8f41736 100644 --- a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp +++ b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp @@ -447,6 +447,7 @@ bool doesGC(Graph& graph, Node* node) case CallDOMGetter: case CallDOM: case ArraySlice: + case ArrayIncludes: case ArrayIndexOf: case ParseInt: // We might resolve a rope even though we don't clobber anything. case SetAdd: diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp index 8f6b79f00c7fa..4243e859e7fdd 100644 --- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp @@ -1623,8 +1623,9 @@ class FixupPhase : public Phase { break; } + case ArrayIncludes: case ArrayIndexOf: - fixupArrayIndexOf(node); + fixupArrayIndexOfOrArrayIncludes(node); break; case RegExpExec: @@ -4791,12 +4792,19 @@ class FixupPhase : public Phase { fixup(node->child3(), 1); } - void fixupArrayIndexOf(Node* node) + void fixupArrayIndexOfOrArrayIncludes(Node* node) { + ASSERT(node->op() == ArrayIndexOf || node->op() == ArrayIncludes); + + bool isArrayIncludes = node->op() == ArrayIncludes; + Edge& array = m_graph.varArgChild(node, 0); Edge& storage = m_graph.varArgChild(node, node->numChildren() == 3 ? 2 : 3); blessArrayOperation(array, Edge(), storage); - ASSERT_WITH_MESSAGE(storage.node(), "blessArrayOperation for ArrayIndexOf must set Butterfly for storage edge."); + if (isArrayIncludes) + ASSERT_WITH_MESSAGE(storage.node(), "blessArrayOperation for ArrayIncludes must set Butterfly for storage edge."); + else + ASSERT_WITH_MESSAGE(storage.node(), "blessArrayOperation for ArrayIndexOf must set Butterfly for storage edge."); Edge& searchElement = m_graph.varArgChild(node, 1); @@ -4813,15 +4821,21 @@ class FixupPhase : public Phase { if (searchElement->shouldSpeculateCell()) { fixEdge(searchElement); m_insertionSet.insertCheck(m_graph, m_indexInBlock, node); - m_graph.convertToConstant(node, jsNumber(-1)); + if (isArrayIncludes) + m_graph.convertToConstant(node, jsBoolean(false)); + else + m_graph.convertToConstant(node, jsNumber(-1)); observeUseKindOnNode(searchElementNode); return; } - if (searchElement->shouldSpeculateOther()) { + if (searchElement->shouldSpeculateOther() && !isArrayIncludes) { fixEdge(searchElement); m_insertionSet.insertCheck(m_graph, m_indexInBlock, node); - m_graph.convertToConstant(node, jsNumber(-1)); + if (isArrayIncludes) + m_graph.convertToConstant(node, jsBoolean(false)); + else + m_graph.convertToConstant(node, jsNumber(-1)); observeUseKindOnNode(searchElementNode); return; } @@ -4829,7 +4843,10 @@ class FixupPhase : public Phase { if (searchElement->shouldSpeculateBoolean()) { fixEdge(searchElement); m_insertionSet.insertCheck(m_graph, m_indexInBlock, node); - m_graph.convertToConstant(node, jsNumber(-1)); + if (isArrayIncludes) + m_graph.convertToConstant(node, jsBoolean(false)); + else + m_graph.convertToConstant(node, jsNumber(-1)); observeUseKindOnNode(searchElementNode); return; } diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h index ff15c21074c12..c72320bcf619f 100644 --- a/Source/JavaScriptCore/dfg/DFGNode.h +++ b/Source/JavaScriptCore/dfg/DFGNode.h @@ -2231,6 +2231,7 @@ struct Node { case GetTypedArrayLengthAsInt52: case HasIndexedProperty: case EnumeratorNextUpdateIndexAndMode: + case ArrayIncludes: case ArrayIndexOf: return true; default: @@ -2576,6 +2577,7 @@ struct Node { case ArrayifyToStructure: case ArrayPush: case ArrayPop: + case ArrayIncludes: case ArrayIndexOf: case HasIndexedProperty: case AtomicsAdd: diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h index 3bbc995c7354f..73b7f7bf4b188 100644 --- a/Source/JavaScriptCore/dfg/DFGNodeType.h +++ b/Source/JavaScriptCore/dfg/DFGNodeType.h @@ -334,6 +334,7 @@ namespace JSC { namespace DFG { macro(ArrayPush, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \ macro(ArrayPop, NodeResultJS | NodeMustGenerate) \ macro(ArraySlice, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \ + macro(ArrayIncludes, NodeResultBoolean | NodeHasVarArgs) \ macro(ArrayIndexOf, NodeResultInt32 | NodeHasVarArgs) \ macro(ArraySplice, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \ \ diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp index 2c4caa5a9b9e2..5b4083987b273 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.cpp +++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp @@ -3706,6 +3706,131 @@ JSC_DEFINE_JIT_OPERATION(operationToLengthUntyped, EncodedJSValue, (JSGlobalObje OPERATION_RETURN(scope, JSValue::encode(jsNumber(value.toLength(globalObject)))); } +static ALWAYS_INLINE UCPUStrictInt32 arrayIncludesString(JSGlobalObject* globalObject, Butterfly* butterfly, JSString* searchElement, int32_t index) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + int32_t length = butterfly->publicLength(); + auto data = butterfly->contiguous().data(); + for (; index < length; ++index) { + JSValue value = data[index].get(); + if (!value || !value.isString()) + continue; + auto* string = asString(value); + if (string == searchElement) + return toUCPUStrictInt32(1); + if (string->equalInline(globalObject, searchElement)) { + scope.assertNoExceptionExceptTermination(); + return toUCPUStrictInt32(1); + } + RETURN_IF_EXCEPTION(scope, { }); + } + return toUCPUStrictInt32(0); +} + +JSC_DEFINE_JIT_OPERATION(operationArrayIncludesString, UCPUStrictInt32, (JSGlobalObject* globalObject, Butterfly* butterfly, JSString* searchElement, int32_t index)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + OPERATION_RETURN(scope, arrayIncludesString(globalObject, butterfly, searchElement, index)); +} + +JSC_DEFINE_JIT_OPERATION(operationArrayIncludesValueInt32OrContiguous, UCPUStrictInt32, (JSGlobalObject* globalObject, Butterfly* butterfly, EncodedJSValue encodedValue, int32_t index)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue searchElement = JSValue::decode(encodedValue); + + if (searchElement.isString()) + OPERATION_RETURN(scope, arrayIncludesString(globalObject, butterfly, asString(searchElement), index)); + + int32_t length = butterfly->publicLength(); + auto data = butterfly->contiguous().data(); + + if (index >= length) + OPERATION_RETURN(scope, toUCPUStrictInt32(0)); + + if (searchElement.isObject()) { + auto* result = std::bit_cast*>(WTF::find64(std::bit_cast(data + index), encodedValue, length - index)); + if (result) + OPERATION_RETURN(scope, toUCPUStrictInt32(1)); + OPERATION_RETURN(scope, toUCPUStrictInt32(0)); + } + + if (searchElement.isInt32()) { + for (; index < length; ++index) { + JSValue value = data[index].get(); + if (!value || !value.isInt32()) + continue; + if (searchElement.asInt32() == value.asInt32()) + OPERATION_RETURN(scope, toUCPUStrictInt32(1)); + } + OPERATION_RETURN(scope, toUCPUStrictInt32(0)); + } + + bool searchElementIsUndefined = searchElement.isUndefined(); + for (; index < length; ++index) { + JSValue value = data[index].get(); + if (!value) { + if (searchElementIsUndefined) + OPERATION_RETURN(scope, 1); + continue; + } + bool isEqual = sameValueZero(globalObject, searchElement, value); + OPERATION_RETURN_IF_EXCEPTION(scope, 0); + if (isEqual) + OPERATION_RETURN(scope, toUCPUStrictInt32(1)); + } + OPERATION_RETURN(scope, toUCPUStrictInt32(0)); +} + +JSC_DEFINE_NOEXCEPT_JIT_OPERATION(operationArrayIncludesValueDouble, UCPUStrictInt32, (Butterfly* butterfly, EncodedJSValue encodedValue, int32_t index)) +{ + // We do not cause any exceptions, thus we do not need FrameTracers. + JSValue searchElement = JSValue::decode(encodedValue); + const double* data = butterfly->contiguousDouble().data(); + int32_t length = butterfly->publicLength(); + + if (searchElement.isUndefined() && containsHole(data, length)) + return toUCPUStrictInt32(1); + if (!searchElement.isNumber()) + return toUCPUStrictInt32(0); + + double number = searchElement.asNumber(); + for (; index < length; ++index) { + // This comparison ignores NaN. + if (data[index] == number) + return toUCPUStrictInt32(1); + } + return toUCPUStrictInt32(0); +} + +JSC_DEFINE_NOEXCEPT_JIT_OPERATION(operationArrayIncludesNonStringIdentityValueContiguous, UCPUStrictInt32, (Butterfly* butterfly, EncodedJSValue searchElement, int32_t index)) +{ + // We do not cause any exceptions, thus we do not need FrameTracers. + int32_t length = butterfly->publicLength(); + auto data = butterfly->contiguous().data(); + + if (index >= length) + return toUCPUStrictInt32(0); + + auto* result = std::bit_cast*>(WTF::find64(std::bit_cast(data + index), searchElement, length - index)); + if (result) + return toUCPUStrictInt32(1); + + JSValue searchElementValue = JSValue::decode(searchElement); + if (searchElementValue.isUndefined() && containsHole(data, length)) + return toUCPUStrictInt32(1); + return toUCPUStrictInt32(0); +} + static ALWAYS_INLINE UCPUStrictInt32 arrayIndexOfString(JSGlobalObject* globalObject, Butterfly* butterfly, JSString* searchElement, int32_t index) { VM& vm = globalObject->vm(); diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h index 882ed29be1a19..db15d0d5687c6 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.h +++ b/Source/JavaScriptCore/dfg/DFGOperations.h @@ -368,6 +368,11 @@ JSC_DECLARE_JIT_OPERATION(operationThrowStaticError, void, (JSGlobalObject*, JSS JSC_DECLARE_JIT_OPERATION(operationHasOwnProperty, size_t, (JSGlobalObject*, JSObject*, EncodedJSValue)); +JSC_DECLARE_JIT_OPERATION(operationArrayIncludesString, UCPUStrictInt32, (JSGlobalObject*, Butterfly*, JSString*, int32_t)); +JSC_DECLARE_NOEXCEPT_JIT_OPERATION(operationArrayIncludesValueDouble, UCPUStrictInt32, (Butterfly*, EncodedJSValue, int32_t)); +JSC_DECLARE_JIT_OPERATION(operationArrayIncludesValueInt32OrContiguous, UCPUStrictInt32, (JSGlobalObject*, Butterfly*, EncodedJSValue, int32_t)); +JSC_DECLARE_NOEXCEPT_JIT_OPERATION(operationArrayIncludesNonStringIdentityValueContiguous, UCPUStrictInt32, (Butterfly*, EncodedJSValue, int32_t)); + JSC_DECLARE_JIT_OPERATION(operationArrayIndexOfString, UCPUStrictInt32, (JSGlobalObject*, Butterfly*, JSString*, int32_t)); JSC_DECLARE_NOEXCEPT_JIT_OPERATION(operationArrayIndexOfValueDouble, UCPUStrictInt32, (Butterfly*, EncodedJSValue, int32_t)); JSC_DECLARE_JIT_OPERATION(operationArrayIndexOfValueInt32OrContiguous, UCPUStrictInt32, (JSGlobalObject*, Butterfly*, EncodedJSValue, int32_t)); diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp index af6511d1110b1..7a5390dbc6eb9 100644 --- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp @@ -1144,6 +1144,11 @@ class PredictionPropagationPhase : public Phase { break; } + case ArrayIncludes: { + setPrediction(SpecBoolean); + break; + } + case GetTypedArrayByteOffset: case GetArrayLength: case GetUndetachedTypeArrayLength: diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h index c75f3587be170..069c4948ce7b8 100644 --- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h +++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h @@ -350,6 +350,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno return state.forNode(node->child1()).isType(SpecObject); case ArraySlice: + case ArrayIncludes: case ArrayIndexOf: { // You could plausibly move this code around as long as you proved the // incoming array base structure is an original array at the hoisted location. diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index c2a7be72d1ad2..6524ac1962c73 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -9476,9 +9476,11 @@ void SpeculativeJIT::compileArraySplice(Node* node) jsValueResult(resultRegs, node); } -void SpeculativeJIT::compileArrayIndexOf(Node* node) +void SpeculativeJIT::compileArrayIndexOfOrArrayIncludes(Node* node) { - ASSERT(node->op() == ArrayIndexOf); + ASSERT(node->op() == ArrayIndexOf || node->op() == ArrayIncludes); + + bool isArrayIncludes = node->op() == ArrayIncludes; StorageOperand storage(this, m_graph.varArgChild(node, node->numChildren() == 3 ? 2 : 3)); GPRTemporary index(this); @@ -9514,10 +9516,20 @@ void SpeculativeJIT::compileArrayIndexOf(Node* node) add32(TrustedImm32(1), indexGPR); jump().linkTo(loop, this); - notFound.link(this); - move(TrustedImm32(-1), indexGPR); - found.link(this); - strictInt32Result(indexGPR, node); + if (isArrayIncludes) { + notFound.link(this); + move(TrustedImm32(0), indexGPR); + Jump done = jump(); + found.link(this); + move(TrustedImm32(1), indexGPR); + done.link(this); + unblessedBooleanResult(indexGPR, node); + } else { + notFound.link(this); + move(TrustedImm32(-1), indexGPR); + found.link(this); + strictInt32Result(indexGPR, node); + } }; ASSERT(node->arrayMode().type() == Array::Int32); @@ -9569,10 +9581,20 @@ void SpeculativeJIT::compileArrayIndexOf(Node* node) add32(TrustedImm32(1), indexGPR); jump().linkTo(loop, this); - notFound.link(this); - move(TrustedImm32(-1), indexGPR); - found.link(this); - strictInt32Result(indexGPR, node); + if (isArrayIncludes) { + notFound.link(this); + move(TrustedImm32(0), indexGPR); + Jump done = jump(); + found.link(this); + move(TrustedImm32(1), indexGPR); + done.link(this); + unblessedBooleanResult(indexGPR, node); + } else { + notFound.link(this); + move(TrustedImm32(-1), indexGPR); + found.link(this); + strictInt32Result(indexGPR, node); + } return; } @@ -9587,9 +9609,13 @@ void SpeculativeJIT::compileArrayIndexOf(Node* node) flushRegisters(); - callOperation(operationArrayIndexOfString, lengthGPR, LinkableConstant::globalObject(*this, node), storageGPR, searchElementGPR, indexGPR); - - strictInt32Result(lengthGPR, node); + if (isArrayIncludes) { + callOperation(operationArrayIncludesString, lengthGPR, LinkableConstant::globalObject(*this, node), storageGPR, searchElementGPR, indexGPR); + unblessedBooleanResult(lengthGPR, node); + } else { + callOperation(operationArrayIndexOfString, lengthGPR, LinkableConstant::globalObject(*this, node), storageGPR, searchElementGPR, indexGPR); + strictInt32Result(lengthGPR, node); + } return; #else @@ -9634,10 +9660,18 @@ void SpeculativeJIT::compileArrayIndexOf(Node* node) add32(TrustedImm32(1), indexGPR); jump().linkTo(loop, this); - notFound.link(this); - move(TrustedImm32(-1), indexGPR); - - found.link(this); + if (isArrayIncludes) { + notFound.link(this); + move(TrustedImm32(0), indexGPR); + Jump done = jump(); + found.link(this); + move(TrustedImm32(1), indexGPR); + done.link(this); + } else { + notFound.link(this); + move(TrustedImm32(-1), indexGPR); + found.link(this); + } }; auto emitCompare = [&]() -> JumpList { @@ -9692,13 +9726,21 @@ void SpeculativeJIT::compileArrayIndexOf(Node* node) emitLoop(emitCompare); - addSlowPathGenerator(slowPathCall( - slowCase, this, operationArrayIndexOfString, - indexGPR, LinkableConstant::globalObject(*this, node), - storageGPR, searchElementGPR, indexGPR - )); - - strictInt32Result(indexGPR, node); + if (isArrayIncludes) { + addSlowPathGenerator(slowPathCall( + slowCase, this, operationArrayIncludesString, + indexGPR, LinkableConstant::globalObject(*this, node), + storageGPR, searchElementGPR, indexGPR + )); + unblessedBooleanResult(indexGPR, node); + } else { + addSlowPathGenerator(slowPathCall( + slowCase, this, operationArrayIndexOfString, + indexGPR, LinkableConstant::globalObject(*this, node), + storageGPR, searchElementGPR, indexGPR + )); + strictInt32Result(indexGPR, node); + } return; #endif @@ -9715,8 +9757,13 @@ void SpeculativeJIT::compileArrayIndexOf(Node* node) ASSERT(node->arrayMode().type() == Array::Contiguous); flushRegisters(); - callOperationWithoutExceptionCheck(operationArrayIndexOfNonStringIdentityValueContiguous, lengthGPR, storageGPR, valueRegs, indexGPR); - strictInt32Result(lengthGPR, node); + if (isArrayIncludes) { + callOperationWithoutExceptionCheck(operationArrayIncludesNonStringIdentityValueContiguous, lengthGPR, storageGPR, valueRegs, indexGPR); + unblessedBooleanResult(lengthGPR, node); + } else { + callOperationWithoutExceptionCheck(operationArrayIndexOfNonStringIdentityValueContiguous, lengthGPR, storageGPR, valueRegs, indexGPR); + strictInt32Result(lengthGPR, node); + } return; } @@ -9728,18 +9775,27 @@ void SpeculativeJIT::compileArrayIndexOf(Node* node) flushRegisters(); switch (node->arrayMode().type()) { case Array::Double: - callOperation(operationArrayIndexOfValueDouble, lengthGPR, storageGPR, searchElementRegs, indexGPR); + if (isArrayIncludes) + callOperation(operationArrayIncludesValueDouble, lengthGPR, storageGPR, searchElementRegs, indexGPR); + else + callOperation(operationArrayIndexOfValueDouble, lengthGPR, storageGPR, searchElementRegs, indexGPR); break; case Array::Int32: case Array::Contiguous: - callOperation(operationArrayIndexOfValueInt32OrContiguous, lengthGPR, LinkableConstant::globalObject(*this, node), storageGPR, searchElementRegs, indexGPR); + if (isArrayIncludes) + callOperation(operationArrayIncludesValueInt32OrContiguous, lengthGPR, LinkableConstant::globalObject(*this, node), storageGPR, searchElementRegs, indexGPR); + else + callOperation(operationArrayIndexOfValueInt32OrContiguous, lengthGPR, LinkableConstant::globalObject(*this, node), storageGPR, searchElementRegs, indexGPR); break; default: RELEASE_ASSERT_NOT_REACHED(); break; } - strictInt32Result(lengthGPR, node); + if (isArrayIncludes) + unblessedBooleanResult(lengthGPR, node); + else + strictInt32Result(lengthGPR, node); return; } diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h index 62b5187681cd0..084212562e026 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h @@ -1686,7 +1686,7 @@ class SpeculativeJIT : public JITCompiler { void compileGetRestLength(Node*); void compileArraySlice(Node*); void compileArraySplice(Node*); - void compileArrayIndexOf(Node*); + void compileArrayIndexOfOrArrayIncludes(Node*); void compileArrayPush(Node*); void compileNotifyWrite(Node*); void compileRegExpExec(Node*); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp index e74f25b5cf3f8..d807d6c91cd8d 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp @@ -2932,8 +2932,9 @@ void SpeculativeJIT::compile(Node* node) break; } + case ArrayIncludes: case ArrayIndexOf: { - compileArrayIndexOf(node); + compileArrayIndexOfOrArrayIncludes(node); break; } diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp index a3c7bc88a3980..cab6e82492629 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp @@ -4154,8 +4154,9 @@ void SpeculativeJIT::compile(Node* node) break; } + case ArrayIncludes: case ArrayIndexOf: { - compileArrayIndexOf(node); + compileArrayIndexOfOrArrayIncludes(node); break; } diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp index 94b1463827e89..ffdeaf2a714fc 100644 --- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp +++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp @@ -422,6 +422,7 @@ inline CapabilityLevel canCompile(Node* node) case CallDOMGetter: case ArraySlice: case ArraySplice: + case ArrayIncludes: case ArrayIndexOf: case ArrayPop: case ArrayPush: diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp index 830c23c3de385..9193e3347fb6b 100644 --- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp +++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp @@ -1160,8 +1160,9 @@ class LowerDFGToB3 { case ArraySplice: compileArraySplice(); break; + case ArrayIncludes: case ArrayIndexOf: - compileArrayIndexOf(); + compileArrayIndexOfOrArrayIncludes(); break; case CreateActivation: compileCreateActivation(); @@ -7592,8 +7593,10 @@ IGNORE_CLANG_WARNINGS_END setJSValue(vmCall(Int64, refCount ? operationArraySplice : operationArraySpliceIgnoreResult, weakPointer(globalObject), base, index, deleteCount, m_out.constIntPtr(buffer), m_out.constInt32(insertionCount))); } - void compileArrayIndexOf() + void compileArrayIndexOfOrArrayIncludes() { + ASSERT(m_node->op() == ArrayIncludes || m_node->op() == ArrayIndexOf); + JSGlobalObject* globalObject = m_graph.globalObjectFor(m_origin.semantic); LValue base = lowCell(m_graph.varArgChild(m_node, 0)); LValue storage = lowStorage(m_node->numChildren() == 3 ? m_graph.varArgChild(m_node, 2) : m_graph.varArgChild(m_node, 3)); @@ -7608,6 +7611,8 @@ IGNORE_CLANG_WARNINGS_END } else startIndex = m_out.int32Zero; + bool isArrayIncludes = m_node->op() == ArrayIncludes; + Edge& searchElementEdge = m_graph.varArgChild(m_node, 1); switch (searchElementEdge.useKind()) { case Int32Use: @@ -7645,7 +7650,7 @@ IGNORE_CLANG_WARNINGS_END m_out.branch(m_out.notEqual(index, length), unsure(loopBody), unsure(notFound)); m_out.appendTo(loopBody, loopNext); - ValueFromBlock foundResult = m_out.anchor(index); + ValueFromBlock foundResult = isArrayIncludes ? m_out.anchor(m_out.constBool(true)) : m_out.anchor(index); switch (searchElementEdge.useKind()) { case Int32Use: { // Empty value is ignored because of JSValue::NumberTag. @@ -7670,13 +7675,16 @@ IGNORE_CLANG_WARNINGS_END m_out.jump(loopHeader); m_out.appendTo(notFound, continuation); - ValueFromBlock notFoundResult = m_out.anchor(m_out.constIntPtr(-1)); + ValueFromBlock notFoundResult = isArrayIncludes ? m_out.anchor(m_out.constBool(false)) : m_out.anchor(m_out.constIntPtr(-1)); m_out.jump(continuation); m_out.appendTo(continuation, lastNext); // We have to keep base alive since that keeps content of storage alive. ensureStillAliveHere(base); - setInt32(m_out.castToInt32(m_out.phi(pointerType(), notFoundResult, foundResult))); + if (isArrayIncludes) + setBoolean(m_out.phi(Int32, notFoundResult, foundResult)); + else + setInt32(m_out.castToInt32(m_out.phi(pointerType(), notFoundResult, foundResult))); return; } @@ -7726,7 +7734,7 @@ IGNORE_CLANG_WARNINGS_END m_out.branch(isString(element), unsure(fastPath), unsure(loopNext)); m_out.appendTo(fastPath, slowCheckElementRope); - ValueFromBlock foundResult = m_out.anchor(index); + ValueFromBlock foundResult = isArrayIncludes ? m_out.anchor(m_out.constBool(true)) : m_out.anchor(index); m_out.branch(m_out.equal(element, searchElement), unsure(continuation), unsure(slowCheckElementRope)); m_out.appendTo(slowCheckElementRope, slowCheckElement8Bit); @@ -7775,17 +7783,20 @@ IGNORE_CLANG_WARNINGS_END m_out.jump(loopHeader); m_out.appendTo(notFound, continuation); - ValueFromBlock notFoundResult = m_out.anchor(m_out.constIntPtr(-1)); + ValueFromBlock notFoundResult = isArrayIncludes ? m_out.anchor(m_out.constBool(false)) : m_out.anchor(m_out.constIntPtr(-1)); m_out.jump(continuation); m_out.appendTo(slowCase, continuation); - ValueFromBlock slowCaseResult = m_out.anchor(vmCall(Int64, operationArrayIndexOfString, weakPointer(globalObject), storage, searchElement, startIndex)); + ValueFromBlock slowCaseResult = isArrayIncludes ? m_out.anchor(vmCall(Int32, operationArrayIncludesString, weakPointer(globalObject), storage, searchElement, startIndex)) : m_out.anchor(vmCall(Int64, operationArrayIndexOfString, weakPointer(globalObject), storage, searchElement, startIndex)); m_out.jump(continuation); m_out.appendTo(continuation, lastNext); // We have to keep base alive since that keeps content of storage alive. ensureStillAliveHere(base); - setInt32(m_out.castToInt32(m_out.phi(Int64, notFoundResult, foundResult, slowCaseResult))); + if (isArrayIncludes) + setBoolean(m_out.phi(Int32, notFoundResult, foundResult, slowCaseResult)); + else + setInt32(m_out.castToInt32(m_out.phi(Int64, notFoundResult, foundResult, slowCaseResult))); return; } @@ -7809,21 +7820,30 @@ IGNORE_CLANG_WARNINGS_END RELEASE_ASSERT_NOT_REACHED(); break; } - setInt32(m_out.castToInt32(vmCall(Int64, operationArrayIndexOfNonStringIdentityValueContiguous, storage, searchElement, startIndex))); + if (isArrayIncludes) + setBoolean(vmCall(Int32, operationArrayIncludesNonStringIdentityValueContiguous, storage, searchElement, startIndex)); + else + setInt32(m_out.castToInt32(vmCall(Int64, operationArrayIndexOfNonStringIdentityValueContiguous, storage, searchElement, startIndex))); return; } case UntypedUse: switch (m_node->arrayMode().type()) { case Array::Double: - setInt32(m_out.castToInt32(vmCall(Int64, operationArrayIndexOfValueDouble, storage, lowJSValue(searchElementEdge), startIndex))); + if (isArrayIncludes) + setBoolean(vmCall(Int32, operationArrayIncludesValueDouble, storage, lowJSValue(searchElementEdge), startIndex)); + else + setInt32(m_out.castToInt32(vmCall(Int64, operationArrayIndexOfValueDouble, storage, lowJSValue(searchElementEdge), startIndex))); return; case Array::Contiguous: // We have to keep base alive since that keeps content of storage alive. ensureStillAliveHere(base); FALLTHROUGH; case Array::Int32: - setInt32(m_out.castToInt32(vmCall(Int64, operationArrayIndexOfValueInt32OrContiguous, weakPointer(globalObject), storage, lowJSValue(searchElementEdge), startIndex))); + if (isArrayIncludes) + setBoolean(vmCall(Int32, operationArrayIncludesValueInt32OrContiguous, weakPointer(globalObject), storage, lowJSValue(searchElementEdge), startIndex)); + else + setInt32(m_out.castToInt32(vmCall(Int64, operationArrayIndexOfValueInt32OrContiguous, weakPointer(globalObject), storage, lowJSValue(searchElementEdge), startIndex))); return; default: RELEASE_ASSERT_NOT_REACHED(); diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp index b6b5404c8949d..64daf2e3b52d6 100644 --- a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp +++ b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp @@ -68,6 +68,7 @@ static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncFill); static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncToReversed); static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncToSorted); static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncWith); +static JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncIncludes); // ------------------------------ ArrayPrototype ---------------------------- @@ -125,7 +126,7 @@ void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().findLastPublicName(), arrayPrototypeFindLastCodeGenerator, static_cast(PropertyAttribute::DontEnum)); JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().findIndexPublicName(), arrayPrototypeFindIndexCodeGenerator, static_cast(PropertyAttribute::DontEnum)); JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().findLastIndexPublicName(), arrayPrototypeFindLastIndexCodeGenerator, static_cast(PropertyAttribute::DontEnum)); - JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().includesPublicName(), arrayPrototypeIncludesCodeGenerator, static_cast(PropertyAttribute::DontEnum)); + JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->includes, arrayProtoFuncIncludes, static_cast(PropertyAttribute::DontEnum), 1, ImplementationVisibility::Public, ArrayIncludesIntrinsic); JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().copyWithinPublicName(), arrayPrototypeCopyWithinCodeGenerator, static_cast(PropertyAttribute::DontEnum)); JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().atPublicName(), arrayPrototypeAtCodeGenerator, static_cast(PropertyAttribute::DontEnum)); JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toReversed, arrayProtoFuncToReversed, static_cast(PropertyAttribute::DontEnum), 0, ImplementationVisibility::Public); @@ -134,7 +135,7 @@ void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->with, arrayProtoFuncWith, static_cast(PropertyAttribute::DontEnum), 2, ImplementationVisibility::Public); putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().entriesPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().entriesPublicName()), static_cast(PropertyAttribute::ReadOnly)); putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().forEachPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().forEachPublicName()), static_cast(PropertyAttribute::ReadOnly)); - putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().includesPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().includesPublicName()), static_cast(PropertyAttribute::ReadOnly)); + putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().includesPrivateName(), getDirect(vm, vm.propertyNames->includes), static_cast(PropertyAttribute::ReadOnly)); putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().indexOfPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().indexOfPublicName()), static_cast(PropertyAttribute::ReadOnly)); putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().keysPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().keysPublicName()), static_cast(PropertyAttribute::ReadOnly)); putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().mapPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().mapPublicName()), static_cast(PropertyAttribute::ReadOnly)); @@ -154,7 +155,7 @@ void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) &vm.propertyNames->builtinNames().findLastIndexPublicName(), &vm.propertyNames->builtinNames().flatPublicName(), &vm.propertyNames->builtinNames().flatMapPublicName(), - &vm.propertyNames->builtinNames().includesPublicName(), + &vm.propertyNames->includes, &vm.propertyNames->builtinNames().keysPublicName(), &vm.propertyNames->toReversed, &vm.propertyNames->toSorted, @@ -2126,6 +2127,50 @@ JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncWith, (JSGlobalObject* globalObject, Call return JSValue::encode(result); } +JSC_DEFINE_HOST_FUNCTION(arrayProtoFuncIncludes, (JSGlobalObject* globalObject, CallFrame* callFrame)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue thisValue = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()); + RETURN_IF_EXCEPTION(scope, { }); + + if (UNLIKELY(thisValue.isUndefinedOrNull())) + return throwVMTypeError(globalObject, scope, "Array.prototype.includes requires that |this| not be null or undefined"_s); + auto* thisObject = thisValue.toObject(globalObject); + RETURN_IF_EXCEPTION(scope, { }); + + uint64_t length = toLength(globalObject, thisObject); + RETURN_IF_EXCEPTION(scope, { }); + + if (!length) + return JSValue::encode(jsBoolean(false)); + + uint64_t index = argumentClampedIndexFromStartOrEnd(globalObject, callFrame->argument(1), length, 0); + RETURN_IF_EXCEPTION(scope, { }); + + ASSERT(index <= length); + if (index == length) + return JSValue::encode(jsBoolean(false)); + + JSValue searchElement = callFrame->argument(0); + + if (LIKELY(isJSArray(thisObject))) { + JSArray* thisArray = jsCast(thisObject); + if (auto fastResult = thisArray->fastIncludes(globalObject, searchElement, index, length)) + return JSValue::encode(jsBoolean(fastResult.value())); + } + + for (; index < length; ++index) { + auto currentElement = thisObject->getIndex(globalObject, index); + RETURN_IF_EXCEPTION(scope, { }); + if (sameValueZero(globalObject, searchElement, currentElement)) + return JSValue::encode(jsBoolean(true)); + } + + return JSValue::encode(jsBoolean(false)); +} + } // namespace JSC WTF_ALLOW_UNSAFE_BUFFER_USAGE_END diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.h b/Source/JavaScriptCore/runtime/CommonIdentifiers.h index 6bfa977720646..5165f0f3565e0 100644 --- a/Source/JavaScriptCore/runtime/CommonIdentifiers.h +++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.h @@ -161,6 +161,7 @@ macro(id) \ macro(ignoreCase) \ macro(ignorePunctuation) \ + macro(includes) \ macro(index) \ macro(indices) \ macro(inferredName) \ diff --git a/Source/JavaScriptCore/runtime/Intrinsic.h b/Source/JavaScriptCore/runtime/Intrinsic.h index 4b886f779cc26..220ae250b6d61 100644 --- a/Source/JavaScriptCore/runtime/Intrinsic.h +++ b/Source/JavaScriptCore/runtime/Intrinsic.h @@ -56,6 +56,7 @@ namespace JSC { macro(ArrayPopIntrinsic) \ macro(ArraySliceIntrinsic) \ macro(ArraySpliceIntrinsic) \ + macro(ArrayIncludesIntrinsic) \ macro(ArrayIndexOfIntrinsic) \ macro(ArrayValuesIntrinsic) \ macro(ArrayKeysIntrinsic) \ diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp index f5e157840f99b..605c92520530c 100644 --- a/Source/JavaScriptCore/runtime/JSArray.cpp +++ b/Source/JavaScriptCore/runtime/JSArray.cpp @@ -734,6 +734,91 @@ JSArray* JSArray::fastWith(JSGlobalObject* globalObject, uint32_t index, JSValue } } +std::optional JSArray::fastIncludes(JSGlobalObject* globalObject, JSValue searchElement, uint64_t index64, uint64_t length64) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + bool canDoFastPath = this->canDoFastIndexedAccess() + && this->getArrayLength() == length64 // The effects in getting `index` could have changed the length of this array. + && static_cast(index64) == index64; + if (!canDoFastPath) + return std::nullopt; + + uint32_t length = static_cast(length64); + uint32_t index = static_cast(index64); + + switch (this->indexingType()) { + case ArrayWithInt32: { + auto& butterfly = *this->butterfly(); + auto data = butterfly.contiguous().data(); + + if (searchElement.isUndefined()) + return containsHole(data, length64); + if (!searchElement.isNumber()) + return false; + JSValue searchInt32; + if (searchElement.isInt32()) + searchInt32 = searchElement; + else { + double searchNumber = searchElement.asNumber(); + if (!canBeInt32(searchNumber)) + return false; + searchInt32 = jsNumber(static_cast(searchNumber)); + } + for (; index < length; ++index) { + if (searchInt32 == data[index].get()) + return true; + } + return false; + } + case ArrayWithContiguous: { + auto& butterfly = *this->butterfly(); + auto data = butterfly.contiguous().data(); + + if (searchElement.isObject()) { + auto* result = std::bit_cast*>(WTF::find64(std::bit_cast(data + index), JSValue::encode(searchElement), length - index)); + if (result) + return true; + return false; + } + + bool searchElementIsUndefined = searchElement.isUndefined(); + for (; index < length; ++index) { + JSValue value = data[index].get(); + if (!value) { + if (searchElementIsUndefined) + return true; + continue; + } + bool isEqual = sameValueZero(globalObject, searchElement, value); + RETURN_IF_EXCEPTION(scope, { }); + if (isEqual) + return true; + } + return false; + } + case ALL_DOUBLE_INDEXING_TYPES: { + auto& butterfly = *this->butterfly(); + auto data = butterfly.contiguousDouble().data(); + + if (searchElement.isUndefined()) + return containsHole(data, length64); + if (!searchElement.isNumber()) + return false; + + double searchNumber = searchElement.asNumber(); + for (; index < length; ++index) { + if (data[index] == searchNumber) + return true; + } + return false; + } + default: + return std::nullopt; + } +} + bool JSArray::appendMemcpy(JSGlobalObject* globalObject, VM& vm, unsigned startIndex, IndexingType otherType, std::span values) { auto scope = DECLARE_THROW_SCOPE(vm); diff --git a/Source/JavaScriptCore/runtime/JSArray.h b/Source/JavaScriptCore/runtime/JSArray.h index e26dac32883f9..9ad15922ad58d 100644 --- a/Source/JavaScriptCore/runtime/JSArray.h +++ b/Source/JavaScriptCore/runtime/JSArray.h @@ -125,6 +125,8 @@ class JSArray : public JSNonFinalObject { JSArray* fastWith(JSGlobalObject*, uint32_t index, JSValue, uint64_t length); + std::optional fastIncludes(JSGlobalObject*, JSValue, uint64_t fromIndex, uint64_t length); + ALWAYS_INLINE bool definitelyNegativeOneMiss() const; enum ShiftCountMode { diff --git a/Source/JavaScriptCore/runtime/JSCJSValueInlines.h b/Source/JavaScriptCore/runtime/JSCJSValueInlines.h index 3010335e0101b..7d6b82faf7203 100644 --- a/Source/JavaScriptCore/runtime/JSCJSValueInlines.h +++ b/Source/JavaScriptCore/runtime/JSCJSValueInlines.h @@ -1479,4 +1479,26 @@ ALWAYS_INLINE bool sameValue(JSGlobalObject* globalObject, JSValue a, JSValue b) return std::bit_cast(x) == std::bit_cast(y); } +ALWAYS_INLINE bool sameValueZero(JSGlobalObject* globalObject, JSValue a, JSValue b) +{ + if (a == b) + return true; + + if (!a.isNumber()) + return JSValue::strictEqual(globalObject, a, b); + if (!b.isNumber()) + return false; + double x = a.asNumber(); + double y = b.asNumber(); + if (std::isnan(x)) + return std::isnan(y); + if (std::isnan(y)) + return std::isnan(x); + if (!x && y == -0) + return true; + if (x == -0 && !y) + return true; + return std::bit_cast(x) == std::bit_cast(y); +} + } // namespace JSC