Title: [154518] trunk
Revision
154518
Author
[email protected]
Date
2013-08-23 13:40:34 -0700 (Fri, 23 Aug 2013)

Log Message

Incorrect TypedArray#set behavior
https://bugs.webkit.org/show_bug.cgi?id=83818

Source/_javascript_Core: 

Reviewed by Oliver Hunt and Mark Hahnenberg.
        
This was so much fun! typedArray.set() is like a memmove on steroids, and I'm
not smart enough to figure out optimal versions for *all* of the cases. But I
did come up with optimal implementations for most of the cases, and I wrote
spec-literal code (i.e. copy via a transfer buffer) for the cases I'm not smart
enough to write optimal code for.

* runtime/JSArrayBufferView.h:
(JSC::JSArrayBufferView::hasArrayBuffer):
* runtime/JSArrayBufferViewInlines.h:
(JSC::JSArrayBufferView::buffer):
(JSC::JSArrayBufferView::existingBufferInButterfly):
(JSC::JSArrayBufferView::neuter):
(JSC::JSArrayBufferView::byteOffset):
* runtime/JSGenericTypedArrayView.h:
* runtime/JSGenericTypedArrayViewInlines.h:
(JSC::::setWithSpecificType):
(JSC::::set):
(JSC::::existingBuffer):

LayoutTests: 

Reviewed by Oliver Hunt and Mark Hahnenberg.
        
Made it possible for shouldBe() to compare typed arrays to each other and to any array-like
object.
        
Added a bunch of tests for different kinds of overlapping typedArray.set()'s.
        
For sanity, also added the reduced test case from the bug. Interestingly, though, that test
case already passed on trunk - probably by luck (we had incidentally changed the default
copy direction from one that happened to not work to one that happened to be fine, but only
for this test).

* fast/js/jsc-test-list:
* fast/js/resources/js-test-pre.js:
(isTypedArray):
(isResultCorrect):
(stringify):
(shouldBe):
* fast/js/script-tests/typed-array-copy.js: Added.
* fast/js/script-tests/typedarray-set-destination-smaller-than-source.js: Added.
* fast/js/script-tests/typedarray-set-overlapping-elements-of-same-size.js: Added.
* fast/js/script-tests/typedarray-set-same-type-memmove.js: Added.
(arraysEqual):
* fast/js/script-tests/typedarray-set-source-smaller-than-destination.js: Added.
* fast/js/typed-array-copy-expected.txt: Added.
* fast/js/typed-array-copy.html: Added.
* fast/js/typedarray-set-destination-smaller-than-source-expected.txt: Added.
* fast/js/typedarray-set-destination-smaller-than-source.html: Added.
* fast/js/typedarray-set-overlapping-elements-of-same-size-expected.txt: Added.
* fast/js/typedarray-set-overlapping-elements-of-same-size.html: Added.
* fast/js/typedarray-set-same-type-memmove-expected.txt: Added.
* fast/js/typedarray-set-same-type-memmove.html: Added.
* fast/js/typedarray-set-source-smaller-than-destination-expected.txt: Added.
* fast/js/typedarray-set-source-smaller-than-destination.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (154517 => 154518)


