Bug 1167411 - Add JSAutoStructuredCloneBuffer::abandon, r=jorendorff

This commit is contained in:
Steve Fink 2015-05-26 09:11:04 -07:00
parent f19ea84f9a
commit 5ab5b05e52
2 changed files with 52 additions and 40 deletions

View File

@ -162,17 +162,25 @@ class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
uint64_t* data_;
size_t nbytes_;
uint32_t version_;
enum {
OwnsTransferablesIfAny,
IgnoreTransferablesIfAny,
NoTransferables
} ownTransferables_;
const JSStructuredCloneCallbacks* callbacks_;
void* closure_;
public:
JSAutoStructuredCloneBuffer()
: data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION),
ownTransferables_(NoTransferables),
callbacks_(nullptr), closure_(nullptr)
{}
JSAutoStructuredCloneBuffer(const JSStructuredCloneCallbacks* callbacks, void* closure)
: data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION),
ownTransferables_(NoTransferables),
callbacks_(callbacks), closure_(closure)
{}
@ -194,11 +202,17 @@ class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
// JSAutoStructuredCloneBuffer::steal).
void adopt(uint64_t* data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION);
// Remove the buffer so that it will not be automatically freed.
// After this, the caller is responsible for feeding the memory back to
// JSAutoStructuredCloneBuffer::adopt.
// Release the buffer and transfer ownership to the caller. The caller is
// responsible for calling JS_ClearStructuredClone or feeding the memory
// back to JSAutoStructuredCloneBuffer::adopt.
void steal(uint64_t** datap, size_t* nbytesp, uint32_t* versionp=nullptr);
// Abandon ownership of any transferable objects stored in the buffer,
// without freeing the buffer itself. Useful when copying the data out into
// an external container, though note that you will need to use adopt() or
// JS_ClearStructuredClone to properly release that data eventually.
void abandon() { ownTransferables_ = IgnoreTransferablesIfAny; }
bool read(JSContext* cx, JS::MutableHandleValue vp,
const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);

View File

