mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 01:05:45 +00:00
Bug 1240984 - Remove dummy ArrayBufferContents backstop, r=Waldo
MozReview-Commit-ID: h4oF04rDZn --HG-- extra : rebase_source : f1186c2c37c222e747b951207b0ed02a31a2639d
This commit is contained in:
parent
dc9bddc5cd
commit
8dabe9a4da
@ -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);
|
||||||
|
@ -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,
|
||||||
|
@ -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*)
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user