REGRESSION(r227340): ArrayBuffers were not being serialized when sent via MessagePorts
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 19 Apr 2018 23:59:50 +0000 (23:59 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 19 Apr 2018 23:59:50 +0000 (23:59 +0000)
https://bugs.webkit.org/show_bug.cgi?id=184254
<rdar://problem/39140200>

Patch by Tadeu Zagallo <tzagallo@apple.com> on 2018-04-19
Reviewed by Daniel Bates.

Source/JavaScriptCore:

Expose an extra constructor of ArrayBufferContents in order to be able to decode SerializedScriptValues.

* runtime/ArrayBuffer.h:
(JSC::ArrayBufferContents::ArrayBufferContents):

Source/WebCore:

Add a new encoding method to SerializedScriptValue that includes ArrayBuffers.

Test: workers/message-port.html

* bindings/js/SerializedScriptValue.h:
(WebCore::SerializedScriptValue::encode const):
(WebCore::SerializedScriptValue::decode):
* dom/messageports/MessageWithMessagePorts.h:
(WebCore::MessageWithMessagePorts::encode const):
(WebCore::MessageWithMessagePorts::decode):

LayoutTests:

The regression test provided with the bug report verifies that the ArrayBuffer is properly
serialized - before, the whole data object would be null.
Test case provided by Yann Cabon <ycabon@esri.com> as part of the bug report.

* workers/message-port-expected.txt: Added.
* workers/message-port.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@230828 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/workers/message-port-expected.txt [new file with mode: 0644]
LayoutTests/workers/message-port.html [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/ArrayBuffer.h
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/SerializedScriptValue.cpp
Source/WebCore/bindings/js/SerializedScriptValue.h
Source/WebCore/dom/messageports/MessageWithMessagePorts.h

index 86fb6bb..4959962 100644 (file)
@@ -1,3 +1,18 @@
+2018-04-19  Tadeu Zagallo  <tzagallo@apple.com>
+
+        REGRESSION(r227340): ArrayBuffers were not being serialized when sent via MessagePorts
+        https://bugs.webkit.org/show_bug.cgi?id=184254
+        <rdar://problem/39140200>
+
+        Reviewed by Daniel Bates.
+
+        The regression test provided with the bug report verifies that the ArrayBuffer is properly
+        serialized - before, the whole data object would be null.
+        Test case provided by Yann Cabon <ycabon@esri.com> as part of the bug report.
+
+        * workers/message-port-expected.txt: Added.
+        * workers/message-port.html: Added.
+
 2018-04-19  Eric Carlson  <eric.carlson@apple.com>
 
         Runtime logging during GC can cause crash
diff --git a/LayoutTests/workers/message-port-expected.txt b/LayoutTests/workers/message-port-expected.txt
new file mode 100644 (file)
index 0000000..770ec01
--- /dev/null
@@ -0,0 +1,12 @@
+Sends ArrayBuffers through MessagePorts.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS array[0] contains 3.141592653589793
+PASS successfully transferred array of length 0
+PASS successfully transferred array of length 0
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/workers/message-port.html b/LayoutTests/workers/message-port.html
new file mode 100644 (file)
index 0000000..b92b62d
--- /dev/null
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../resources/js-test.js"></script>
+</head>
+<body>
+<script>
+description("Sends ArrayBuffers through MessagePorts.");
+window.jsTestIsAsync = true;
+
+function test(data, postMessage, checkResult) {
+    return new Promise(function (resolve) {
+        const channel = new MessageChannel();
+
+        channel.port1.onmessage = event => {
+            if (!event.data)
+                debug("message data null!");
+            postMessage(event.data, event.target);
+        };
+
+        channel.port2.onmessage = event => {
+            checkResult(event.data);
+            resolve();
+        };
+
+        postMessage(data, channel.port2);
+    });
+}
+
+function testEmptyArray(buf) {
+    const array = new Float64Array(buf);
+    if (!array.length)
+        testPassed(`successfully transferred array of length 0`);
+    else
+        testFailed(`${array} should be a Float64Array of length 0`);
+}
+
+const array = new Float64Array([Math.PI]);
+const emptyArray = new Float64Array();
+const emptyArray2 = new Float64Array();
+
+test(
+    { buf: [array.buffer, emptyArray.buffer] },
+    (data, port) => port.postMessage({ buf: data.buf }, data.buf),
+    data => {
+        const array = new Float64Array(data.buf[0]);
+        if (array[0] === Math.PI)
+            testPassed(`array[0] contains ${Math.PI}`);
+        else
+            testFailed(`${array[0]} should be ${Math.PI}`);
+
+        testEmptyArray(data.buf[1]);
+    }
+).then(() =>
+    test(
+        { buf: emptyArray2.buffer },
+        (data, port) => port.postMessage({ buf: data.buf }, [data.buf]),
+        data => testEmptyArray(data.buf)
+    ),
+).then(finishJSTest);
+</script>
+</body>
+</html>
index a652117..29b21bb 100644 (file)
@@ -1,3 +1,16 @@
+2018-04-19  Tadeu Zagallo  <tzagallo@apple.com>
+
+        REGRESSION(r227340): ArrayBuffers were not being serialized when sent via MessagePorts
+        https://bugs.webkit.org/show_bug.cgi?id=184254
+        <rdar://problem/39140200>
+
+        Reviewed by Daniel Bates.
+
+        Expose an extra constructor of ArrayBufferContents in order to be able to decode SerializedScriptValues.
+
+        * runtime/ArrayBuffer.h:
+        (JSC::ArrayBufferContents::ArrayBufferContents):
+
 2018-04-19  Mark Lam  <mark.lam@apple.com>
 
         Apply pointer profiling to Signal pointers.
index 18725cc..fc027ea 100644 (file)
@@ -59,6 +59,7 @@ class ArrayBufferContents {
     WTF_MAKE_NONCOPYABLE(ArrayBufferContents);
 public:
     JS_EXPORT_PRIVATE ArrayBufferContents();
+    JS_EXPORT_PRIVATE ArrayBufferContents(void* data, unsigned sizeInBytes, ArrayBufferDestructorFunction&&);
     
     JS_EXPORT_PRIVATE ArrayBufferContents(ArrayBufferContents&&);
     JS_EXPORT_PRIVATE ArrayBufferContents& operator=(ArrayBufferContents&&);
@@ -73,10 +74,8 @@ public:
     unsigned sizeInBytes() const { return m_sizeInBytes; }
     
     bool isShared() const { return m_shared; }
-
-private:
-    ArrayBufferContents(void* data, unsigned sizeInBytes, ArrayBufferDestructorFunction&&);
     
+private:
     void destroy();
     void reset();
 
index 58cc5af..10c8fc3 100644 (file)
@@ -1,3 +1,22 @@
+2018-04-19  Tadeu Zagallo  <tzagallo@apple.com>
+
+        REGRESSION(r227340): ArrayBuffers were not being serialized when sent via MessagePorts
+        https://bugs.webkit.org/show_bug.cgi?id=184254
+        <rdar://problem/39140200>
+
+        Reviewed by Daniel Bates.
+
+        Add a new encoding method to SerializedScriptValue that includes ArrayBuffers.
+
+        Test: workers/message-port.html
+
+        * bindings/js/SerializedScriptValue.h:
+        (WebCore::SerializedScriptValue::encode const):
+        (WebCore::SerializedScriptValue::decode):
+        * dom/messageports/MessageWithMessagePorts.h:
+        (WebCore::MessageWithMessagePorts::encode const):
+        (WebCore::MessageWithMessagePorts::decode):
+
 2018-04-19  David Kilzer  <ddkilzer@apple.com>
 
         Enable Objective-C weak references
index 58e39e9..bb132fd 100644 (file)
@@ -3169,6 +3169,12 @@ SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>&& buffer)
 {
 }
 
+SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>&& buffer, std::unique_ptr<ArrayBufferContentsArray> arrayBufferContentsArray)
+    : m_data(WTFMove(buffer))
+    , m_arrayBufferContentsArray(WTFMove(arrayBufferContentsArray))
+{
+}
+
 SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>&& buffer, const Vector<String>& blobURLs, std::unique_ptr<ArrayBufferContentsArray> arrayBufferContentsArray, std::unique_ptr<ArrayBufferContentsArray> sharedBufferContentsArray, Vector<std::pair<std::unique_ptr<ImageBuffer>, bool>>&& imageBuffers
 #if ENABLE(WEBASSEMBLY)
         , std::unique_ptr<WasmModuleArray> wasmModulesArray
index 43d1304..2352904 100644 (file)
@@ -33,6 +33,7 @@
 #include <JavaScriptCore/Strong.h>
 #include <wtf/Forward.h>
 #include <wtf/Function.h>
+#include <wtf/Gigacage.h>
 #include <wtf/text/WTFString.h>
 
 typedef const struct OpaqueJSContext* JSContextRef;
@@ -101,10 +102,14 @@ public:
     }
     const Vector<uint8_t>& toWireBytes() const { return m_data; }
 
