Bug 1240984 - Remove dummy ArrayBufferContents backstop, r=Waldo

MozReview-Commit-ID: h4oF04rDZn

--HG--
extra : rebase_source : f1186c2c37c222e747b951207b0ed02a31a2639d
This commit is contained in:
Steve Fink 2016-03-09 14:39:35 -08:00
parent dc9bddc5cd
commit 8dabe9a4da
5 changed files with 75 additions and 85 deletions

View File

@ -23,10 +23,7 @@ var buffer = new ArrayBuffer(BUF_MIN);
var {get, set} = asmLink(m, this, null, buffer); var {get, set} = asmLink(m, this, null, buffer);
set(4, 42); set(4, 42);
assertEq(get(4), 42); assertEq(get(4), 42);
assertThrowsInstanceOf(() => detachArrayBuffer(buffer, "change-data"), assertThrowsInstanceOf(() => detachArrayBuffer(buffer), Error);
InternalError);
assertThrowsInstanceOf(() => detachArrayBuffer(buffer, "same-data"),
InternalError);
var m = asmCompile('stdlib', 'foreign', 'buffer', var m = asmCompile('stdlib', 'foreign', 'buffer',
`"use asm"; `"use asm";
@ -42,8 +39,13 @@ var m = asmCompile('stdlib', 'foreign', 'buffer',
var buffer = new ArrayBuffer(BUF_MIN); var buffer = new ArrayBuffer(BUF_MIN);
function ffi1() function ffi1()
{ {
assertThrowsInstanceOf(() => detachArrayBuffer(buffer, "change-data"), assertThrowsInstanceOf(() => detachArrayBuffer(buffer), Error);
InternalError);
} }
var inner = asmLink(m, this, {ffi: ffi1}, buffer); var inner = asmLink(m, this, {ffi: ffi1}, buffer);
function ffi2()
{
assertThrowsInstanceOf(() => serialize([], [buffer]), Error);
}
var inner2 = asmLink(m, this, {ffi: ffi2}, buffer);

View File

@ -2107,12 +2107,7 @@ enum DetachDataDisposition {
* Detach an ArrayBuffer, causing all associated views to no longer refer to * Detach an ArrayBuffer, causing all associated views to no longer refer to
* the ArrayBuffer's original attached memory. * the ArrayBuffer's original attached memory.
* *
* The |changeData| argument is a hint to inform internal behavior with respect * The |changeData| argument is obsolete and ignored.
* to the ArrayBuffer's internal pointer to associated data. |ChangeData|
* attempts to set the internal pointer to fresh memory of the same size as the
* original memory; |KeepData| attempts to preserve the original pointer, even
* while the ArrayBuffer appears observably detached. There is no guarantee
* this parameter is respected -- it's only a hint.
*/ */
extern JS_FRIEND_API(bool) extern JS_FRIEND_API(bool)
JS_DetachArrayBuffer(JSContext* cx, JS::HandleObject obj, JS_DetachArrayBuffer(JSContext* cx, JS::HandleObject obj,

View File

@ -260,14 +260,12 @@ NoteViewBufferWasDetached(ArrayBufferViewObject* view,
MarkObjectStateChange(cx, view); MarkObjectStateChange(cx, view);
} }
/* static */ bool /* static */ void
ArrayBufferObject::detach(JSContext* cx, Handle<ArrayBufferObject*> buffer, ArrayBufferObject::detach(JSContext* cx, Handle<ArrayBufferObject*> buffer,
BufferContents newContents) BufferContents newContents)
{ {
if (buffer->isWasm()) { MOZ_ASSERT(!buffer->isWasm(), "WASM/asm.js ArrayBuffers cannot be detached");
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY); MOZ_ASSERT(buffer->hasDetachableContents(), "detaching the non-detachable");
return false;
}
// When detaching buffers where we don't know all views, the new data must // When detaching buffers where we don't know all views, the new data must
// match the old data. All missing views are typed objects, which do not // match the old data. All missing views are typed objects, which do not
@ -314,7 +312,6 @@ ArrayBufferObject::detach(JSContext* cx, Handle<ArrayBufferObject*> buffer,
buffer->setByteLength(0); buffer->setByteLength(0);
buffer->setIsDetached(); buffer->setIsDetached();
return true;
} }
void void
@ -729,35 +726,39 @@ ArrayBufferObject::createDataViewForThis(JSContext* cx, unsigned argc, Value* vp
/* static */ ArrayBufferObject::BufferContents /* static */ ArrayBufferObject::BufferContents
ArrayBufferObject::stealContents(JSContext* cx, Handle<ArrayBufferObject*> buffer, ArrayBufferObject::stealContents(JSContext* cx, Handle<ArrayBufferObject*> buffer,
bool hasStealableContents) bool forceCopy)
{ {
MOZ_ASSERT_IF(hasStealableContents, buffer->hasStealableContents()); if (buffer->isDetached()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
return BufferContents::createPlain(nullptr);
}
if (!buffer->hasDetachableContents()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
return BufferContents::createPlain(nullptr);
}
BufferContents oldContents(buffer->dataPointer(), buffer->bufferKind()); BufferContents oldContents(buffer->dataPointer(), buffer->bufferKind());
BufferContents newContents = AllocateArrayBufferContents(cx, buffer->byteLength());
if (!newContents)
return BufferContents::createPlain(nullptr);
if (hasStealableContents) { if (!forceCopy && buffer->ownsData()) {
// Return the old contents and give the detached buffer a pointer to // Return the old contents and reset the detached buffer's data
// freshly allocated memory that we will never write to and should // pointer. This pointer should never be accessed.
// never get committed. auto newContents = BufferContents::createPlain(nullptr);
buffer->setOwnsData(DoesntOwnData); buffer->setOwnsData(DoesntOwnData); // Do not free the stolen data.
if (!ArrayBufferObject::detach(cx, buffer, newContents)) { ArrayBufferObject::detach(cx, buffer, newContents);
js_free(newContents.data()); buffer->setOwnsData(DoesntOwnData); // Do not free the nullptr.
return BufferContents::createPlain(nullptr);
}
return oldContents; return oldContents;
} }
// Create a new chunk of memory to return since we cannot steal the // Create a new chunk of memory to return since we cannot remove the
// existing contents away from the buffer. // existing contents pointer from the buffer.
memcpy(newContents.data(), oldContents.data(), buffer->byteLength()); BufferContents contentsCopy = AllocateArrayBufferContents(cx, buffer->byteLength());
if (!ArrayBufferObject::detach(cx, buffer, oldContents)) { if (!contentsCopy)
js_free(newContents.data());
return BufferContents::createPlain(nullptr); return BufferContents::createPlain(nullptr);
}
return newContents; if (buffer->byteLength() > 0)
memcpy(contentsCopy.data(), oldContents.data(), buffer->byteLength());
ArrayBufferObject::detach(cx, buffer, oldContents);
return contentsCopy;
} }
/* static */ void /* static */ void
@ -1032,10 +1033,11 @@ ArrayBufferViewObject::trace(JSTracer* trc, JSObject* objArg)
if (IsArrayBuffer(&bufSlot.toObject())) { if (IsArrayBuffer(&bufSlot.toObject())) {
ArrayBufferObject& buf = AsArrayBuffer(MaybeForwarded(&bufSlot.toObject())); ArrayBufferObject& buf = AsArrayBuffer(MaybeForwarded(&bufSlot.toObject()));
uint32_t offset = uint32_t(obj->getFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT).toInt32()); uint32_t offset = uint32_t(obj->getFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT).toInt32());
MOZ_ASSERT(buf.dataPointer() != nullptr);
MOZ_ASSERT(offset <= INT32_MAX); MOZ_ASSERT(offset <= INT32_MAX);
if (buf.forInlineTypedObject()) { if (buf.forInlineTypedObject()) {
MOZ_ASSERT(buf.dataPointer() != nullptr);
// The data is inline with an InlineTypedObject associated with the // The data is inline with an InlineTypedObject associated with the
// buffer. Get a new address for the typed object if it moved. // buffer. Get a new address for the typed object if it moved.
JSObject* view = buf.firstView(); JSObject* view = buf.firstView();
@ -1055,6 +1057,8 @@ ArrayBufferViewObject::trace(JSTracer* trc, JSObject* objArg)
trc->runtime()->gc.nursery.maybeSetForwardingPointer(trc, srcData, dstData, trc->runtime()->gc.nursery.maybeSetForwardingPointer(trc, srcData, dstData,
/* direct = */ false); /* direct = */ false);
} else { } else {
MOZ_ASSERT_IF(buf.dataPointer() == nullptr, offset == 0);
// The data may or may not be inline with the buffer. The buffer // The data may or may not be inline with the buffer. The buffer
// can only move during a compacting GC, in which case its // can only move during a compacting GC, in which case its
// objectMoved hook has already updated the buffer's data pointer. // objectMoved hook has already updated the buffer's data pointer.
@ -1081,7 +1085,6 @@ JSObject::is<js::ArrayBufferObjectMaybeShared>() const
void void
ArrayBufferViewObject::notifyBufferDetached(void* newData) ArrayBufferViewObject::notifyBufferDetached(void* newData)
{ {
MOZ_ASSERT(newData != nullptr);
if (is<DataViewObject>()) { if (is<DataViewObject>()) {
as<DataViewObject>().notifyBufferDetached(newData); as<DataViewObject>().notifyBufferDetached(newData);
} else if (is<TypedArrayObject>()) { } else if (is<TypedArrayObject>()) {
@ -1190,20 +1193,21 @@ JS_DetachArrayBuffer(JSContext* cx, HandleObject obj,
Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>()); Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
if (changeData == ChangeData && buffer->hasStealableContents()) { if (buffer->isDetached())
ArrayBufferObject::BufferContents newContents = return true;
AllocateArrayBufferContents(cx, buffer->byteLength());
if (!newContents) if (buffer->isWasm()) {
return false; JS_ReportError(cx, "Cannot detach WASM ArrayBuffer");
if (!ArrayBufferObject::detach(cx, buffer, newContents)) { return false;
js_free(newContents.data());
return false;
}
} else {
if (!ArrayBufferObject::detach(cx, buffer, buffer->contents()))
return false;
} }
if (!buffer->hasDetachableContents()) {
JS_ReportError(cx, "Cannot detach ArrayBuffer");
return false;
}
ArrayBufferObject::detach(cx, buffer, buffer->contents());
return true; return true;
} }
@ -1286,18 +1290,14 @@ JS_StealArrayBufferContents(JSContext* cx, HandleObject objArg)
} }
Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>()); Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
if (buffer->isDetached()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
return nullptr;
}
// The caller assumes that a plain malloc'd buffer is returned. // The caller assumes that a plain malloc'd buffer is returned. ownsData is
// hasStealableContents is true for mapped buffers, so we must additionally // true for (owned) mapped buffers, so we must additionally require that
// require that the buffer is plain. In the future, we could consider // the buffer is plain. In the future, we could consider returning
// returning something that handles releasing the memory. // something that handles releasing the memory.
bool hasStealableContents = buffer->hasStealableContents() && buffer->hasMallocedContents(); bool forceCopy = !buffer->ownsData() || !buffer->hasMallocedContents();
return ArrayBufferObject::stealContents(cx, buffer, hasStealableContents).data(); return ArrayBufferObject::stealContents(cx, buffer, forceCopy).data();
} }
JS_PUBLIC_API(JSObject*) JS_PUBLIC_API(JSObject*)

View File

@ -213,9 +213,6 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
static bool fun_slice(JSContext* cx, unsigned argc, Value* vp); static bool fun_slice(JSContext* cx, unsigned argc, Value* vp);
static bool fun_isView(JSContext* cx, unsigned argc, Value* vp); static bool fun_isView(JSContext* cx, unsigned argc, Value* vp);
#ifdef NIGHTLY_BUILD
static bool fun_transfer(JSContext* cx, unsigned argc, Value* vp);
#endif
static bool fun_species(JSContext* cx, unsigned argc, Value* vp); static bool fun_species(JSContext* cx, unsigned argc, Value* vp);
@ -246,21 +243,19 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
static void trace(JSTracer* trc, JSObject* obj); static void trace(JSTracer* trc, JSObject* obj);
static void objectMoved(JSObject* obj, const JSObject* old); static void objectMoved(JSObject* obj, const JSObject* old);
// Detach the ArrayBuffer and return its contents (if possible), or
// possibly a copy of its contents (if forceCopy is passed or the buffer
// does not own the data).
static BufferContents stealContents(JSContext* cx, static BufferContents stealContents(JSContext* cx,
Handle<ArrayBufferObject*> buffer, Handle<ArrayBufferObject*> buffer,
bool hasStealableContents); bool forceCopy);
bool hasStealableContents() const { // Some types of buffer are simply not willing to be detached, eg Wasm
// Inline elements strictly adhere to the corresponding buffer. // buffers.
if (!ownsData()) bool hasDetachableContents() const {
if (isDetached())
return false; return false;
return isPlain() || isMapped();
// Detached contents aren't transferrable because we want a detached
// buffer'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
// detached buffer -- not the original byteLength.
return !isDetached();
} }
// Return whether the buffer is allocated by js_malloc and should be freed // Return whether the buffer is allocated by js_malloc and should be freed
@ -286,8 +281,8 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
// Detach this buffer from its original memory. (This necessarily makes // Detach this buffer from its original memory. (This necessarily makes
// views of this buffer unusable for modifying that original memory.) // views of this buffer unusable for modifying that original memory.)
static MOZ_MUST_USE bool static void detach(JSContext* cx, Handle<ArrayBufferObject*> buffer,
detach(JSContext* cx, Handle<ArrayBufferObject*> buffer, BufferContents newContents); BufferContents newContents);
private: private:
void changeViewContents(JSContext* cx, ArrayBufferViewObject* view, void changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
@ -325,6 +320,8 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
bool isMapped() const { return bufferKind() == MAPPED; } bool isMapped() const { return bufferKind() == MAPPED; }
bool isDetached() const { return flags() & DETACHED; } bool isDetached() const { return flags() & DETACHED; }
bool ownsData() const { return flags() & OWNS_DATA; }
static ArrayBufferObject* createForWasm(JSContext* cx, uint32_t numBytes, bool signalsForOOB); static ArrayBufferObject* createForWasm(JSContext* cx, uint32_t numBytes, bool signalsForOOB);
static bool prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buffer, bool signalsForOOB); static bool prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buffer, bool signalsForOOB);
@ -355,7 +352,6 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
uint32_t flags() const; uint32_t flags() const;
void setFlags(uint32_t flags); void setFlags(uint32_t flags);
bool ownsData() const { return flags() & OWNS_DATA; }
void setOwnsData(OwnsState owns) { void setOwnsData(OwnsState owns) {
setFlags(owns ? (flags() | OWNS_DATA) : (flags() & ~OWNS_DATA)); setFlags(owns ? (flags() | OWNS_DATA) : (flags() & ~OWNS_DATA));
} }

View File

@ -1339,13 +1339,10 @@ JSStructuredCloneWriter::transferOwnership()
// Structured cloning currently only has optimizations for mapped // Structured cloning currently only has optimizations for mapped
// and malloc'd buffers, not asm.js-ified buffers. // and malloc'd buffers, not asm.js-ified buffers.
bool hasStealableContents = arrayBuffer->hasStealableContents() &&
(arrayBuffer->isMapped() || arrayBuffer->hasMallocedContents());
ArrayBufferObject::BufferContents bufContents = ArrayBufferObject::BufferContents bufContents =
ArrayBufferObject::stealContents(context(), arrayBuffer, hasStealableContents); ArrayBufferObject::stealContents(context(), arrayBuffer, false);
if (!bufContents) if (!bufContents)
return false; // Destructor will clean up the already-transferred data. return false; // already transferred data or non-transferable buffer type
content = bufContents.data(); content = bufContents.data();
tag = SCTAG_TRANSFER_MAP_ARRAY_BUFFER; tag = SCTAG_TRANSFER_MAP_ARRAY_BUFFER;