Bug 1312817 - support {window,worker}.postMessage() of WebAssembly.Module, r=qdot, r=luke
☠☠ backed out by 79635ac69f44 ☠ ☠
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 27 Oct 2016 20:50:23 +0200
changeset 319969 19e79becc54001c1f887133e78ea92d215bad4b9
parent 319968 6abb6ebe96b7417191c39f28750947ef85f5f618
child 319970 ad24f084859f9ebf5a1a99a7470d6abb513d8d61
push id20749
push userryanvm@gmail.com
push dateSat, 29 Oct 2016 13:21:21 +0000
treeherderfx-team@1b170b39ed6b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersqdot, luke
bugs1312817
milestone52.0a1
Bug 1312817 - support {window,worker}.postMessage() of WebAssembly.Module, r=qdot, r=luke
dom/base/StructuredCloneHolder.cpp
dom/base/StructuredCloneHolder.h
dom/base/test/test_postMessages.html
dom/ipc/StructuredCloneData.cpp
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -992,16 +992,51 @@ WriteFormData(JSStructuredCloneWriter* a
 
       return true;
     }
   };
   Closure closure(aWriter, aHolder);
   return aFormData->ForEach(Closure::Write, &closure);
 }
 
+JSObject*
+ReadWasmModule(JSContext* aCx,
+               uint32_t aIndex,
+               StructuredCloneHolder* aHolder)
+{
+  MOZ_ASSERT(aHolder);
+  MOZ_ASSERT(aIndex < aHolder->WasmModules().Length());
+  MOZ_ASSERT(aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessSameThread ||
+             aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessDifferentThread);
+
+  RefPtr<JS::WasmModule> wasmModule = aHolder->WasmModules()[aIndex];
+  return wasmModule->createObject(aCx);
+}
+
+bool
+WriteWasmModule(JSStructuredCloneWriter* aWriter,
+                JS::WasmModule* aWasmModule,
+                StructuredCloneHolder* aHolder)
+{
+  MOZ_ASSERT(aWriter);
+  MOZ_ASSERT(aWasmModule);
+  MOZ_ASSERT(aHolder);
+  MOZ_ASSERT(aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessSameThread ||
+             aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessDifferentThread);
+
+  // We store the position of the wasmModule in the array as index.
+  if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_WASM,
+                         aHolder->WasmModules().Length())) {
+    aHolder->WasmModules().AppendElement(aWasmModule);
+    return true;
+  }
+
+  return false;
+}
+
 } // anonymous namespace
 
 JSObject*
 StructuredCloneHolder::CustomReadHandler(JSContext* aCx,
                                          JSStructuredCloneReader* aReader,
                                          uint32_t aTag,
                                          uint32_t aIndex)
 {
@@ -1028,17 +1063,21 @@ StructuredCloneHolder::CustomReadHandler
                mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread);
 
     // Get the current global object.
     // This can be null.
     nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
     // aIndex is the index of the cloned image.
     return ImageBitmap::ReadStructuredClone(aCx, aReader,
                                             parent, GetSurfaces(), aIndex);
-   }
+  }
+
+  if (aTag == SCTAG_DOM_WASM) {
+    return ReadWasmModule(aCx, aIndex, this);
+  }
 
   return ReadFullySerializableObjects(aCx, aReader, aTag);
 }
 
 bool
 StructuredCloneHolder::CustomWriteHandler(JSContext* aCx,
                                           JSStructuredCloneWriter* aWriter,
                                           JS::Handle<JSObject*> aObj)
@@ -1090,16 +1129,26 @@ StructuredCloneHolder::CustomWriteHandle
     ImageBitmap* imageBitmap = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, aObj, imageBitmap))) {
       return ImageBitmap::WriteStructuredClone(aWriter,
                                                GetSurfaces(),
                                                imageBitmap);
     }
   }
 
+  // See if this is a WasmModule.
+  if ((mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
+       mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) &&
+      JS::IsWasmModuleObject(aObj)) {
+    RefPtr<JS::WasmModule> module = JS::GetWasmModule(aObj);
+    MOZ_ASSERT(module);
+
+    return WriteWasmModule(aWriter, module, this);
+  }
+
   return WriteFullySerializableObjects(aCx, aWriter, aObj);
 }
 
 bool
 StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx,
                                                  JSStructuredCloneReader* aReader,
                                                  uint32_t aTag,
                                                  void* aContent,
--- a/dom/base/StructuredCloneHolder.h
+++ b/dom/base/StructuredCloneHolder.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_StructuredCloneHolder_h
 #define mozilla_dom_StructuredCloneHolder_h
 
