Bug 1529294 - Add a test for ArrayBuffers with external contents not being serializable. r=sfink
authorJeff Walden <jwalden@mit.edu>
Mon, 18 Feb 2019 22:45:17 -0800
changeset 460378 9cb1a38c00ed72c33b02781d4ece643fb80ba5fd
parent 460377 eed1098d0d6c9e3af5b02154295e452c6c21bb48
child 460379 efd2d5e7ec57c3f347b74ace7ea4605b84bfeac5
push id112085
push userjwalden@mit.edu
push dateFri, 22 Feb 2019 04:41:17 +0000
treeherdermozilla-inbound@5b7a07b449ba [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1529294
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1529294 - Add a test for ArrayBuffers with external contents not being serializable. r=sfink
js/src/builtin/TestingFunctions.cpp
js/src/builtin/TestingFunctions.h
js/src/jsapi-tests/testArrayBuffer.cpp
js/src/vm/StructuredClone.cpp
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -3038,17 +3038,17 @@ static mozilla::Maybe<JS::StructuredClon
     scope.emplace(JS::StructuredCloneScope::DifferentProcess);
   } else if (StringEqualsAscii(scopeStr, "DifferentProcessForIndexedDB")) {
     scope.emplace(JS::StructuredCloneScope::DifferentProcessForIndexedDB);
   }
 
   return scope;
 }
 