@ -383,7 +383,8 @@ ReadStructuredClone(JSContext* cx, uint64_t* data, size_t nbytes, MutableHandleV
// Transferables will use the JSStructuredCloneCallbacks::freeTransfer() to
// delete their transferables.
static void
Discard(uint64_t* buffer, size_t nbytes, const JSStructuredCloneCallbacks* cb, void* cbClosure)
DiscardTransferables(uint64_t* buffer, size_t nbytes,
const JSStructuredCloneCallbacks* cb, void* cbClosure)
{
MOZ_ASSERT(nbytes % sizeof(uint64_t) == 0);
if (nbytes < sizeof(uint64_t))
@ -431,27 +432,15 @@ Discard(uint64_t* buffer, size_t nbytes, const JSStructuredCloneCallbacks* cb, v
}
}
static void
ClearStructuredClone(uint64_t* data, size_t nbytes,
const JSStructuredCloneCallbacks* cb, void* cbClosure)
static bool
StructuredCloneHasTransferObjects(const uint64_t* data, size_t nbytes)
{
Discard(data, nbytes, cb, cbClosure);
js_free(data);
}
if (!data)
return false;
bool
StructuredCloneHasTransferObjects(const uint64_t* data, size_t nbytes, bool* hasTransferable)
{
*hasTransferable = false;
if (data) {
uint64_t u = LittleEndian::readUint64(data);
uint32_t tag = uint32_t(u >> 32);
if (tag == SCTAG_TRANSFER_MAP_HEADER)
*hasTransferable = true;
}
return true;
uint64_t u = LittleEndian::readUint64(data);
uint32_t tag = uint32_t(u >> 32);
return (tag == SCTAG_TRANSFER_MAP_HEADER);
}
namespace js {
@ -734,7 +723,8 @@ JSStructuredCloneWriter::~JSStructuredCloneWriter()
uint64_t* data;
size_t size;
MOZ_ALWAYS_TRUE(extractBuffer(&data, &size));
ClearStructuredClone(data, size, callbacks, closure);
DiscardTransferables(data, size, callbacks, closure);
js_free(data);
}
bool
@ -1789,8 +1779,9 @@ JSStructuredCloneReader::readTransferMap()
MOZ_ASSERT(!cx->isExceptionPending());
}
// On failure, the buffer will still own the data (since its ownership will not get set to SCTAG_TMO_UNOWNED),
// so the data will be freed by ClearStructuredClone
// On failure, the buffer will still own the data (since its ownership
// will not get set to SCTAG_TMO_UNOWNED), so the data will be freed by
// DiscardTransferables.
if (!obj)
return false;
@ -1919,7 +1910,8 @@ JS_ClearStructuredClone(uint64_t* data, size_t nbytes,
const JSStructuredCloneCallbacks* optionalCallbacks,
void* closure)
{
ClearStructuredClone(data, nbytes, optionalCallbacks, closure);
DiscardTransferables(data, nbytes, optionalCallbacks, closure);
js_free(data);
return true;
}
@ -1927,11 +1919,7 @@ JS_PUBLIC_API(bool)
JS_StructuredCloneHasTransferables(const uint64_t* data, size_t nbytes,
bool* hasTransferable)
{
bool transferable;
if (!StructuredCloneHasTransferObjects(data, nbytes, &transferable))
return false;
*hasTransferable = transferable;
*hasTransferable = StructuredCloneHasTransferObjects(data, nbytes);
return true;
}
@ -1979,6 +1967,7 @@ JS_StructuredClone(JSContext* cx, HandleValue value, MutableHandleValue vp,
JSAutoStructuredCloneBuffer::JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other)
{
ownTransferables_ = other.ownTransferables_;
other.steal(&data_, &nbytes_, &version_);
}
@ -1987,6 +1976,7 @@ JSAutoStructuredCloneBuffer::operator=(JSAutoStructuredCloneBuffer&& other)
{
MOZ_ASSERT(&other != this);
clear();
ownTransferables_ = other.ownTransferables_;
other.steal(&data_, &nbytes_, &version_);
return *this;
}
@ -1995,7 +1985,10 @@ void
JSAutoStructuredCloneBuffer::clear()
{
if (data_) {
ClearStructuredClone(data_, nbytes_, callbacks_, closure_);
if (ownTransferables_ == OwnsTransferablesIfAny)
DiscardTransferables(data_, nbytes_, callbacks_, closure_);
ownTransferables_ = NoTransferables;
js_free(data_);
data_ = nullptr;
nbytes_ = 0;
version_ = 0;
@ -2006,9 +1999,7 @@ bool
JSAutoStructuredCloneBuffer::copy(const uint64_t* srcData, size_t nbytes, uint32_t version)
{
// transferable objects cannot be copied
bool hasTransferable;
if (!StructuredCloneHasTransferObjects(data_, nbytes_, &hasTransferable) ||
hasTransferable)
if (StructuredCloneHasTransferObjects(data_, nbytes_))
return false;
uint64_t* newData = static_cast<uint64_t*>(js_malloc(nbytes));
@ -2021,6 +2012,7 @@ JSAutoStructuredCloneBuffer::copy(const uint64_t* srcData, size_t nbytes, uint32
data_ = newData;
nbytes_ = nbytes;
version_ = version;
ownTransferables_ = NoTransferables;
return true;
}
@ -2031,6 +2023,7 @@ JSAutoStructuredCloneBuffer::adopt(uint64_t* data, size_t nbytes, uint32_t versi
data_ = data;
nbytes_ = nbytes;
version_ = version;
ownTransferables_ = OwnsTransferablesIfAny;
}
void
@ -2044,6 +2037,7 @@ JSAutoStructuredCloneBuffer::steal(uint64_t** datap, size_t* nbytesp, uint32_t*
data_ = nullptr;
nbytes_ = 0;
version_ = 0;
ownTransferables_ = NoTransferables;
}
bool
@ -2073,13 +2067,17 @@ JSAutoStructuredCloneBuffer::write(JSContext* cx, HandleValue value,
void* closure)
{
clear();
bool ok = !!JS_WriteStructuredClone(cx, value, &data_, &nbytes_,
optionalCallbacks, closure,
transferable);
if (!ok) {
bool ok = JS_WriteStructuredClone(cx, value, &data_, &nbytes_,
optionalCallbacks, closure,
transferable);
if (ok) {
ownTransferables_ = OwnsTransferablesIfAny;
} else {
data_ = nullptr;
nbytes_ = 0;
version_ = JS_STRUCTURED_CLONE_VERSION;
ownTransferables_ = NoTransferables;
}
return ok;
}