Bug 999755 - Add neuter() variants to vary data pointer, r=Waldo
authorSteve Fink <sfink@mozilla.com>
Thu, 24 Apr 2014 14:40:57 -0700
changeset 198812 e9cb7cf27ce7a4d5deeeeff7c9ac9415dce31c70
parent 198811 d4684d3c0340363a12e062985d37ac0ee201fcff
child 198813 c00e3be62b888eb20266b6d5266b41b47bfdb174
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs999755
milestone31.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 999755 - Add neuter() variants to vary data pointer, r=Waldo
js/src/builtin/TestingFunctions.cpp
js/src/jit-test/tests/TypedObject/atopneuteredbuffer.js
js/src/jit-test/tests/TypedObject/bug976697.js
js/src/jit-test/tests/TypedObject/neutertypedobj.js
js/src/jit-test/tests/TypedObject/neutertypedobjsizedarray.js
js/src/jit-test/tests/TypedObject/neutertypedobjunsizedarray.js
js/src/jit-test/tests/asm.js/testNeuter.js
js/src/jit-test/tests/basic/testNeutering.js
js/src/jsapi-tests/testMappedArrayBuffer.cpp
js/src/jsfriendapi.h
js/src/vm/ArrayBufferObject.cpp
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1458,26 +1458,47 @@ Deserialize(JSContext *cx, unsigned argc
     return true;
 }
 
 static bool
 Neuter(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
+    if (args.length() != 2) {
+        JS_ReportError(cx, "wrong number of arguments to neuter()");
+        return false;
+    }
+
     RootedObject obj(cx);
-    if (!JS_ValueToObject(cx, args.get(0), &obj))
+    if (!JS_ValueToObject(cx, args[0], &obj))
         return false;
 
     if (!obj) {
         JS_ReportError(cx, "neuter must be passed an object");
         return false;
     }
 
-    if (!JS_NeuterArrayBuffer(cx, obj))
+    NeuterDataDisposition changeData;
+    RootedString str(cx, JS::ToString(cx, args[1]));
+    if (!str)
+        return false;
+    JSAutoByteString dataDisposition(cx, str);
+    if (!dataDisposition)
+        return false;
+    if (strcmp(dataDisposition.ptr(), "same-data") == 0) {
+        changeData = KeepData;
+    } else if (strcmp(dataDisposition.ptr(), "change-data") == 0) {
+        changeData = ChangeData;
+    } else {
+        JS_ReportError(cx, "unknown parameter 2 to neuter()");
+        return false;
+    }
+
+    if (!JS_NeuterArrayBuffer(cx, obj, changeData))
         return false;
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 WorkerThreadCount(JSContext *cx, unsigned argc, jsval *vp)
@@ -1743,18 +1764,21 @@ static const JSFunctionSpecWithHelp Test
 "  Serialize 'data' using JS_WriteStructuredClone. Returns a structured\n"
 "  clone buffer object."),
 
     JS_FN_HELP("deserialize", Deserialize, 1, 0,
 "deserialize(clonebuffer)",
 "  Deserialize data generated by serialize."),
 
     JS_FN_HELP("neuter", Neuter, 1, 0,
-"neuter(buffer)",
-"  Neuter the given ArrayBuffer object as if it had been transferred to a WebWorker."),
+"neuter(buffer, \"change-data\"|\"same-data\")",
+"  Neuter the given ArrayBuffer object as if it had been transferred to a\n"
+"  WebWorker. \"change-data\" will update the internal data pointer.\n"
+"  \"same-data\" will leave it set to its original value, to mimic eg\n"
+"  asm.js ArrayBuffer neutering."),
 
     JS_FN_HELP("workerThreadCount", WorkerThreadCount, 0, 0,
 "workerThreadCount()",
 "  Returns the number of worker threads available for off-main-thread tasks."),
 
     JS_FN_HELP("startTraceLogger", EnableTraceLogger, 0, 0,
 "startTraceLogger()",
 "  Start logging the mainThread.\n"
--- a/js/src/jit-test/tests/TypedObject/atopneuteredbuffer.js
+++ b/js/src/jit-test/tests/TypedObject/atopneuteredbuffer.js
@@ -3,21 +3,22 @@
 
 if (typeof TypedObject === "undefined")
   quit();
 
 load(libdir + "asserts.js")
 
 var {StructType, uint32, Object, Any, storage, objectType} = TypedObject;
 
-function main() { // once a C programmer, always a C programmer.
+function main(variant) { // once a C programmer, always a C programmer.
   var Uints = uint32.array();
   var Unit = new StructType({});   // Empty struct type
   var buffer = new ArrayBuffer(0); // Empty buffer
   var p = new Unit(buffer);        // OK
-  neuter(buffer);
+  neuter(buffer, variant);
   assertThrowsInstanceOf(() => new Unit(buffer), TypeError,
                          "Able to instantiate atop neutered buffer");
   assertThrowsInstanceOf(() => new Uints(buffer, 0), TypeError,
                          "Able to instantiate atop neutered buffer");
 }
 
-main();
+main("same-data");
+main("change-data");
--- a/js/src/jit-test/tests/TypedObject/bug976697.js
+++ b/js/src/jit-test/tests/TypedObject/bug976697.js
@@ -1,13 +1,15 @@
 // Test that instantiating a typed array on top of a neutered buffer
 // doesn't trip any asserts.
 //
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/licenses/publicdomain/
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
+x = ArrayBuffer();
+neuter(x, "same-data");
+Uint32Array(x);
+gc();
 
 x = ArrayBuffer();
-neuter(x);
+neuter(x, "change-data");
 Uint32Array(x);
 gc();
--- a/js/src/jit-test/tests/TypedObject/neutertypedobj.js
+++ b/js/src/jit-test/tests/TypedObject/neutertypedobj.js
@@ -3,30 +3,31 @@ if (!this.hasOwnProperty("TypedObject"))
 
 var {StructType, uint32, storage} = TypedObject;
 var S = new StructType({f: uint32, g: uint32});
 
 function readFromS(s) {
   return s.f + s.g;
 }
 
-function main() {
+function main(variant) {
   var s = new S({f: 22, g: 44});
 
   for (var i = 0; i < 10; i++)
     assertEq(readFromS(s), 66);
 
-  neuter(storage(s).buffer);
+  neuter(storage(s).buffer, variant);
 
   for (var i = 0; i < 10; i++) {
     var ok = false;
 
     try {
       readFromS(s);
     } catch (e) {
       ok = e instanceof TypeError;
     }
 
     assertEq(ok, true);
   }
 }
 
-main();
+main("same-data");
+main("change-data");
--- a/js/src/jit-test/tests/TypedObject/neutertypedobjsizedarray.js
+++ b/js/src/jit-test/tests/TypedObject/neutertypedobjsizedarray.js
@@ -9,32 +9,33 @@ if (!this.hasOwnProperty("TypedObject"))
 var {StructType, uint32, storage} = TypedObject;
 var S = new StructType({f: uint32, g: uint32});
 var A = S.array(10);
 
 function readFrom(a) {
   return a[2].f + a[2].g;
 }
 
-function main() {
+function main(variant) {
   var a = new A();
   a[2].f = 22;
   a[2].g = 44;
 
   for (var i = 0; i < 10; i++)
     assertEq(readFrom(a), 66);
 
-  neuter(storage(a).buffer);
+  neuter(storage(a).buffer, variant);
 
   for (var i = 0; i < 10; i++) {
     var ok = false;
 
     try {
       readFrom(a);
     } catch (e) {
       ok = e instanceof TypeError;
     }
 
     assertEq(ok, true);
   }
 }
 
-main();
+main("same-data");
+main("change-data");
--- a/js/src/jit-test/tests/TypedObject/neutertypedobjunsizedarray.js
+++ b/js/src/jit-test/tests/TypedObject/neutertypedobjunsizedarray.js
@@ -7,32 +7,33 @@ if (!this.hasOwnProperty("TypedObject"))
 var {StructType, uint32, storage} = TypedObject;
 var S = new StructType({f: uint32, g: uint32});
 var A = S.array();
 
 function readFrom(a) {
   return a[2].f + a[2].g;
 }
 
-function main() {
+function main(variant) {
   var a = new A(10);
   a[2].f = 22;
   a[2].g = 44;
 
   for (var i = 0; i < 10; i++)
     assertEq(readFrom(a), 66);
 
-  neuter(storage(a).buffer);
+  neuter(storage(a).buffer, variant);
 
   for (var i = 0; i < 10; i++) {
     var ok = false;
 
     try {
       readFrom(a);
     } catch (e) {
       ok = e instanceof TypeError;
     }
 
     assertEq(ok, true);
   }
 }
 
-main();
+main("same-data");
+main("change-data");
--- a/js/src/jit-test/tests/asm.js/testNeuter.js
+++ b/js/src/jit-test/tests/asm.js/testNeuter.js
@@ -21,17 +21,17 @@ var i32 = new Int32Array(1024);
 var buffer = i32.buffer;
 var {get, set} = f(this, null, buffer);
 if (isAsmJSCompilationAvailable())
     assertEq(isAsmJSFunction(get) && isAsmJSFunction(set), true);
 
 set(4, 42);
 assertEq(get(4), 42);
 
-neuter(buffer);
+neuter(buffer, "same-data");
 
 // These operations may throw internal errors
 try {
     assertEq(get(4), 0);
     set(0, 42);
     assertEq(get(0), 0);
 } catch (e) {
     assertEq(String(e).indexOf("InternalError"), 0);
@@ -51,17 +51,17 @@ function f2(stdlib, foreign, buffer) {
 if (isAsmJSCompilationAvailable())
     assertEq(isAsmJSModule(f2), true);
 
 var i32 = new Int32Array(1024);
 var buffer = i32.buffer;
 var threw = false;
 function ffi() {
     try {
-        neuter(buffer);
+        neuter(buffer, "same-data");
     } catch (e) {
         assertEq(String(e).indexOf("InternalError"), 0);
         threw = true;
     }
 }
 var inner = f2(this, {ffi:ffi}, buffer);
 if (isAsmJSCompilationAvailable())
     assertEq(isAsmJSFunction(inner), true);
--- a/js/src/jit-test/tests/basic/testNeutering.js
+++ b/js/src/jit-test/tests/basic/testNeutering.js
@@ -1,23 +1,25 @@
-var ab = new ArrayBuffer(4);
-var i32 = new Int32Array(ab);
-i32[0] = 42;
-neuter(ab);
-assertEq(i32.length, 0);
-assertEq(ab.byteLength, 0);
-assertEq(i32[0], undefined);
+for (var variant of ["same-data", "change-data"]) {
+  var ab = new ArrayBuffer(4);
+  var i32 = new Int32Array(ab);
+  i32[0] = 42;
+  neuter(ab, variant);
+  assertEq(i32.length, 0);
+  assertEq(ab.byteLength, 0);
+  assertEq(i32[0], undefined);
 
-var ab = new ArrayBuffer(12);
-var i32 = new Int32Array(ab);
-i32[0] = 42;
-neuter(ab);
-assertEq(i32.length, 0);
-assertEq(ab.byteLength, 0);
-assertEq(i32[0], undefined);
+  var ab = new ArrayBuffer(12);
+  var i32 = new Int32Array(ab);
+  i32[0] = 42;
+  neuter(ab, variant);
+  assertEq(i32.length, 0);
+  assertEq(ab.byteLength, 0);
+  assertEq(i32[0], undefined);
 
-var ab = new ArrayBuffer(4096);
-var i32 = new Int32Array(ab);
-i32[0] = 42;
-neuter(ab);
-assertEq(i32.length, 0);
-assertEq(ab.byteLength, 0);
-assertEq(i32[0], undefined);
+  var ab = new ArrayBuffer(4096);
+  var i32 = new Int32Array(ab);
+  i32[0] = 42;
+  neuter(ab, variant);
+  assertEq(i32.length, 0);
+  assertEq(ab.byteLength, 0);
+  assertEq(i32[0], undefined);
+}
--- a/js/src/jsapi-tests/testMappedArrayBuffer.cpp
+++ b/js/src/jsapi-tests/testMappedArrayBuffer.cpp
@@ -104,17 +104,17 @@ bool TestReleaseContents()
 
     return true;
 }
 
 bool TestNeuterObject()
 {
     JS::RootedObject obj(cx, CreateNewObject(8, 12));
     CHECK(obj);
-    JS_NeuterArrayBuffer(cx, obj);
+    JS_NeuterArrayBuffer(cx, obj, ChangeData);
     CHECK(isNeutered(obj));
 
     return true;
 }
 
 bool TestCloneObject()
 {
     JS::RootedObject obj1(cx, CreateNewObject(8, 12));
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1441,21 +1441,27 @@ JS_GetArrayBufferViewData(JSObject *obj)
 /*
  * Return the ArrayBuffer underlying an ArrayBufferView. If the buffer has been
  * neutered, this will still return the neutered buffer. |obj| must be an
  * object that would return true for JS_IsArrayBufferViewObject().
  */
 extern JS_FRIEND_API(JSObject *)
 JS_GetArrayBufferViewBuffer(JSContext *cx, JSObject *obj);
 
+typedef enum {
+    ChangeData,
+    KeepData
+} NeuterDataDisposition;
+
 /*
  * Set an ArrayBuffer's length to 0 and neuter all of its views.
  */
 extern JS_FRIEND_API(bool)
-JS_NeuterArrayBuffer(JSContext *cx, JS::HandleObject obj);
+JS_NeuterArrayBuffer(JSContext *cx, JS::HandleObject obj,
+                     NeuterDataDisposition changeData);
 
 /*
  * Check whether obj supports JS_GetDataView* APIs.
  */
 JS_FRIEND_API(bool)
 JS_IsDataViewObject(JSObject *obj);
 
 /*
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -1019,31 +1019,41 @@ JS_GetStableArrayBufferData(JSContext *c
     Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(obj));
     if (!ArrayBufferObject::ensureNonInline(cx, buffer))
         return nullptr;
 
     return buffer->dataPointer();
 }
 
 JS_FRIEND_API(bool)
-JS_NeuterArrayBuffer(JSContext *cx, HandleObject obj)
+JS_NeuterArrayBuffer(JSContext *cx, HandleObject obj,
+                     NeuterDataDisposition changeData)
 {
     if (!obj->is<ArrayBufferObject>()) {
         JS_ReportError(cx, "ArrayBuffer object required");
         return false;
     }
 
     Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
 
     if (!buffer->canNeuter(cx)) {
         js_ReportOverRecursed(cx);
         return false;
     }
 
-    ArrayBufferObject::neuter(cx, buffer, buffer->dataPointer());
+    void *newData;
+    if (changeData == ChangeData) {
+        newData = AllocateArrayBufferContents(cx, buffer->byteLength());
+        if (!newData)
+            return false;
+    } else {
+        newData = buffer->dataPointer();
+    }
+
+    ArrayBufferObject::neuter(cx, buffer, newData);
     return true;
 }
 
 JS_FRIEND_API(JSObject *)
 JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes)
 {
     JS_ASSERT(nbytes <= INT32_MAX);
     return ArrayBufferObject::create(cx, nbytes);