mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 861925 - Allow grabbing data from ArrayBuffers and neutering them independently (in addition to Steal, which does both at the same time). r=Waldo
--HG-- extra : rebase_source : 86a233d67fbd734b59ce78556b3a808e4d10faa2
This commit is contained in:
parent
bca7cd292f
commit
b8c80b5536
@ -1240,6 +1240,12 @@ JS_GetArrayBufferViewData(JSObject *obj);
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_GetArrayBufferViewBuffer(JSObject *obj);
|
||||
|
||||
/*
|
||||
* Set an ArrayBuffer's length to 0 and neuter all of its views.
|
||||
*/
|
||||
extern JS_FRIEND_API(void)
|
||||
JS_NeuterArrayBuffer(JSObject *obj, JSContext *cx);
|
||||
|
||||
/*
|
||||
* Check whether obj supports JS_GetDataView* APIs.
|
||||
*/
|
||||
|
@ -32,6 +32,8 @@
|
||||
#include "mozilla/Endian.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsdate.h"
|
||||
|
@ -244,7 +244,7 @@ AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, uint8_t *initda
|
||||
memcpy(newheader->elements(), initdata, nbytes);
|
||||
|
||||
// we rely on this being correct
|
||||
ArrayBufferObject::setElementsHeader(newheader, nbytes);
|
||||
ArrayBufferObject::updateElementsHeader(newheader, nbytes);
|
||||
|
||||
return newheader;
|
||||
}
|
||||
@ -274,7 +274,7 @@ ArrayBufferObject::allocateSlots(JSContext *maybecx, uint32_t bytes, uint8_t *co
|
||||
memset(elements, 0, bytes);
|
||||
}
|
||||
|
||||
setElementsHeader(getElementsHeader(), bytes);
|
||||
initElementsHeader(getElementsHeader(), bytes);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -297,7 +297,7 @@ PostBarrierTypedArrayObject(JSObject *obj)
|
||||
// JS_USE_NEW_OBJECT_REPRESENTATION pending, since it will solve this much
|
||||
// more cleanly.
|
||||
struct OldObjectRepresentationHack {
|
||||
uint32_t capacity;
|
||||
uint32_t flags;
|
||||
uint32_t initializedLength;
|
||||
EncapsulatedPtr<ArrayBufferViewObject> views;
|
||||
};
|
||||
@ -329,17 +329,35 @@ GetViewListRef(ArrayBufferObject *obj)
|
||||
return reinterpret_cast<OldObjectRepresentationHack*>(obj->getElementsHeader())->views;
|
||||
}
|
||||
|
||||
void
|
||||
ArrayBufferObject::neuterViews(JSContext *maybecx)
|
||||
{
|
||||
ArrayBufferViewObject *view;
|
||||
for (view = GetViewList(this); view; view = view->nextView()) {
|
||||
view->neuter();
|
||||
|
||||
// Notify compiled jit code that the base pointer has moved.
|
||||
if (maybecx)
|
||||
MarkObjectStateChange(maybecx, view);
|
||||
}
|
||||
|
||||
// neuterAsmJSArrayBuffer adjusts state specific to the ArrayBuffer data
|
||||
// itself, but it only affects the behavior of views
|
||||
if (isAsmJSArrayBuffer())
|
||||
ArrayBufferObject::neuterAsmJSArrayBuffer(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ArrayBufferObject::changeContents(JSContext *maybecx, ObjectElements *newHeader)
|
||||
{
|
||||
// Grab out data before invalidating it.
|
||||
uint32_t byteLengthCopy = byteLength();
|
||||
uintptr_t oldDataPointer = uintptr_t(dataPointer());
|
||||
ArrayBufferViewObject *viewListHead = GetViewList(this);
|
||||
ArrayBufferViewObject *viewListHead = GetViewList(this);
|
||||
|
||||
// Update all views.
|
||||
uintptr_t newDataPointer = uintptr_t(newHeader->elements());
|
||||
for (ArrayBufferViewObject *view = viewListHead; view; view = view->nextView()) {
|
||||
for (ArrayBufferViewObject *view = viewListHead; view; view = view->nextView()) {
|
||||
uintptr_t newDataPtr = uintptr_t(view->getPrivate()) - oldDataPointer + newDataPointer;
|
||||
view->setPrivate(reinterpret_cast<uint8_t*>(newDataPtr));
|
||||
|
||||
@ -348,20 +366,35 @@ ArrayBufferObject::changeContents(JSContext *maybecx, ObjectElements *newHeader)
|
||||
MarkObjectStateChange(maybecx, view);
|
||||
}
|
||||
|
||||
// Change to the new header (now, so we can use SetViewList).
|
||||
// The list of views in the old header is reachable if the contents are
|
||||
// being transferred, so NULL it out
|
||||
SetViewList(this, NULL);
|
||||
|
||||
elements = newHeader->elements();
|
||||
|
||||
// Initialize 'newHeader'.
|
||||
ArrayBufferObject::setElementsHeader(newHeader, byteLengthCopy);
|
||||
SetViewList(this, viewListHead);
|
||||
initElementsHeader(newHeader, byteLengthCopy);
|
||||
InitViewList(this, viewListHead);
|
||||
}
|
||||
|
||||
void
|
||||
ArrayBufferObject::neuter(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(cx);
|
||||
if (hasDynamicElements() && !isAsmJSArrayBuffer()) {
|
||||
ObjectElements *oldHeader = getElementsHeader();
|
||||
changeContents(cx, ObjectElements::fromElements(fixedElements()));
|
||||
|
||||
FreeOp fop(cx->runtime(), false);
|
||||
fop.free_(oldHeader);
|
||||
}
|
||||
|
||||
uint32_t byteLen = 0;
|
||||
updateElementsHeader(getElementsHeader(), byteLen);
|
||||
}
|
||||
|
||||
bool
|
||||
ArrayBufferObject::uninlineData(JSContext *maybecx)
|
||||
ArrayBufferObject::copyData(JSContext *maybecx)
|
||||
{
|
||||
if (hasDynamicElements())
|
||||
return true;
|
||||
|
||||
ObjectElements *newHeader = AllocateArrayBufferContents(maybecx, byteLength(), dataPointer());
|
||||
if (!newHeader)
|
||||
return false;
|
||||
@ -370,6 +403,36 @@ ArrayBufferObject::uninlineData(JSContext *maybecx)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ArrayBufferObject::ensureNonInline(JSContext *maybecx)
|
||||
{
|
||||
if (hasDynamicElements())
|
||||
return true;
|
||||
return copyData(maybecx);
|
||||
}
|
||||
|
||||
// If the ArrayBuffer already contains dynamic contents, hand them back.
|
||||
// Otherwise, allocate some new contents and copy the data over, but in no case
|
||||
// modify the original ArrayBuffer. (Also, any allocated contents will have no
|
||||
// views linked to in its header.)
|
||||
ObjectElements *
|
||||
ArrayBufferObject::getTransferableContents(JSContext *maybecx, bool *callerOwns)
|
||||
{
|
||||
if (hasDynamicElements() && !isAsmJSArrayBuffer()) {
|
||||
*callerOwns = false;
|
||||
return getElementsHeader();
|
||||
}
|
||||
|
||||
uint32_t byteLen = byteLength();
|
||||
ObjectElements *newheader = AllocateArrayBufferContents(maybecx, byteLen, dataPointer());
|
||||
if (!newheader)
|
||||
return NULL;
|
||||
|
||||
initElementsHeader(newheader, byteLen);
|
||||
*callerOwns = true;
|
||||
return newheader;
|
||||
}
|
||||
|
||||
#if defined(JS_ION) && defined(JS_CPU_X64)
|
||||
// To avoid dynamically checking bounds on each load/store, asm.js code relies
|
||||
// on the SIGSEGV handler in AsmJSSignalHandlers.cpp. However, this only works
|
||||
@ -479,8 +542,9 @@ ArrayBufferObject::neuterAsmJSArrayBuffer(ArrayBufferObject &buffer)
|
||||
bool
|
||||
ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
|
||||
{
|
||||
if (!buffer->uninlineData(cx))
|
||||
if (!buffer->copyData(cx))
|
||||
return false;
|
||||
JS_ASSERT(buffer->hasDynamicElements());
|
||||
|
||||
buffer->getElementsHeader()->setIsAsmJSArrayBuffer();
|
||||
return true;
|
||||
@ -597,41 +661,28 @@ ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp
|
||||
}
|
||||
|
||||
bool
|
||||
ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, void **contents,
|
||||
uint8_t **data)
|
||||
ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, void **contents, uint8_t **data)
|
||||
{
|
||||
ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
|
||||
ArrayBufferViewObject *views = GetViewList(&buffer);
|
||||
js::ObjectElements *header = js::ObjectElements::fromElements((js::HeapSlot*)buffer.dataPointer());
|
||||
if (buffer.hasDynamicElements() && !buffer.isAsmJSArrayBuffer()) {
|
||||
SetViewList(&buffer, nullptr);
|
||||
*contents = header;
|
||||
*data = buffer.dataPointer();
|
||||
|
||||
buffer.setFixedElements();
|
||||
header = js::ObjectElements::fromElements((js::HeapSlot*)buffer.dataPointer());
|
||||
} else {
|
||||
uint32_t length = buffer.byteLength();
|
||||
js::ObjectElements *newheader =
|
||||
AllocateArrayBufferContents(cx, length, buffer.dataPointer());
|
||||
if (!newheader) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
// Make the data stealable
|
||||
bool own;
|
||||
ObjectElements *header = reinterpret_cast<ObjectElements*>(buffer.getTransferableContents(cx, &own));
|
||||
if (!header)
|
||||
return false;
|
||||
*contents = header;
|
||||
*data = reinterpret_cast<uint8_t *>(header + 1);
|
||||
|
||||
ArrayBufferObject::setElementsHeader(newheader, length);
|
||||
*contents = newheader;
|
||||
*data = reinterpret_cast<uint8_t *>(newheader + 1);
|
||||
// Neuter the views, which may also mprotect(PROT_NONE) the buffer. So do
|
||||
// it after copying out the data.
|
||||
buffer.neuterViews(cx);
|
||||
|
||||
if (buffer.isAsmJSArrayBuffer())
|
||||
ArrayBufferObject::neuterAsmJSArrayBuffer(buffer);
|
||||
if (!own) {
|
||||
// If header has dynamically allocated elements, revert it back to
|
||||
// fixed-element storage before neutering it.
|
||||
buffer.changeContents(cx, ObjectElements::fromElements(buffer.fixedElements()));
|
||||
}
|
||||
|
||||
// Neuter the donor ArrayBufferObject and all views of it
|
||||
ArrayBufferObject::setElementsHeader(header, 0);
|
||||
InitViewList(&buffer, views);
|
||||
for (ArrayBufferViewObject *view = views; view; view = view->nextView())
|
||||
view->neuter();
|
||||
buffer.neuter(cx);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -3966,11 +4017,19 @@ JS_GetArrayBufferData(JSObject *obj)
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
|
||||
if (!buffer.uninlineData(nullptr))
|
||||
if (!buffer.ensureNonInline(NULL))
|
||||
return nullptr;
|
||||
return buffer.dataPointer();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
JS_NeuterArrayBuffer(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
|
||||
buffer.neuterViews(cx);
|
||||
buffer.neuter(cx);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes)
|
||||
{
|
||||
@ -3997,7 +4056,7 @@ JS_AllocateArrayBufferContents(JSContext *cx, uint32_t nbytes, void **contents,
|
||||
if (!header)
|
||||
return false;
|
||||
|
||||
ArrayBufferObject::setElementsHeader(header, nbytes);
|
||||
ArrayBufferObject::updateElementsHeader(header, nbytes);
|
||||
|
||||
*contents = header;
|
||||
*data = reinterpret_cast<uint8_t*>(header->elements());
|
||||
@ -4011,7 +4070,7 @@ JS_ReallocateArrayBufferContents(JSContext *cx, uint32_t nbytes, void **contents
|
||||
if (!header)
|
||||
return false;
|
||||
|
||||
ArrayBufferObject::setElementsHeader(header, nbytes);
|
||||
ArrayBufferObject::initElementsHeader(header, nbytes);
|
||||
|
||||
*contents = header;
|
||||
*data = reinterpret_cast<uint8_t*>(header->elements());
|
||||
|
@ -146,8 +146,7 @@ class ArrayBufferObject : public JSObject
|
||||
static bool stealContents(JSContext *cx, JSObject *obj, void **contents,
|
||||
uint8_t **data);
|
||||
|
||||
static void setElementsHeader(js::ObjectElements *header, uint32_t bytes) {
|
||||
header->flags = 0;
|
||||
static void updateElementsHeader(js::ObjectElements *header, uint32_t bytes) {
|
||||
header->initializedLength = bytes;
|
||||
|
||||
// NB: one or both of these fields is clobbered by GetViewList to store
|
||||
@ -157,6 +156,11 @@ class ArrayBufferObject : public JSObject
|
||||
header->capacity = 0;
|
||||
}
|
||||
|
||||
static void initElementsHeader(js::ObjectElements *header, uint32_t bytes) {
|
||||
header->flags = 0;
|
||||
updateElementsHeader(header, bytes);
|
||||
}
|
||||
|
||||
static uint32_t headerInitializedLength(const js::ObjectElements *header) {
|
||||
return header->initializedLength;
|
||||
}
|
||||
@ -164,22 +168,48 @@ class ArrayBufferObject : public JSObject
|
||||
void addView(ArrayBufferViewObject *view);
|
||||
|
||||
bool allocateSlots(JSContext *cx, uint32_t size, uint8_t *contents = nullptr);
|
||||
|
||||
void changeContents(JSContext *cx, ObjectElements *newHeader);
|
||||
|
||||
/*
|
||||
* Ensure that the data is not stored inline. Used when handing back a
|
||||
* Copy the data into freshly-allocated memory. Used when un-inlining or
|
||||
* when converting an ArrayBuffer to an AsmJS (MMU-assisted) ArrayBuffer.
|
||||
*/
|
||||
bool copyData(JSContext *maybecx);
|
||||
|
||||
/*
|
||||
* Ensure data is not stored inline in the object. Used when handing back a
|
||||
* GC-safe pointer.
|
||||
*/
|
||||
bool uninlineData(JSContext *cx);
|
||||
bool ensureNonInline(JSContext *maybecx);
|
||||
|
||||
uint32_t byteLength() const {
|
||||
return getElementsHeader()->initializedLength;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the contents of an ArrayBuffer without modifying the ArrayBuffer
|
||||
* itself. Set *callerOwns to true if the caller has the only pointer to
|
||||
* the returned contents (which is the case for inline or asm.js buffers),
|
||||
* and false if the ArrayBuffer still owns the pointer.
|
||||
*/
|
||||
ObjectElements *getTransferableContents(JSContext *maybecx, bool *callerOwns);
|
||||
|
||||
/*
|
||||
* Neuter all views of an ArrayBuffer.
|
||||
*/
|
||||
void neuterViews(JSContext *maybecx);
|
||||
|
||||
inline uint8_t * dataPointer() const {
|
||||
return (uint8_t *) elements;
|
||||
}
|
||||
|
||||
/*
|
||||
* Discard the ArrayBuffer contents. For asm.js buffers, at least, should
|
||||
* be called after neuterViews().
|
||||
*/
|
||||
void neuter(JSContext *maybecx);
|
||||
|
||||
/*
|
||||
* Check if the arrayBuffer contains any data. This will return false for
|
||||
* ArrayBuffer.prototype and neutered ArrayBuffers.
|
||||
@ -521,6 +551,7 @@ class DataViewObject : public ArrayBufferViewObject
|
||||
static bool fun_setFloat64(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
static JSObject *initClass(JSContext *cx);
|
||||
static void neuter(JSObject *view);
|
||||
static bool getDataPointer(JSContext *cx, Handle<DataViewObject*> obj,
|
||||
CallArgs args, size_t typeSize, uint8_t **data);
|
||||
template<typename NativeType>
|
||||
|
Loading…
Reference in New Issue
Block a user