--- trunk/LayoutTests/ChangeLog	2013-08-23 20:35:09 UTC (rev 154517)
+++ trunk/LayoutTests/ChangeLog	2013-08-23 20:40:34 UTC (rev 154518)
@@ -1,3 +1,43 @@
+2013-08-23  Filip Pizlo  <[email protected]>
+
+        Incorrect TypedArray#set behavior
+        https://bugs.webkit.org/show_bug.cgi?id=83818
+
+        Reviewed by Oliver Hunt and Mark Hahnenberg.
+        
+        Made it possible for shouldBe() to compare typed arrays to each other and to any array-like
+        object.
+        
+        Added a bunch of tests for different kinds of overlapping typedArray.set()'s.
+        
+        For sanity, also added the reduced test case from the bug. Interestingly, though, that test
+        case already passed on trunk - probably by luck (we had incidentally changed the default
+        copy direction from one that happened to not work to one that happened to be fine, but only
+        for this test).
+
+        * fast/js/jsc-test-list:
+        * fast/js/resources/js-test-pre.js:
+        (isTypedArray):
+        (isResultCorrect):
+        (stringify):
+        (shouldBe):
+        * fast/js/script-tests/typed-array-copy.js: Added.
+        * fast/js/script-tests/typedarray-set-destination-smaller-than-source.js: Added.
+        * fast/js/script-tests/typedarray-set-overlapping-elements-of-same-size.js: Added.
+        * fast/js/script-tests/typedarray-set-same-type-memmove.js: Added.
+        (arraysEqual):
+        * fast/js/script-tests/typedarray-set-source-smaller-than-destination.js: Added.
+        * fast/js/typed-array-copy-expected.txt: Added.
+        * fast/js/typed-array-copy.html: Added.
+        * fast/js/typedarray-set-destination-smaller-than-source-expected.txt: Added.
+        * fast/js/typedarray-set-destination-smaller-than-source.html: Added.
+        * fast/js/typedarray-set-overlapping-elements-of-same-size-expected.txt: Added.
+        * fast/js/typedarray-set-overlapping-elements-of-same-size.html: Added.
+        * fast/js/typedarray-set-same-type-memmove-expected.txt: Added.
+        * fast/js/typedarray-set-same-type-memmove.html: Added.
+        * fast/js/typedarray-set-source-smaller-than-destination-expected.txt: Added.
+        * fast/js/typedarray-set-source-smaller-than-destination.html: Added.
+
 2013-08-23  Andre Moreira Magalhaes   <[email protected]>
 
         LayoutTests/http/tests/media/video-throttled-load.cgi issue on range support

Modified: trunk/LayoutTests/fast/js/jsc-test-list (154517 => 154518)


--- trunk/LayoutTests/fast/js/jsc-test-list	2013-08-23 20:35:09 UTC (rev 154517)
+++ trunk/LayoutTests/fast/js/jsc-test-list	2013-08-23 20:40:34 UTC (rev 154518)
@@ -437,6 +437,11 @@
 fast/js/toString-recursion
 fast/js/try-catch-try-try-catch-try-finally-return-catch-finally
 fast/js/try-try-return-finally-finally
+fast/js/typed-array-copy
+fast/js/typedarray-set-destination-smaller-than-source
+fast/js/typedarray-set-overlapping-elements-of-same-size
+fast/js/typedarray-set-same-type-memmove
+fast/js/typedarray-set-source-smaller-than-destination
 fast/js/typeof-codegen-crash
 fast/js/typeof-constant-string
 fast/js/unexpected-constant-crash

Modified: trunk/LayoutTests/fast/js/resources/js-test-pre.js (154517 => 154518)


--- trunk/LayoutTests/fast/js/resources/js-test-pre.js	2013-08-23 20:35:09 UTC (rev 154517)
+++ trunk/LayoutTests/fast/js/resources/js-test-pre.js	2013-08-23 20:40:34 UTC (rev 154518)
@@ -133,6 +133,19 @@
     return n === 0 && 1/n < 0;
 }
 
+function isTypedArray(array)
+{
+    return array instanceof Int8Array
+        || array instanceof Int16Array
+        || array instanceof Int32Array
+        || array instanceof Uint8Array
+        || array instanceof Uint8ClampedArray
+        || array instanceof Uint16Array
+        || array instanceof Uint32Array
+        || array instanceof Float32Array
+        || array instanceof Float64Array;
+}
+
 function isResultCorrect(_actual, _expected)
 {
     if (_expected === 0)
@@ -141,7 +154,10 @@
         return true;
     if (typeof(_expected) == "number" && isNaN(_expected))
         return typeof(_actual) == "number" && isNaN(_actual);
-    if (_expected && (Object.prototype.toString.call(_expected) == Object.prototype.toString.call([])))
+    if (_expected
+        && (Object.prototype.toString.call(_expected) ==
+            Object.prototype.toString.call([])
+            || isTypedArray(_expected)))
         return areArraysEqual(_actual, _expected);
     return false;
 }
@@ -150,7 +166,10 @@
 {
     if (v === 0 && 1/v < 0)
         return "-0";
-    else return "" + v;
+    else if (isTypedArray(v))
+        return v.__proto__.constructor.name + ":[" + Array.prototype.join.call(v, ",") + "]";
+    else
+        return "" + v;
 }
 
 function evalAndLog(_a, _quiet)