+    template<class Encoder> void encode(Encoder&) const;
+    template<class Decoder> static RefPtr<SerializedScriptValue> decode(Decoder&);
+
     WEBCORE_EXPORT ~SerializedScriptValue();
 
 private:
     WEBCORE_EXPORT SerializedScriptValue(Vector<unsigned char>&&);
+    WEBCORE_EXPORT SerializedScriptValue(Vector<unsigned char>&&, std::unique_ptr<ArrayBufferContentsArray>);
     SerializedScriptValue(Vector<unsigned char>&&, const Vector<String>& blobURLs, std::unique_ptr<ArrayBufferContentsArray>, std::unique_ptr<ArrayBufferContentsArray> sharedBuffers, Vector<std::pair<std::unique_ptr<ImageBuffer>, bool>>&& imageBuffers
 #if ENABLE(WEBASSEMBLY)
         , std::unique_ptr<WasmModuleArray>
@@ -121,4 +126,62 @@ private:
     Vector<String> m_blobURLs;
 };
 
+template<class Encoder>
+void SerializedScriptValue::encode(Encoder& encoder) const
+{
+    encoder << m_data;
+
+    auto hasArray = m_arrayBufferContentsArray && m_arrayBufferContentsArray->size();
+    encoder << hasArray;
+
+    if (!hasArray)
+        return;
+
+    encoder << static_cast<uint64_t>(m_arrayBufferContentsArray->size());
+    for (const auto &arrayBufferContents : *m_arrayBufferContentsArray) {
+        encoder << arrayBufferContents.sizeInBytes();
+        encoder.encodeFixedLengthData(static_cast<const uint8_t*>(arrayBufferContents.data()), arrayBufferContents.sizeInBytes(), 1);
+    }
+}
+
+template<class Decoder>
+RefPtr<SerializedScriptValue> SerializedScriptValue::decode(Decoder& decoder)
+{
+    Vector<uint8_t> data;
+    if (!decoder.decode(data))
+        return nullptr;
+
+    bool hasArray;
+    if (!decoder.decode(hasArray))
+        return nullptr;
+
+    if (!hasArray)
+        return adoptRef(*new SerializedScriptValue(WTFMove(data)));
+
+    uint64_t arrayLength;
+    if (!decoder.decode(arrayLength))
+        return nullptr;
+    ASSERT(arrayLength);
+
+    auto arrayBufferContentsArray = std::make_unique<ArrayBufferContentsArray>();
+    while (arrayLength--) {
+        unsigned bufferSize;
+        if (!decoder.decode(bufferSize))
+            return nullptr;
+
+        auto buffer = Gigacage::tryMalloc(Gigacage::Primitive, bufferSize);
+        auto destructor = [] (void* ptr) {
+            Gigacage::free(Gigacage::Primitive, ptr);
+        };
+        if (!decoder.decodeFixedLengthData(static_cast<uint8_t*>(buffer), bufferSize, 1)) {
+            destructor(buffer);
+            return nullptr;
+        }
+        arrayBufferContentsArray->append({ buffer, bufferSize, WTFMove(destructor) });
+    }
+
+    return adoptRef(*new SerializedScriptValue(WTFMove(data), WTFMove(arrayBufferContentsArray)));
+}
+
+
 }
index 7cd6068..8dedf4e 100644 (file)
@@ -48,7 +48,7 @@ template<class Encoder>
 void MessageWithMessagePorts::encode(Encoder& encoder) const
 {
     ASSERT(message);
-    encoder << message->toWireBytes() << transferredPorts;
+    encoder << *message << transferredPorts;
 }
 
 template<class Decoder>
@@ -56,14 +56,13 @@ std::optional<MessageWithMessagePorts> MessageWithMessagePorts::decode(Decoder&
 {
     MessageWithMessagePorts result;
 
-    Vector<uint8_t> wireBytes;
-    if (!decoder.decode(wireBytes))
+    result.message = SerializedScriptValue::decode(decoder);
+    if (!result.message)
         return std::nullopt;
 
     if (!decoder.decode(result.transferredPorts))
         return std::nullopt;
 
-    result.message = SerializedScriptValue::createFromWireBytes(WTFMove(wireBytes));
     return result;
 }