-static bool Serialize(JSContext* cx, unsigned argc, Value* vp) {
+bool js::testingFunc_serialize(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   mozilla::Maybe<JSAutoStructuredCloneBuffer> clonebuf;
   JS::CloneDataPolicy policy;
 
   if (!args.get(2).isUndefined()) {
     RootedObject opts(cx, ToObject(cx, args.get(2)));
     if (!opts) {
@@ -6089,17 +6089,17 @@ gc::ZealModeHelpText),
 "  Iterates the Jit stack and check that stack invariants hold."),
 
     JS_FN_HELP("setIonCheckGraphCoherency", SetIonCheckGraphCoherency, 1, 0,
 "setIonCheckGraphCoherency(bool)",
 "  Set whether Ion should perform graph consistency (DEBUG-only) assertions. These assertions\n"
 "  are valuable and should be generally enabled, however they can be very expensive for large\n"
 "  (wasm) programs."),
 
-    JS_FN_HELP("serialize", Serialize, 1, 0,
+    JS_FN_HELP("serialize", testingFunc_serialize, 1, 0,
 "serialize(data, [transferables, [policy]])",
 "  Serialize 'data' using JS_WriteStructuredClone. Returns a structured\n"
 "  clone buffer object. 'policy' may be an options hash. Valid keys:\n"
 "    'SharedArrayBuffer' - either 'allow' (the default) or 'deny'\n"
 "      to specify whether SharedArrayBuffers may be serialized.\n"
 "    'scope' - SameProcessSameThread, SameProcessDifferentThread,\n"
 "      DifferentProcess, or DifferentProcessForIndexedDB. Determines how some\n"
 "      values will be serialized. Clone buffers may only be deserialized with a\n"
--- a/js/src/builtin/TestingFunctions.h
+++ b/js/src/builtin/TestingFunctions.h
@@ -17,14 +17,18 @@ MOZ_MUST_USE bool DefineTestingFunctions
 
 MOZ_MUST_USE bool testingFunc_assertFloat32(JSContext* cx, unsigned argc,
                                             Value* vp);
 
 MOZ_MUST_USE bool testingFunc_assertRecoveredOnBailout(JSContext* cx,
                                                        unsigned argc,
                                                        Value* vp);
 
+MOZ_MUST_USE bool testingFunc_serialize(JSContext* cx,
+                                        unsigned argc,
+                                        Value* vp);
+
 extern JSScript* TestingFunctionArgumentToScript(JSContext* cx, HandleValue v,
                                                  JSFunction** funp = nullptr);
 
 } /* namespace js */
 
 #endif /* builtin_TestingFunctions_h */
--- a/js/src/jsapi-tests/testArrayBuffer.cpp
+++ b/js/src/jsapi-tests/testArrayBuffer.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  */
 
 #include "jsfriendapi.h"
+
+#include "builtin/TestingFunctions.h"
 #include "js/MemoryFunctions.h"
-
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testArrayBuffer_bug720949_steal) {
   static const unsigned NUM_TEST_BUFFERS = 2;
   static const unsigned MAGIC_VALUE_1 = 3;
   static const unsigned MAGIC_VALUE_2 = 17;
 
   JS::RootedObject buf_len1(cx), buf_len200(cx);
@@ -301,8 +302,66 @@ BEGIN_TEST(testArrayBuffer_stealDetachEx
   buffer = nullptr;
   JS_GC(cx);
   JS_GC(cx);
   CHECK(data.wasFreed());
 
   return true;
 }
 END_TEST(testArrayBuffer_stealDetachExternal)
+
+BEGIN_TEST(testArrayBuffer_serializeExternal) {
+  JS::RootedValue serializeValue(cx);
+
+  {
+    JS::RootedFunction serialize(cx);
+    serialize = JS_NewFunction(cx, js::testingFunc_serialize, 1, 0, "serialize");
+    CHECK(serialize);
+
+    serializeValue.setObject(*JS_GetFunctionObject(serialize));
+  }
+
+  ExternalData data("One two three four");
+  JS::RootedObject externalBuffer(
+      cx, JS_NewExternalArrayBuffer(cx, data.len(), data.contents(),
+                                    &ExternalData::freeCallback, &data));
+  CHECK(externalBuffer);
+  CHECK(!data.wasFreed());
+
+  JS::RootedValue v(cx, JS::ObjectValue(*externalBuffer));
+  JS::RootedObject transferMap(cx, JS_NewArrayObject(cx, JS::HandleValueArray(v)));
+  CHECK(transferMap);
+
+  JS::AutoValueArray<2> args(cx);
+  args[0].setObject(*externalBuffer);
+  args[1].setObject(*transferMap);
+
+  // serialize(externalBuffer, [externalBuffer]) should throw for an unhandled
+  // BufferContents kind.
+  CHECK(!JS::Call(cx, JS::UndefinedHandleValue, serializeValue,
+                  JS::HandleValueArray(args), &v));
+
+  JS::RootedValue exn(cx);
+  CHECK(JS_GetPendingException(cx, &exn));
+  JS_ClearPendingException(cx);
+
+  js::ErrorReport report(cx);
+  CHECK(report.init(cx, exn, js::ErrorReport::NoSideEffects));
+
+  CHECK_EQUAL(report.report()->errorNumber,
+              static_cast<unsigned int>(JSMSG_SC_NOT_TRANSFERABLE));
+
+  // Data should have been left alone.
+  CHECK(!data.wasFreed());
+
+  v.setNull();
+  transferMap = nullptr;
+  args[0].setNull();
+  args[1].setNull();
+  externalBuffer = nullptr;
+
+  JS_GC(cx);
+  JS_GC(cx);
+  CHECK(data.wasFreed());
+
+  return true;
+}
+END_TEST(testArrayBuffer_serializeExternal)
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -1882,16 +1882,18 @@ bool JSStructuredCloneWriter::transferOw
         if (!bufContents) {
           return false;  // out of memory
         }
 
         content = bufContents.data();
         if (bufContents.kind() == ArrayBufferObject::MAPPED) {
           ownership = JS::SCTAG_TMO_MAPPED_DATA;
         } else {
+          MOZ_ASSERT(bufContents.kind() == ArrayBufferObject::PLAIN_DATA,
+                     "failing to handle new ArrayBuffer kind?");
           ownership = JS::SCTAG_TMO_ALLOC_DATA;
         }
         extraData = nbytes;
       }
     } else {
       if (!out.buf.callbacks_ || !out.buf.callbacks_->writeTransfer) {
         return reportDataCloneError(JS_SCERR_TRANSFERABLE);
       }