@@ -185,15 +204,15 @@
   var _bv = eval(_b);
 
   if (exception)
-    testFailed(_a + " should be " + _bv + ". Threw exception " + exception);
+    testFailed(_a + " should be " + stringify(_bv) + ". Threw exception " + exception);
   else if (isResultCorrect(_av, _bv)) {
     if (!quiet) {
         testPassed(_a + " is " + _b);
     }
   } else if (typeof(_av) == typeof(_bv))
-    testFailed(_a + " should be " + _bv + ". Was " + stringify(_av) + ".");
+    testFailed(_a + " should be " + stringify(_bv) + ". Was " + stringify(_av) + ".");
   else
-    testFailed(_a + " should be " + _bv + " (of type " + typeof _bv + "). Was " + _av + " (of type " + typeof _av + ").");
+    testFailed(_a + " should be " + stringify(_bv) + " (of type " + typeof _bv + "). Was " + _av + " (of type " + typeof _av + ").");
 }
 
 // Execute condition every 5 milliseconds until it succeed or failureTime is reached.

Added: trunk/LayoutTests/fast/js/script-tests/typed-array-copy.js (0 => 154518)


--- trunk/LayoutTests/fast/js/script-tests/typed-array-copy.js	                        (rev 0)
+++ trunk/LayoutTests/fast/js/script-tests/typed-array-copy.js	2013-08-23 20:40:34 UTC (rev 154518)
@@ -0,0 +1,16 @@
+description(
+"Reduced test case for https://bugs.webkit.org/show_bug.cgi?id=83818"
+);
+
+a = new Uint16Array(8);
+
+b = new Uint8Array(a.buffer, 0, 2);
+b[0] = 0x05;
+b[1] = 0x05;
+
+shouldBe("a[0]", "0x0505");
+
+a.set(b);
+
+shouldBe("a[0]", "0x0005");
+shouldBe("a[1]", "0x0005");

Added: trunk/LayoutTests/fast/js/script-tests/typedarray-set-destination-smaller-than-source.js (0 => 154518)


--- trunk/LayoutTests/fast/js/script-tests/typedarray-set-destination-smaller-than-source.js	                        (rev 0)
+++ trunk/LayoutTests/fast/js/script-tests/typedarray-set-destination-smaller-than-source.js	2013-08-23 20:40:34 UTC (rev 154518)
@@ -0,0 +1,58 @@
+description(
+"Tests the code path in typedArray.set that may have to do a copy via an intermediate buffer because the source and destination overlap and have different size elements (source is larger than destination)."
+);
+
+function foo_reference(n) {
+    var array = new Int8Array(n);
+    for (var i = 0; i < n; ++i)
+        array[i] = 42 + i;
+    var array2 = new Int8Array(array);
+    array2.set(new Int32Array(array.buffer));
+    return array2;
+}
+
+function foo(n) {
+    var array = new Int8Array(n);
+    for (var i = 0; i < n; ++i)
+        array[i] = 42 + i;
+    array.set(new Int32Array(array.buffer));
+    return array;
+}
+
+function bar_reference(n) {
+    var array = new Int8Array(n);
+    for (var i = 0; i < n; ++i)
+        array[i] = 42 + i;
+    var array2 = new Int8Array(array);
+    array2.set(new Int32Array(array.buffer), n - n / 4);
+    return array2;
+}
+
+function bar(n) {
+    var array = new Int8Array(n);
+    for (var i = 0; i < n; ++i)
+        array[i] = 42 + i;
+    array.set(new Int32Array(array.buffer), n - n / 4);
+    return array;
+}
+
+function baz_reference(n) {
+    var array = new Int8Array(n);
+    for (var i = 0; i < n; ++i)
+        array[i] = 42 + i;
+    var array2 = new Int8Array(array);
+    array2.set(new Int32Array(array.buffer), n / 2 - (n / 4) / 2);
+    return array2;
+}
+
+function baz(n) {
+    var array = new Int8Array(n);
+    for (var i = 0; i < n; ++i)
+        array[i] = 42 + i;
+    array.set(new Int32Array(array.buffer), n / 2 - (n / 4) / 2);
+    return array;
+}
+
+shouldBe("foo(64)", "foo_reference(64)");
+shouldBe("bar(64)", "bar_reference(64)");
+shouldBe("baz(64)", "baz_reference(64)");