+#include "jsapi.h"
 #include "js/StructuredClone.h"
 #include "mozilla/Move.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsISupports.h"
 #include "nsTArray.h"
 
 #ifdef DEBUG
@@ -173,25 +174,32 @@ public:
             JSContext* aCx,
             JS::MutableHandle<JS::Value> aValue,
             ErrorResult &aRv);
 
   // Call this method to know if this object is keeping some DOM object alive.
   bool HasClonedDOMObjects() const
   {
     return !mBlobImplArray.IsEmpty() ||
+           !mWasmModuleArray.IsEmpty() ||
            !mClonedSurfaces.IsEmpty();
   }
 
   nsTArray<RefPtr<BlobImpl>>& BlobImpls()
   {
     MOZ_ASSERT(mSupportsCloning, "Blobs cannot be taken/set if cloning is not supported.");
     return mBlobImplArray;
   }
 
+  nsTArray<RefPtr<JS::WasmModule>>& WasmModules()
+  {
+    MOZ_ASSERT(mSupportsCloning, "WasmModules cannot be taken/set if cloning is not supported.");
+    return mWasmModuleArray;
+  }
+
   StructuredCloneScope CloneScope() const
   {
     return mStructuredCloneScope;
   }
 
   // The parent object is set internally just during the Read(). This method
   // can be used by read functions to retrieve it.
   nsISupports* ParentDuringRead() const
@@ -287,16 +295,19 @@ protected:
                       ErrorResult &aRv);
 
   bool mSupportsCloning;
   bool mSupportsTransferring;
 
   // Used for cloning blobs in the structured cloning algorithm.
   nsTArray<RefPtr<BlobImpl>> mBlobImplArray;
 
+  // Used for cloning JS::WasmModules in the structured cloning algorithm.
+  nsTArray<RefPtr<JS::WasmModule>> mWasmModuleArray;
+
   // This is used for sharing the backend of ImageBitmaps.
   // The DataSourceSurface object must be thread-safely reference-counted.
   // The DataSourceSurface object will not be written ever via any ImageBitmap
   // instance, so no race condition will occur.
   nsTArray<RefPtr<gfx::DataSourceSurface>> mClonedSurfaces;
 
   // This raw pointer is only set within ::Read() and is unset by the end.
   nsISupports* MOZ_NON_OWNING_REF mParent;
--- a/dom/base/test/test_postMessages.html
+++ b/dom/base/test/test_postMessages.html
@@ -6,23 +6,27 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 
 <body>
 <input id="fileList" type="file"></input>
 <script type="application/javascript;version=1.7">
 
 function setup_tests() {
-  SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true]]}, next);
+  SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true],
+                                     ["javascript.options.wasm", true]]}, next);
 }
 
 function getType(a) {
   if (a === null || a === undefined)
     return 'null';
 
+  if (a instanceof WebAssembly.Module)
+    return "wasm";
+
   if (Array.isArray(a))
     return 'array';
 
   if (typeof a == 'object')
     return 'object';
 
   return 'primitive';
 }
@@ -54,46 +58,57 @@ function compare(a, b) {
 
     for (var p in a) {
       compare(a[p], b[p]);
     }
 
     return;
   }
 
+  if (type == 'wasm') {
+    var wasmA = new WebAssembly.Instance(a);
+    ok(wasmA instanceof WebAssembly.Instance, "got an instance");
+
+    var wasmB = new WebAssembly.Instance(b);
+    ok(wasmB instanceof WebAssembly.Instance, "got an instance");
+
+    ok(wasmA.exports.foo() === wasmB.exports.foo(), "Same result!");
+    ok(wasmB.exports.foo() === 42, "We want 42");
+  }
+
   if (type != 'null') {
     is (a.toSource(), b.toSource(), 'Matching using toSource()');
   }
 }
 
 var clonableObjects = [
-  'hello world',
-  123,
-  null,
-  true,
-  new Date(),
-  [ 1, 'test', true, new Date() ],
-  { a: true, b:  null, c: new Date(), d: [ true, false, {} ] },
-  new Blob([123], { type: 'plain/text' }),
-  new ImageData(2, 2),
+  { target: 'all', data: 'hello world' },
+  { target: 'all', data: 123 },
+  { target: 'all', data: null },
+  { target: 'all', data: true },
+  { target: 'all', data: new Date() },
+  { target: 'all', data: [ 1, 'test', true, new Date() ] },
+  { target: 'all', data: { a: true, b:  null, c: new Date(), d: [ true, false, {} ] } },
+  { target: 'all', data: new Blob([123], { type: 'plain/text' }) },
+  { target: 'all', data: new ImageData(2, 2) },
 ];
 
 function create_fileList() {
   var url = SimpleTest.getTestFileURL("script_postmessages_fileList.js");
   var script = SpecialPowers.loadChromeScript(url);
 
   function onOpened(message) {
     var fileList = document.getElementById('fileList');
     SpecialPowers.wrap(fileList).mozSetFileArray([message.file]);
 
     // Just a simple test
     var domFile = fileList.files[0];
     is(domFile.name, "prefs.js", "fileName should be prefs.js");
 
-    clonableObjects.push(fileList.files);
+    clonableObjects.push({ target: 'all', data: fileList.files });
     script.destroy();
     next();
   }
 
   script.addMessageListener("file.opened", onOpened);
   script.sendAsyncMessage("file.open");
 }
 
