Bug 785167 - Copy ArrayBuffer data to separately-allocated storage when JS_GetArrayBufferData is called

This commit is contained in:
Steve Fink 2012-08-24 11:36:10 -07:00
parent aa0097bfb1
commit c27923da49
3 changed files with 59 additions and 8 deletions

View File

@ -1217,7 +1217,8 @@ JS_GetArrayBufferByteLength(JSObject *obj, JSContext *cx);
/*
* Return a pointer to an array buffer's data. The buffer is still owned by the
* array buffer object, and should not be modified on another thread.
* array buffer object, and should not be modified on another thread. The
* returned pointer is stable across GCs.
*
* |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known
* that it would pass such a test: it is an ArrayBuffer or a wrapper of an

View File

@ -202,12 +202,19 @@ ArrayBufferObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
return true;
}
/*
* Note that some callers are allowed to pass in a NULL cx, so we allocate with
* the cx if available and fall back to the runtime.
*/
static ObjectElements *
AllocateArrayBufferContents(JSContext *cx, uint32_t nbytes, uint8_t *contents)
AllocateArrayBufferContents(JSRuntime *rt, JSContext *cx, uint32_t nbytes, uint8_t *contents)
{
ObjectElements *newheader = (ObjectElements *)cx->calloc_(nbytes + sizeof(ObjectElements));
uint32_t size = nbytes + sizeof(ObjectElements);
ObjectElements *newheader =
static_cast<ObjectElements *>(cx ? cx->calloc_(size) : rt->calloc_(size));
if (!newheader) {
js_ReportOutOfMemory(cx);
if (cx)
js_ReportOutOfMemory(cx);
return NULL;
}
if (contents)
@ -228,7 +235,7 @@ ArrayBufferObject::allocateSlots(JSContext *cx, uint32_t bytes, uint8_t *content
size_t usableSlots = ARRAYBUFFER_RESERVED_SLOTS - ObjectElements::VALUES_PER_HEADER;
if (bytes > sizeof(Value) * usableSlots) {
ObjectElements *header = AllocateArrayBufferContents(cx, bytes, contents);
ObjectElements *header = AllocateArrayBufferContents(NULL, cx, bytes, contents);
if (!header)
return false;
elements = header->elements();
@ -285,6 +292,39 @@ GetViewList(ArrayBufferObject *obj)
#endif
}
bool
ArrayBufferObject::uninlineData(JSContext *cx)
{
if (hasDynamicElements())
return true;
// Grab out data before invalidating it
uint32_t bytes = byteLength();
uintptr_t oldPointer = uintptr_t(dataPointer());
JSObject *view = *GetViewList(this);
JSObject *viewListHead = view;
JSRuntime *rt = cx ? NULL : compartment()->rt;
ObjectElements *header = AllocateArrayBufferContents(rt, cx, bytes, reinterpret_cast<uint8_t*>(oldPointer));
if (!header)
return false;
elements = header->elements();
setElementsHeader(getElementsHeader(), bytes);
// Update all views
uintptr_t newPointer = uintptr_t(dataPointer());
while (view) {
uintptr_t newDataPtr = uintptr_t(view->getPrivate()) - oldPointer + newPointer;
view->setPrivate(reinterpret_cast<uint8_t*>(newDataPtr));
view = NextView(view);
}
// Restore the list of views
*GetViewList(this) = viewListHead;
return true;
}
void
ArrayBufferObject::addView(JSContext *cx, RawObject view)
{
@ -409,7 +449,7 @@ ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, void **contents)
} else {
uint32_t length = buffer.byteLength();
js::ObjectElements *newheader =
AllocateArrayBufferContents(cx, length, buffer.dataPointer());
AllocateArrayBufferContents(NULL, cx, length, buffer.dataPointer());
if (!newheader) {
js_ReportOutOfMemory(cx);
return false;
@ -3520,7 +3560,10 @@ JS_GetArrayBufferData(JSObject *objArg, JSContext *cx)
JSObject *obj = CheckedUnwrap(cx, obj_);
if (!obj)
return NULL;
return obj->asArrayBuffer().dataPointer();
ArrayBufferObject &buffer = obj->asArrayBuffer();
if (!buffer.uninlineData(cx))
return NULL;
return buffer.dataPointer();
}
JS_FRIEND_API(JSObject *)
@ -3543,7 +3586,7 @@ JS_NewArrayBufferWithContents(JSContext *cx, void *contents)
JS_PUBLIC_API(JSBool)
JS_AllocateArrayBufferContents(JSContext *cx, uint32_t nbytes, void **contents, uint8_t **data)
{
js::ObjectElements *header = AllocateArrayBufferContents(cx, nbytes, NULL);
js::ObjectElements *header = AllocateArrayBufferContents(NULL, cx, nbytes, NULL);
if (!header)
return false;

View File

@ -157,6 +157,13 @@ class ArrayBufferObject : public JSObject
bool
allocateSlots(JSContext *cx, uint32_t size, uint8_t *contents = NULL);
/*
* Ensure that the data is not stored inline. Used when handing back a
* GC-safe pointer.
*/
bool
uninlineData(JSContext *cx);
inline uint32_t byteLength() const;
inline uint8_t * dataPointer() const;