Added: trunk/LayoutTests/fast/js/script-tests/typedarray-set-overlapping-elements-of-same-size.js (0 => 154518)


--- trunk/LayoutTests/fast/js/script-tests/typedarray-set-overlapping-elements-of-same-size.js	                        (rev 0)
+++ trunk/LayoutTests/fast/js/script-tests/typedarray-set-overlapping-elements-of-same-size.js	2013-08-23 20:40:34 UTC (rev 154518)
@@ -0,0 +1,23 @@
+description(
+"Tests the code path of typedArray.set that tries to do a memmove-with-conversion for overlapping arrays."
+);
+
+function foo(n) {
+    var array = new Int32Array(n + 1);
+    for (var i = 0; i < n; ++i)
+        array[i] = 42 + i;
+    array.set(new Uint32Array(array.buffer, 0, n), 1);
+    return array;
+}
+
+function bar(n) {
+    var array = new Int32Array(n + 1);
+    for (var i = 0; i < n; ++i)
+        array[i + 1] = 42 + i;
+    array.set(new Uint32Array(array.buffer, 4), 0);
+    return array;
+}
+
+shouldBe("foo(10)", "[42,42,42,42,42,42,42,42,42,42,42]");
+shouldBe("bar(10)", "[42,43,44,45,46,47,48,49,50,51,51]");
+

Added: trunk/LayoutTests/fast/js/script-tests/typedarray-set-same-type-memmove.js (0 => 154518)


--- trunk/LayoutTests/fast/js/script-tests/typedarray-set-same-type-memmove.js	                        (rev 0)
+++ trunk/LayoutTests/fast/js/script-tests/typedarray-set-same-type-memmove.js	2013-08-23 20:40:34 UTC (rev 154518)
@@ -0,0 +1,27 @@
+description(
+"Tests the code path of typedArray.set that bottoms out in memmove."
+);
+
+function foo(n) {
+    var array = new Int32Array(n + 1);
+    for (var i = 0; i < n; ++i)
+        array[i] = 42 + i;
+    array.set(array.subarray(0, n), 1);
+    return array;
+}
+
+function bar(n) {
+    var array = new Int32Array(n + 1);
+    for (var i = 0; i < n; ++i)
+        array[i + 1] = 42 + i;
+    array.set(array.subarray(1, n + 1), 0);
+    return array;
+}
+
+function arraysEqual(a, b) {
+    if (a.length != b.length)
+        return false;
+}
+
+shouldBe("foo(10)", "[42,42,43,44,45,46,47,48,49,50,51]");
+shouldBe("bar(10)", "[42,43,44,45,46,47,48,49,50,51,51]");

Added: trunk/LayoutTests/fast/js/script-tests/typedarray-set-source-smaller-than-destination.js (0 => 154518)