@@ -110,56 +125,120 @@ function create_directory() {
     var fileList = document.getElementById('fileList');
     SpecialPowers.wrap(fileList).mozSetDirectory(message.dir);
 
     fileList.getFilesAndDirectories().then(function(list) {
       // Just a simple test
       is(list.length, 1, "This list has 1 element");
       ok(list[0] instanceof Directory, "We have a directory.");
 
-      clonableObjects.push(list[0]);
+      clonableObjects.push({ target: 'all', data: list[0] });
       script.destroy();
       next();
     });
   }
 
   script.addMessageListener("dir.opened", onOpened);
   script.sendAsyncMessage("dir.open");
 }
 
+function create_wasmModule() {
+  info("Checking if we can play with WebAssembly...");
+
+  if (!SpecialPowers.Cu.getJSTestingFunctions().wasmIsSupported()) {
+    next();
+    return;
+  }
+
+  ok(WebAssembly, "WebAssembly object should exist");
+  ok(WebAssembly.compile, "WebAssembly.compile function should exist");
+
+  const wasmTextToBinary = SpecialPowers.unwrap(SpecialPowers.Cu.getJSTestingFunctions().wasmTextToBinary);
+  const fooModuleCode = wasmTextToBinary(`(module
+    (func $foo (result i32) (i32.const 42))
+    (export "foo" $foo)
+  )`, 'new-format');
+
+  WebAssembly.compile(fooModuleCode).then((m) => {
+    ok(m instanceof WebAssembly.Module, "The WasmModule has been compiled.");
+    clonableObjects.push({ target: 'sameProcess', data: m });
+    next();
+  }, () => {
+    ok(false, "The compilation of the wasmModule failed.");
+  });
+}
+
 function runTests(obj) {
-  ok(('clonableObjects' in obj) &&
+  ok(('clonableObjectsEveryWhere' in obj) &&
+     ('clonableObjectsSameProcess' in obj) &&
      ('transferableObjects' in obj) &&
-     (obj.clonableObjects || obj.transferableObjects), "We must run some test!");
+     (obj.clonableObjectsEveryWhere || obj.clonableObjectsSameProcess || obj.transferableObjects), "We must run some test!");
 
-  // cloning tests
+  // cloning tests - everyWhere
   new Promise(function(resolve, reject) {
-    if (!obj.clonableObjects) {
+    if (!obj.clonableObjectsEveryWhere) {
       resolve();
       return;
     }
 
     var clonableObjectsId = 0;
     function runClonableTest() {
       if (clonableObjectsId >= clonableObjects.length) {
         resolve();
         return;
       }
 
       var object = clonableObjects[clonableObjectsId++];
 
-      obj.send(object, []).then(function(received) {
-        compare(received.data, object);
+      if (object.target != 'all') {
+        runClonableTest();
+        return;
+      }
+
+      obj.send(object.data, []).then(function(received) {
+        compare(received.data, object.data);
         runClonableTest();
       });
     }
 
     runClonableTest();
   })
 
+  // clonable same process
+  .then(function() {
+    return new Promise(function(resolve, reject) {
+      if (!obj.clonableObjectsSameProcess) {
+        resolve();
+        return;
+      }
+
+      var clonableObjectsId = 0;
+      function runClonableTest() {
+        if (clonableObjectsId >= clonableObjects.length) {
+          resolve();
+          return;
+        }
+
+        var object = clonableObjects[clonableObjectsId++];
+
+        if (object.target != 'sameProcess') {
+          runClonableTest();
+          return;
+        }
+
+        obj.send(object.data, []).then(function(received) {
+          compare(received.data, object.data);
+          runClonableTest();
+        });
+      }
+
+      runClonableTest();
+    });
+  })
+
   // transfering tests
   .then(function() {
     if (!obj.transferableObjects) {
       return;
     }
 
     // MessagePort
     return new Promise(function(r, rr) {
@@ -229,17 +308,18 @@ function test_windowToWindow() {
     }
 
     let tmp = resolve;
     resolve = null;
     tmp({ data: e.data, ports: e.ports });
   }
 
   runTests({
-    clonableObjects: true,
+    clonableObjectsEveryWhere: true,
+    clonableObjectsSameProcess: true,
     transferableObjects: true,
     send: function(what, ports) {
       return new Promise(function(r, rr) {
         resolve = r;
 
         try {
           postMessage(what, '*', ports);
         } catch(e) {
@@ -282,17 +362,18 @@ function test_windowToIframeURL(url) {
     resolve = null;
     tmp({ data: e.data, ports: e.ports });
   }
 
   var ifr = document.createElement('iframe');
   ifr.src = url;
   ifr.onload = function() {
     runTests({
-      clonableObjects: true,
+      clonableObjectsEveryWhere: true,
+      clonableObjectsSameProcess: false,
       transferableObjects: true,
       send: function(what, ports) {
         return new Promise(function(r, rr) {
           resolve = r;
           try {
             ifr.contentWindow.postMessage(what, '*', ports);
           } catch(e) {
             resolve = null;
@@ -329,17 +410,18 @@ function test_workers() {
       }
 
       let tmp = resolve;
       resolve = null;
       tmp({ data: e.data, ports: e.ports });
     }
 
     runTests({
-      clonableObjects: true,
+      clonableObjectsEveryWhere: true,
+      clonableObjectsSameProcess: true,
       transferableObjects: true,
       send: function(what, ports) {
         return new Promise(function(r, rr) {
           resolve = r;
           try {
             w.postMessage(what, ports);
           } catch(e) {
             resolve = null;
@@ -372,17 +454,18 @@ function test_broadcastChannel() {
     }
 
     let tmp = resolve;
     resolve = null;
     tmp({ data: e.data, ports: [] });
   }
 
   runTests({
-    clonableObjects: true,
+    clonableObjectsEveryWhere: true,
+    clonableObjectsSameProcess: false,
     transferableObjects: false,
     send: function(what, ports) {
       return new Promise(function(r, rr) {
         if (ports.length) {
           rr();
           return;
         }
 
@@ -417,17 +500,18 @@ function test_broadcastChannel_inWorkers
       }
 
       let tmp = resolve;
       resolve = null;
       tmp({ data: e.data, ports: e.ports });
     }
 
     runTests({
-      clonableObjects: true,
+      clonableObjectsEveryWhere: true,
+      clonableObjectsSameProcess: false,
       transferableObjects: false,
       send: function(what, ports) {
         return new Promise(function(r, rr) {
           if (ports.length) {
             rr();
             return;
           }
 
@@ -458,17 +542,18 @@ function test_messagePort() {
     }
 
     let tmp = resolve;
     resolve = null;
     tmp({ data: e.data, ports: e.ports });
   }
 
   runTests({
-    clonableObjects: true,
+    clonableObjectsEveryWhere: true,
+    clonableObjectsSameProcess: false,
     transferableObjects: true,
     send: function(what, ports) {
       return new Promise(function(r, rr) {
         resolve = r;
         try {
           mc.port1.postMessage(what, ports);
         } catch(e) {
           resolve = null;
@@ -503,17 +588,18 @@ function test_messagePort_inWorkers() {
       }
 
       let tmp = resolve;
       resolve = null;
       tmp({ data: e.data, ports: e.ports });
     }
 
     runTests({
-      clonableObjects: true,
+      clonableObjectsEveryWhere: true,
+      clonableObjectsSameProcess: false,
       transferableObjects: true,
       send: function(what, ports) {
         return new Promise(function(r, rr) {
           resolve = r;
           try {
             mc.port1.postMessage(what, ports);
           } catch(e) {
             resolve = null;
@@ -530,16 +616,17 @@ function test_messagePort_inWorkers() {
   }
 }
 
 var tests = [
   setup_tests,
 
   create_fileList,
   create_directory,
+  create_wasmModule,
 
   test_windowToWindow,
   test_windowToIframe,
   test_windowToCrossOriginIframe,
 
   test_workers,
 
   test_broadcastChannel,
--- a/dom/ipc/StructuredCloneData.cpp
+++ b/dom/ipc/StructuredCloneData.cpp
@@ -41,16 +41,17 @@ StructuredCloneData::Copy(const Structur
   }
 
   PortIdentifiers().AppendElements(aData.PortIdentifiers());
 
   MOZ_ASSERT(BlobImpls().IsEmpty());
   BlobImpls().AppendElements(aData.BlobImpls());
 
   MOZ_ASSERT(GetSurfaces().IsEmpty());
+  MOZ_ASSERT(WasmModules().IsEmpty());
 
   mInitialized = true;
 
   return true;
 }
 
 void
 StructuredCloneData::Read(JSContext* aCx,