Bug 999755 - Add neuter() variants to vary data pointer. Also fold in the followup patch in bug 1002864, to ignore disposition when buffer contents can't be changed. r=Waldo, r=sfink, a=bajaj
authorSteve Fink <sfink@mozilla.com>
Thu, 24 Apr 2014 14:40:57 -0700
changeset 157145 851f13605757
parent 157144 96a1ab166184
child 157146 4c359ff76654
push id516
push userjwalden@mit.edu
push dateFri, 30 May 2014 03:00:08 +0000
reviewersWaldo, sfink, bajaj
bugs999755, 1002864
milestone26.0
Bug 999755 - Add neuter() variants to vary data pointer. Also fold in the followup patch in bug 1002864, to ignore disposition when buffer contents can't be changed. r=Waldo, r=sfink, a=bajaj
js/src/builtin/TestingFunctions.cpp
js/src/jsfriendapi.h
js/src/vm/TypedArrayObject.cpp
js/src/vm/TypedArrayObject.h
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1057,16 +1057,58 @@ SetJitCompilerOption(JSContext *cx, unsi
         number = -1;
 
     JS_SetGlobalJitCompilerOption(cx, opt, uint32_t(number));
 
     args.rval().setBoolean(true);
     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[0], obj.address()))
+        return false;
+
+    if (!obj) {
+        JS_ReportError(cx, "neuter must be passed an object");
+        return false;
+    }
+
+    NeuterDataDisposition changeData;
+    RootedString str(cx, ToString<CanGC>(cx, args.get(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 const JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment')",
 "  Run the garbage collector. When obj is given, GC only its compartment.\n"
 "  If 'compartment' is given, GC any compartments that were scheduled for\n"
 "  GC via schedulegc."),
 
     JS_FN_HELP("minorgc", ::MinorGC, 0, 0,
@@ -1248,16 +1290,23 @@ static const JSFunctionSpecWithHelp Test
     JS_FN_HELP("bailout", testingFunc_bailout, 0, 0,
 "bailout()",
 "  Force a bailout out of ionmonkey (if running in ionmonkey)."),
 
     JS_FN_HELP("setJitCompilerOption", SetJitCompilerOption, 2, 0,
 "setCompilerOption(<option>, <number>)",
 "  Set a compiler option indexed in JSCompileOption enum to a number.\n"),
 
+    JS_FN_HELP("neuter", Neuter, 1, 0,
+"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_FS_HELP_END
 };
 
 bool
 js::DefineTestingFunctions(JSContext *cx, HandleObject obj)
 {
     return JS_DefineFunctionsWithHelp(cx, obj, TestingFunctions);
 }
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1225,16 +1225,38 @@ 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(JSObject *obj);
 
+typedef enum {
+    ChangeData,
+    KeepData
+} NeuterDataDisposition;
+
+namespace js {
+
+/*
+ * Set an ArrayBuffer's length to 0 and neuter all of its views.
+ *
+ * The |changeData| argument is a hint to inform internal behavior with respect
+ * to the internal pointer to the ArrayBuffer's data after being neutered.
+ * There is no guarantee it will be respected.  But if it is respected, the
+ * ArrayBuffer's internal data pointer will, or will not, have changed
+ * accordingly.
+ */
+extern JS_FRIEND_API(bool)
+NeuterArrayBuffer(JSContext *cx, JS::HandleObject obj,
+                  NeuterDataDisposition changeData);
+
+} /* namespace js */
+
 /*
  * Check whether obj supports JS_GetDataView* APIs.
  */
 JS_FRIEND_API(bool)
 JS_IsDataViewObject(JSObject *obj);
 
 /*
  * Return the byte offset of a data view into its array buffer. |obj| must be a
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -589,18 +589,18 @@ ArrayBufferObject::createDataViewForThis
 bool
 ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsArrayBuffer, createDataViewForThisImpl>(cx, args);
 }
 
 bool
-ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, void **contents,
-                                 uint8_t **data)
+ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, NeuterDataDisposition changeData,
+                                 void **contents, uint8_t **data)
 {
     MOZ_ASSERT(cx);
 
     ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
     ArrayBufferViewObject *views = GetViewList(&buffer);
 
     // remove buffer from the list of buffers with > 1 view, if it is there yet
     // (the list is only built during tracing)
@@ -627,17 +627,17 @@ ArrayBufferObject::stealContents(JSConte
 
     uint32_t byteLen = buffer.byteLength();
 
     js::ObjectElements *oldHeader = buffer.getElementsHeader();
     js::ObjectElements *newHeader;
 
     // If the ArrayBuffer's elements are transferrable, transfer ownership
     // directly.  Otherwise we have to copy the data into new elements.
-    bool stolen = buffer.hasStealableContents();
+    bool stolen = buffer.hasStealableContents() && changeData == ChangeData;
     if (stolen) {
         newHeader = AllocateArrayBufferContents(cx, byteLen, NULL);
         if (!newHeader)
             return false;
 
         SetViewList(&buffer, NULL);
         *contents = oldHeader;
         *data = buffer.dataPointer();
@@ -4010,16 +4010,34 @@ JS_GetArrayBufferData(JSObject *obj)
     if (!obj)
         return NULL;
     ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
     if (!buffer.uninlineData(NULL))
         return NULL;
     return buffer.dataPointer();
 }
 
+JS_FRIEND_API(bool)
+js::NeuterArrayBuffer(JSContext *cx, HandleObject obj,
+                      NeuterDataDisposition changeData)
+{
+    if (!obj->is<ArrayBufferObject>()) {
+        JS_ReportError(cx, "ArrayBuffer object required");
+        return false;
+    }
+
+    void *contents;
+    uint8_t *data;
+    if (!ArrayBufferObject::stealContents(cx, obj, changeData, &contents, &data))
+        return false;
+
+    JS_free(cx, contents);
+    return true;
+}
+
 JS_FRIEND_API(JSObject *)
 JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes)
 {
     JS_ASSERT(nbytes <= INT32_MAX);
     return ArrayBufferObject::create(cx, nbytes);
 }
 
 JS_PUBLIC_API(JSObject *)
@@ -4069,17 +4087,17 @@ JS_StealArrayBufferContents(JSContext *c
     if (!(obj = CheckedUnwrap(obj)))
         return false;
 
     if (!obj->is<ArrayBufferObject>()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
         return false;
     }
 
-    if (!ArrayBufferObject::stealContents(cx, obj, contents, data))
+    if (!ArrayBufferObject::stealContents(cx, obj, ChangeData, contents, data))
         return false;
 
     return true;
 }
 
 JS_FRIEND_API(uint32_t)
 JS_GetTypedArrayLength(JSObject *obj)
 {
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -156,18 +156,18 @@ class ArrayBufferObject : public JSObjec
         // Neutered contents aren't transferrable because we want a neutered
         // array's contents to be backed by zeroed memory equal in length to
         // the original buffer contents.  Transferring these contents would
         // allocate new ones based on the current byteLength, which is 0 for a
         // neutered array -- not the original byteLength.
         return !isNeutered();
     }
 
-    static bool stealContents(JSContext *cx, JSObject *obj, void **contents,
-                              uint8_t **data);
+    static bool stealContents(JSContext *cx, JSObject *obj, NeuterDataDisposition changeData,
+                              void **contents, uint8_t **data);
 
     static void setElementsHeader(js::ObjectElements *header, uint32_t bytes) {
         header->flags = 0;
         header->initializedLength = bytes;
 
         // NB: one or both of these fields is clobbered by GetViewList to store the
         // 'views' link. Set them to 0 to effectively initialize 'views' to NULL.
         header->length = 0;