--- trunk/LayoutTests/fast/js/script-tests/typedarray-set-source-smaller-than-destination.js	                        (rev 0)
+++ trunk/LayoutTests/fast/js/script-tests/typedarray-set-source-smaller-than-destination.js	2013-08-23 20:40:34 UTC (rev 154518)
@@ -0,0 +1,95 @@
+description(
+"Tests the code path in typedArray.set that may have to do a copy via an intermediate buffer because the source and destination overlap and have different size elements (source is smaller than destination)."
+);
+
+function foo_reference(n) {
+    var array = new Int32Array(n + 1);
+    for (var i = 0; i < n; ++i)
+        array[i] = 42 + i;
+    var array2 = new Int32Array(array);
+    array2.set(new Uint8Array(array.buffer, 0, n), 1);
+    return array2;
+}
+
+function foo(n) {
+    var array = new Int32Array(n + 1);
+    for (var i = 0; i < n; ++i)
+        array[i] = 42 + i;
+    array.set(new Uint8Array(array.buffer, 0, n), 1);
+    return array;
+}
+
+function bar_reference(n) {
+    var array = new Int32Array(n + 1);
+    for (var i = 0; i < n; ++i)
+        array[i + 1] = 42 + i;
+    var array2 = new Int32Array(array);
+    array2.set(new Uint8Array(array.buffer, (n + 1) * 4 - n), 0);
+    return array2;
+}
+
+function bar(n) {
+    var array = new Int32Array(n + 1);
+    for (var i = 0; i < n; ++i)
+        array[i + 1] = 42 + i;
+    array.set(new Uint8Array(array.buffer, (n + 1) * 4 - n), 0);
+    return array;
+}
+
+function baz_reference(n) {
+    var array = new Int32Array(n);
+    for (var i = 0; i < n; ++i)
+        array[i] = 42 + i;
+    var array2 = new Int32Array(array);
+    array2.set(new Uint8Array(array.buffer, 0, n));
+    return array2;
+}
+
+function baz(n) {
+    var array = new Int32Array(n);
+    for (var i = 0; i < n; ++i)
+        array[i] = 42 + i;
+    array.set(new Uint8Array(array.buffer, 0, n));
+    return array;
+}
+
+function fuz_reference(n) {
+    var array = new Int32Array(n);
+    for (var i = 0; i < n; ++i)
+        array[i] = 42 + i;
+    var array2 = new Int32Array(array);
+    array2.set(new Uint8Array(array.buffer, n * 4 - n));
+    return array2;
+}
+
+function fuz(n) {
+    var array = new Int32Array(n);
+    for (var i = 0; i < n; ++i)
+        array[i] = 42 + i;
+    array.set(new Uint8Array(array.buffer, n * 4 - n));
+    return array;
+}
+
+function thingy_reference(n) {
+    var array = new Int32Array(n);
+    for (var i = 0; i < n; ++i)
+        array[i] = 42 + i;
+    var array2 = new Int32Array(array);
+    array2.set(new Uint8Array(array.buffer, 4, n));
+    return array2;
+}
+
+function thingy(n) {
+    var array = new Int32Array(n);
+    for (var i = 0; i < n; ++i)
+        array[i] = 42 + i;
+    array.set(new Uint8Array(array.buffer, 4, n));
+    return array;
+}
+
+shouldBe("foo(10)", "foo_reference(10)");
+shouldBe("bar(10)", "bar_reference(10)");
+shouldBe("baz(10)", "baz_reference(10)");
+shouldBe("fuz(10)", "fuz_reference(10)");
+shouldBe("thingy(10)", "thingy_reference(10)");
+

Added: trunk/LayoutTests/fast/js/typed-array-copy-expected.txt (0 => 154518)


--- trunk/LayoutTests/fast/js/typed-array-copy-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/js/typed-array-copy-expected.txt	2013-08-23 20:40:34 UTC (rev 154518)
@@ -0,0 +1,12 @@
+Reduced test case for https://bugs.webkit.org/show_bug.cgi?id=83818
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS a[0] is 0x0505
+PASS a[0] is 0x0005
+PASS a[1] is 0x0005
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/js/typed-array-copy.html (0 => 154518)


--- trunk/LayoutTests/fast/js/typed-array-copy.html	                        (rev 0)
+++ trunk/LayoutTests/fast/js/typed-array-copy.html	2013-08-23 20:40:34 UTC (rev 154518)
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/js/typedarray-set-destination-smaller-than-source-expected.txt (0 => 154518)


--- trunk/LayoutTests/fast/js/typedarray-set-destination-smaller-than-source-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/js/typedarray-set-destination-smaller-than-source-expected.txt	2013-08-23 20:40:34 UTC (rev 154518)
@@ -0,0 +1,12 @@
+Tests the code path in typedArray.set that may have to do a copy via an intermediate buffer because the source and destination overlap and have different size elements (source is larger than destination).
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS foo(64) is foo_reference(64)
+PASS bar(64) is bar_reference(64)
+PASS baz(64) is baz_reference(64)
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/js/typedarray-set-destination-smaller-than-source.html (0 => 154518)


--- trunk/LayoutTests/fast/js/typedarray-set-destination-smaller-than-source.html	                        (rev 0)
+++ trunk/LayoutTests/fast/js/typedarray-set-destination-smaller-than-source.html	2013-08-23 20:40:34 UTC (rev 154518)
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/js/typedarray-set-overlapping-elements-of-same-size-expected.txt (0 => 154518)


--- trunk/LayoutTests/fast/js/typedarray-set-overlapping-elements-of-same-size-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/js/typedarray-set-overlapping-elements-of-same-size-expected.txt	2013-08-23 20:40:34 UTC (rev 154518)
@@ -0,0 +1,11 @@
+Tests the code path of typedArray.set that tries to do a memmove-with-conversion for overlapping arrays.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS foo(10) is [42,42,42,42,42,42,42,42,42,42,42]
+PASS bar(10) is [42,43,44,45,46,47,48,49,50,51,51]
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/js/typedarray-set-overlapping-elements-of-same-size.html (0 => 154518)


--- trunk/LayoutTests/fast/js/typedarray-set-overlapping-elements-of-same-size.html	                        (rev 0)
+++ trunk/LayoutTests/fast/js/typedarray-set-overlapping-elements-of-same-size.html	2013-08-23 20:40:34 UTC (rev 154518)
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/js/typedarray-set-same-type-memmove-expected.txt (0 => 154518)


--- trunk/LayoutTests/fast/js/typedarray-set-same-type-memmove-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/js/typedarray-set-same-type-memmove-expected.txt	2013-08-23 20:40:34 UTC (rev 154518)
@@ -0,0 +1,11 @@
+Tests the code path of typedArray.set that bottoms out in memmove.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS foo(10) is [42,42,43,44,45,46,47,48,49,50,51]
+PASS bar(10) is [42,43,44,45,46,47,48,49,50,51,51]
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/js/typedarray-set-same-type-memmove.html (0 => 154518)


--- trunk/LayoutTests/fast/js/typedarray-set-same-type-memmove.html	                        (rev 0)
+++ trunk/LayoutTests/fast/js/typedarray-set-same-type-memmove.html	2013-08-23 20:40:34 UTC (rev 154518)
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/js/typedarray-set-source-smaller-than-destination-expected.txt (0 => 154518)


--- trunk/LayoutTests/fast/js/typedarray-set-source-smaller-than-destination-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/js/typedarray-set-source-smaller-than-destination-expected.txt	2013-08-23 20:40:34 UTC (rev 154518)
@@ -0,0 +1,14 @@
+Tests the code path in typedArray.set that may have to do a copy via an intermediate buffer because the source and destination overlap and have different size elements (source is smaller than destination).
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS foo(10) is foo_reference(10)
+PASS bar(10) is bar_reference(10)
+PASS baz(10) is baz_reference(10)
+PASS fuz(10) is fuz_reference(10)
+PASS thingy(10) is thingy_reference(10)
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/js/typedarray-set-source-smaller-than-destination.html (0 => 154518)


--- trunk/LayoutTests/fast/js/typedarray-set-source-smaller-than-destination.html	                        (rev 0)
+++ trunk/LayoutTests/fast/js/typedarray-set-source-smaller-than-destination.html	2013-08-23 20:40:34 UTC (rev 154518)
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+</body>
+</html>

Modified: trunk/Source/_javascript_Core/ChangeLog (154517 => 154518)


--- trunk/Source/_javascript_Core/ChangeLog	2013-08-23 20:35:09 UTC (rev 154517)
+++ trunk/Source/_javascript_Core/ChangeLog	2013-08-23 20:40:34 UTC (rev 154518)
@@ -1,3 +1,29 @@
+2013-08-23  Filip Pizlo  <[email protected]>
+
+        Incorrect TypedArray#set behavior
+        https://bugs.webkit.org/show_bug.cgi?id=83818
+
+        Reviewed by Oliver Hunt and Mark Hahnenberg.
+        
+        This was so much fun! typedArray.set() is like a memmove on steroids, and I'm
+        not smart enough to figure out optimal versions for *all* of the cases. But I
+        did come up with optimal implementations for most of the cases, and I wrote
+        spec-literal code (i.e. copy via a transfer buffer) for the cases I'm not smart
+        enough to write optimal code for.
+
+        * runtime/JSArrayBufferView.h:
+        (JSC::JSArrayBufferView::hasArrayBuffer):
+        * runtime/JSArrayBufferViewInlines.h:
+        (JSC::JSArrayBufferView::buffer):
+        (JSC::JSArrayBufferView::existingBufferInButterfly):
+        (JSC::JSArrayBufferView::neuter):
+        (JSC::JSArrayBufferView::byteOffset):
+        * runtime/JSGenericTypedArrayView.h:
+        * runtime/JSGenericTypedArrayViewInlines.h:
+        (JSC::::setWithSpecificType):
+        (JSC::::set):
+        (JSC::::existingBuffer):
+
 2013-08-23  Alex Christensen  <[email protected]>
 
         Re-separating Win32 and Win64 builds.

Modified: trunk/Source/_javascript_Core/runtime/JSArrayBufferView.h (154517 => 154518)


--- trunk/Source/_javascript_Core/runtime/JSArrayBufferView.h	2013-08-23 20:35:09 UTC (rev 154517)
+++ trunk/Source/_javascript_Core/runtime/JSArrayBufferView.h	2013-08-23 20:40:34 UTC (rev 154518)
@@ -154,6 +154,8 @@
     
 public:
     TypedArrayMode mode() const { return m_mode; }
+    bool hasArrayBuffer() const { return JSC::hasArrayBuffer(mode()); }
+    
     ArrayBuffer* buffer();
     PassRefPtr<ArrayBufferView> impl();
     void neuter();
@@ -173,6 +175,8 @@
 
 protected:
     static const unsigned StructureFlags = OverridesGetPropertyNames | OverridesGetOwnPropertySlot | Base::StructureFlags;
+    
+    ArrayBuffer* existingBufferInButterfly();
 
     void* m_vector;
     uint32_t m_length;

Modified: trunk/Source/_javascript_Core/runtime/JSArrayBufferViewInlines.h (154517 => 154518)


--- trunk/Source/_javascript_Core/runtime/JSArrayBufferViewInlines.h	2013-08-23 20:35:09 UTC (rev 154517)
+++ trunk/Source/_javascript_Core/runtime/JSArrayBufferViewInlines.h	2013-08-23 20:40:34 UTC (rev 154518)
@@ -36,7 +36,7 @@
 {
     switch (m_mode) {
     case WastefulTypedArray:
-        return butterfly()->indexingHeader()->arrayBuffer();
+        return existingBufferInButterfly();
     case DataViewMode:
         return jsCast<JSDataView*>(this)->buffer();
     default:
@@ -44,6 +44,12 @@
     }
 }
 
+inline ArrayBuffer* JSArrayBufferView::existingBufferInButterfly()
+{
+    ASSERT(m_mode == WastefulTypedArray);
+    return butterfly()->indexingHeader()->arrayBuffer();
+}
+
 inline PassRefPtr<ArrayBufferView> JSArrayBufferView::impl()
 {
     return methodTable()->getTypedArrayImpl(this);
@@ -51,14 +57,14 @@
 
 inline void JSArrayBufferView::neuter()
 {
-    ASSERT(hasArrayBuffer(m_mode));
+    ASSERT(hasArrayBuffer());
     m_length = 0;
     m_vector = 0;
 }
 
 inline unsigned JSArrayBufferView::byteOffset()
 {
-    if (!hasArrayBuffer(m_mode))
+    if (!hasArrayBuffer())
         return 0;
     
     ptrdiff_t delta =

Modified: trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayView.h (154517 => 154518)


--- trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayView.h	2013-08-23 20:35:09 UTC (rev 154517)
+++ trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayView.h	2013-08-23 20:40:34 UTC (rev 154518)
@@ -173,10 +173,6 @@
     // appropriate exception.
     bool validateRange(ExecState*, unsigned offset, unsigned length);
     
-    // Returns true if successful, and false on error; it will throw on error.
-    template<typename OtherType>
-    bool setWithSpecificType(ExecState*, OtherType*, unsigned offset, unsigned length);
-    
     // Returns true if successful, and false on error; if it returns false
     // then it will have thrown an exception.
     bool set(ExecState*, JSObject*, unsigned offset, unsigned length);
@@ -220,6 +216,8 @@
         }
     }
     
+    ArrayBuffer* existingBuffer();
+
     static const TypedArrayType TypedArrayStorageType = Adaptor::typeValue;
     
 protected:
@@ -246,6 +244,11 @@
     // necessary. Note that this never allocates in the GC heap.
     static ArrayBuffer* slowDownAndWasteMemory(JSArrayBufferView*);
     static PassRefPtr<ArrayBufferView> getTypedArrayImpl(JSArrayBufferView*);
+
+private:
+    // Returns true if successful, and false on error; it will throw on error.
+    template<typename OtherType>
+    bool setWithSpecificType(ExecState*, OtherType*, unsigned offset, unsigned length);
 };
 
 template<typename Adaptor>

Modified: trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewInlines.h (154517 => 154518)


--- trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewInlines.h	2013-08-23 20:35:09 UTC (rev 154517)
+++ trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewInlines.h	2013-08-23 20:40:34 UTC (rev 154518)
@@ -151,8 +151,55 @@
         return false;
     }
     
+    // This method doesn't support copying between the same array. Note that
+    // set() will only call this if the types differ, which implicitly guarantees
+    // that we can't be the same array. This is relevant because the way we detect
+    // non-overlapping is by checking if either (a) either array doesn't have a
+    // backing buffer or (b) the backing buffers are different, but that doesn't
+    // catch the case where it's the *same* array - fortunately though, this code
+    // path never needs to worry about that case.
+    ASSERT(static_cast<JSCell*>(this) != static_cast<JSCell*>(other));
+    
+    // 1) If the two arrays are non-overlapping, we can copy in any order we like
+    //    and we don't need an intermediate buffer. Arrays are definitely
+    //    non-overlapping if either one of them has no backing buffer (that means
+    //    that it *owns* its philosophical backing buffer) or if they have
+    //    different backing buffers.
+    // 2) If the two arrays overlap but have the same element size, we can do a
+    //    memmove-like copy where we flip-flop direction based on which vector
+    //    starts before the other:
+    //    A) If the destination vector is before the source vector, then a forward
+    //       copy is in order.
+    //    B) If the destination vector is after the source vector, then a backward
+    //       copy is in order.
+    // 3) If we have different element sizes and there is a chance of overlap then
+    //    we need an intermediate vector.
+    
+    // NB. Comparisons involving elementSize will be constant-folded by template
+    // specialization.
+    
+    // Handle cases (1) and (2B).
+    if (!hasArrayBuffer() || !other->hasArrayBuffer()
+        || existingBuffer() != other->existingBuffer()
+        || (elementSize == OtherType::elementSize && vector() > other->vector())) {
+        for (unsigned i = length; i--;)
+            setIndexQuickly(offset + i, other->getIndexQuickly(i));
+        return true;
+    }
+    
+    // Now we either have (2A) or (3) - so first we try to cover (2A).
+    if (elementSize == OtherType::elementSize) {
+        for (unsigned i = 0; i < length; ++i)
+            setIndexQuickly(offset + i, other->getIndexQuickly(i));
+        return true;
+    }
+    
+    // Fail: we need an intermediate transfer buffer (i.e. case (3)).
+    Vector<typename Adaptor::Type, 32> transferBuffer(length);
     for (unsigned i = length; i--;)
-        setIndexQuickly(offset + i, other->getIndexQuickly(i));
+        transferBuffer[i] = Adaptor::toNative(other->getIndexQuickly(i));
+    for (unsigned i = length; i--;)
+        setIndexQuicklyToNativeValue(offset + i, transferBuffer[i]);
     
     return true;
 }
@@ -170,7 +217,7 @@
         if (!validateRange(exec, offset, length))
             return false;
         
-        memcpy(typedVector() + offset, other->typedVector(), other->byteLength());
+        memmove(typedVector() + offset, other->typedVector(), other->byteLength());
         return true;
     }
     
@@ -210,6 +257,12 @@
 }
 
 template<typename Adaptor>
+ArrayBuffer* JSGenericTypedArrayView<Adaptor>::existingBuffer()
+{
+    return existingBufferInButterfly();
+}
+
+template<typename Adaptor>
 bool JSGenericTypedArrayView<Adaptor>::getOwnPropertySlot(
     JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
 {
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to