Bug 1264642 - Part 4. Use BufferList to replace raw buffers in StructuredClone. r=baku r=billm r=jorendorff

In JS StructuredClone BufferList<SystemAllocPolicy> is typedef'd to
JSStructuredCloneData and use everywhere in gecko that stores structured
clone data.

This patch changed some raw pointers to UniquePtr<JSStructuredCloneData>
and some to stack allocated JSStructuredCloneData for better life time
management. Some parameters or methods are deleted because of changing
to the new data structure.

MessagePortMessage now has the exactly same structure with
ClonedMessageData. Maybe in the future they can be consolidated.

MozReview-Commit-ID: 1IY9p5eKLgv
This commit is contained in:
Kan-Ru Chen 2016-04-22 18:04:20 +08:00
parent 3dcef6a932
commit 506dfe6ea3
20 changed files with 596 additions and 545 deletions

View File

@ -133,7 +133,9 @@ StructuredCloneCallbacksError(JSContext* aCx,
NS_WARNING("Failed to clone data.");
}
const JSStructuredCloneCallbacks gCallbacks = {
} // anonymous namespace
const JSStructuredCloneCallbacks StructuredCloneHolder::sCallbacks = {
StructuredCloneCallbacksRead,
StructuredCloneCallbacksWrite,
StructuredCloneCallbacksError,
@ -142,8 +144,6 @@ const JSStructuredCloneCallbacks gCallbacks = {
StructuredCloneCallbacksFreeTransfer
};
} // anonymous namespace
// StructuredCloneHolderBase class
StructuredCloneHolderBase::StructuredCloneHolderBase(StructuredCloneScope aScope)
@ -185,9 +185,9 @@ StructuredCloneHolderBase::Write(JSContext* aCx,
MOZ_ASSERT(!mBuffer, "Double Write is not allowed");
MOZ_ASSERT(!mClearCalled, "This method cannot be called after Clear.");
mBuffer = new JSAutoStructuredCloneBuffer(mStructuredCloneScope, &gCallbacks, this);
mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>(mStructuredCloneScope, &StructuredCloneHolder::sCallbacks, this);
if (!mBuffer->write(aCx, aValue, aTransfer, &gCallbacks, this)) {
if (!mBuffer->write(aCx, aValue, aTransfer, &StructuredCloneHolder::sCallbacks, this)) {
mBuffer = nullptr;
return false;
}
@ -202,7 +202,7 @@ StructuredCloneHolderBase::Read(JSContext* aCx,
MOZ_ASSERT(mBuffer, "Read() without Write() is not allowed.");
MOZ_ASSERT(!mClearCalled, "This method cannot be called after Clear.");
bool ok = mBuffer->read(aCx, aValue, &gCallbacks, this);
bool ok = mBuffer->read(aCx, aValue, &StructuredCloneHolder::sCallbacks, this);
return ok;
}
@ -311,20 +311,18 @@ StructuredCloneHolder::Read(nsISupports* aParent,
void
StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent,
JSContext* aCx,
uint64_t* aBuffer,
size_t aBufferLength,
JSStructuredCloneData& aBuffer,
JS::MutableHandle<JS::Value> aValue,
ErrorResult& aRv)
{
ReadFromBuffer(aParent, aCx, aBuffer, aBufferLength,
ReadFromBuffer(aParent, aCx, aBuffer,
JS_STRUCTURED_CLONE_VERSION, aValue, aRv);
}
void
StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent,
JSContext* aCx,
uint64_t* aBuffer,
size_t aBufferLength,
JSStructuredCloneData& aBuffer,
uint32_t aAlgorithmVersion,
JS::MutableHandle<JS::Value> aValue,
ErrorResult& aRv)
@ -333,53 +331,18 @@ StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent,
mCreationThread == NS_GetCurrentThread());
MOZ_ASSERT(!mBuffer, "ReadFromBuffer() must be called without a Write().");
MOZ_ASSERT(aBuffer);
mozilla::AutoRestore<nsISupports*> guard(mParent);
mParent = aParent;
if (!JS_ReadStructuredClone(aCx, aBuffer, aBufferLength, aAlgorithmVersion,
mStructuredCloneScope, aValue, &gCallbacks,
if (!JS_ReadStructuredClone(aCx, aBuffer, aAlgorithmVersion,
mStructuredCloneScope, aValue, &sCallbacks,
this)) {
JS_ClearPendingException(aCx);
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
}
}
void
StructuredCloneHolder::MoveBufferDataToArray(FallibleTArray<uint8_t>& aArray,
ErrorResult& aRv)
{
MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
mCreationThread == NS_GetCurrentThread());
MOZ_ASSERT(mBuffer, "MoveBuffer() cannot be called without a Write().");
if (NS_WARN_IF(!aArray.SetLength(BufferSize(), mozilla::fallible))) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
uint64_t* buffer;
size_t size;
mBuffer->steal(&buffer, &size);
mBuffer = nullptr;
memcpy(aArray.Elements(), buffer, size);
js_free(buffer);
}
void
StructuredCloneHolder::FreeBuffer(uint64_t* aBuffer,
size_t aBufferLength)
{
MOZ_ASSERT(!mBuffer, "FreeBuffer() must be called without a Write().");
MOZ_ASSERT(aBuffer);
MOZ_ASSERT(aBufferLength);
JS_ClearStructuredClone(aBuffer, aBufferLength, &gCallbacks, this, false);
}
/* static */ JSObject*
StructuredCloneHolder::ReadFullySerializableObjects(JSContext* aCx,
JSStructuredCloneReader* aReader,

View File

@ -8,7 +8,7 @@
#include "js/StructuredClone.h"
#include "mozilla/Move.h"
#include "nsAutoPtr.h"
#include "mozilla/UniquePtr.h"
#include "nsISupports.h"
#include "nsTArray.h"
@ -102,20 +102,14 @@ public:
return !!mBuffer;
}
uint64_t* BufferData() const
JSStructuredCloneData& BufferData() const
{
MOZ_ASSERT(mBuffer, "Write() has never been called.");
return mBuffer->data();
}
size_t BufferSize() const
{
MOZ_ASSERT(mBuffer, "Write() has never been called.");
return mBuffer->nbytes();
}
protected:
nsAutoPtr<JSAutoStructuredCloneBuffer> mBuffer;
UniquePtr<JSAutoStructuredCloneBuffer> mBuffer;
StructuredCloneScope mStructuredCloneScope;
@ -172,12 +166,6 @@ public:
JS::MutableHandle<JS::Value> aValue,
ErrorResult &aRv);
// Sometimes, when IPC is involved, you must send a buffer after a Write().
// This method 'steals' the internal data from this class.
// You should free this buffer with StructuredCloneHolder::FreeBuffer().
void MoveBufferDataToArray(FallibleTArray<uint8_t>& aArray,
ErrorResult& aRv);
// Call this method to know if this object is keeping some DOM object alive.
bool HasClonedDOMObjects() const
{
@ -266,29 +254,25 @@ public:
JSStructuredCloneWriter* aWriter,
JS::Handle<JSObject*> aObj);
static const JSStructuredCloneCallbacks sCallbacks;
protected:
// If you receive a buffer from IPC, you can use this method to retrieve a
// JS::Value. It can happen that you want to pre-populate the array of Blobs
// and/or the PortIdentifiers.
void ReadFromBuffer(nsISupports* aParent,
JSContext* aCx,
uint64_t* aBuffer,
size_t aBufferLength,
JSStructuredCloneData& aBuffer,
JS::MutableHandle<JS::Value> aValue,
ErrorResult &aRv);
void ReadFromBuffer(nsISupports* aParent,
JSContext* aCx,
uint64_t* aBuffer,
size_t aBufferLength,
JSStructuredCloneData& aBuffer,
uint32_t aAlgorithmVersion,
JS::MutableHandle<JS::Value> aValue,
ErrorResult &aRv);
// Use this method to free a buffer generated by MoveToBuffer().
void FreeBuffer(uint64_t* aBuffer,
size_t aBufferLength);
bool mSupportsCloning;
bool mSupportsTransferring;

View File

@ -276,8 +276,13 @@ BuildClonedMessageData(typename BlobTraits<Flavor>::ConcreteContentManagerType*
ClonedMessageData& aClonedData)
{
SerializedStructuredCloneBuffer& buffer = aClonedData.data();
buffer.data = aData.Data();
buffer.dataLength = aData.DataLength();
auto iter = aData.Data().Iter();
size_t size = aData.Data().Size();
bool success;
buffer.data = aData.Data().Borrow<js::SystemAllocPolicy>(iter, size, &success);
if (NS_WARN_IF(!success)) {
return false;
}
aClonedData.identfiers().AppendElements(aData.PortIdentifiers());
const nsTArray<RefPtr<BlobImpl>>& blobImpls = aData.BlobImpls();
@ -325,7 +330,7 @@ UnpackClonedMessageData(const ClonedMessageData& aClonedData,
const InfallibleTArray<ProtocolType*>& blobs = DataBlobs<Flavor>::Blobs(aClonedData);
const InfallibleTArray<MessagePortIdentifier>& identifiers = aClonedData.identfiers();
aData.UseExternalData(buffer.data, buffer.dataLength);
aData.UseExternalData(buffer.data);
aData.PortIdentifiers().AppendElements(identifiers);

View File

@ -137,7 +137,11 @@ nsStructuredCloneContainer::GetDataAsBase64(nsAString &aOut)
return NS_ERROR_FAILURE;
}
nsAutoCString binaryData(reinterpret_cast<char*>(Data()), DataLength());
auto iter = Data().Iter();
size_t size = Data().Size();
nsAutoCString binaryData;
binaryData.SetLength(size);
Data().ReadBytes(iter, binaryData.BeginWriting(), size);
nsAutoCString base64Data;
nsresult rv = Base64Encode(binaryData, base64Data);
if (NS_WARN_IF(NS_FAILED(rv))) {

View File

@ -152,9 +152,13 @@ public:
ClonedMessageData message;
bool success;
SerializedStructuredCloneBuffer& buffer = message.data();
buffer.data = mData->BufferData();
buffer.dataLength = mData->BufferSize();
auto iter = mData->BufferData().Iter();
buffer.data = mData->BufferData().Borrow<js::SystemAllocPolicy>(iter, mData->BufferData().Size(), &success);
if (NS_WARN_IF(!success)) {
return NS_OK;
}
PBackgroundChild* backgroundManager = mActor->Manager();
MOZ_ASSERT(backgroundManager);

View File

@ -91,12 +91,11 @@ BroadcastChannelChild::RecvNotify(const ClonedMessageData& aData)
cloneData.BlobImpls().AppendElements(blobs);
const SerializedStructuredCloneBuffer& buffer = aData.data();
cloneData.UseExternalData(buffer.data, buffer.dataLength);
JSContext* cx = jsapi.cx();
JS::Rooted<JS::Value> value(cx, JS::NullValue());
if (buffer.dataLength) {
if (buffer.data.Size()) {
ErrorResult rv;
cloneData.UseExternalData(buffer.data);
cloneData.Read(cx, &value, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();

View File

@ -871,7 +871,7 @@ CommonStructuredCloneReadCallback(JSContext* aCx,
void
ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer)
{
if (aBuffer.data()) {
if (!aBuffer.empty()) {
aBuffer.clear();
}
}
@ -1042,7 +1042,7 @@ IDBObjectStore::ClearCloneReadInfo(StructuredCloneReadInfo& aReadInfo)
// This is kind of tricky, we only want to release stuff on the main thread,
// but we can end up being called on other threads if we have already been
// cleared on the main thread.
if (!aReadInfo.mCloneBuffer.data() && !aReadInfo.mFiles.Length()) {
if (aReadInfo.mCloneBuffer.empty() && !aReadInfo.mFiles.Length()) {
return;
}
@ -1063,13 +1063,18 @@ IDBObjectStore::DeserializeValue(JSContext* aCx,
return true;
}
auto* data = reinterpret_cast<uint64_t*>(aCloneReadInfo.mData.Elements());
char* data = reinterpret_cast<char*>(aCloneReadInfo.mData.Elements());
size_t dataLen = aCloneReadInfo.mData.Length();
MOZ_ASSERT(!(dataLen % sizeof(*data)));
MOZ_ASSERT(!(dataLen % sizeof(uint64_t)));
JSAutoRequest ar(aCx);
JSStructuredCloneData buf;
if (!buf.WriteBytes(data, dataLen)) {
return false;
}
static const JSStructuredCloneCallbacks callbacks = {
CommonStructuredCloneReadCallback<ValueDeserializationHelper>,
nullptr,
@ -1081,7 +1086,7 @@ IDBObjectStore::DeserializeValue(JSContext* aCx,
// FIXME: Consider to use StructuredCloneHolder here and in other
// deserializing methods.
if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
if (!JS_ReadStructuredClone(aCx, buf, JS_STRUCTURED_CLONE_VERSION,
JS::StructuredCloneScope::SameProcessSameThread,
aValue, &callbacks, &aCloneReadInfo)) {
return false;
@ -1105,22 +1110,24 @@ IDBObjectStore::DeserializeIndexValue(JSContext* aCx,
}
size_t dataLen = aCloneReadInfo.mData.Length();
char* data = reinterpret_cast<char*>(aCloneReadInfo.mData.Elements());
uint64_t* data =
const_cast<uint64_t*>(reinterpret_cast<uint64_t*>(
aCloneReadInfo.mData.Elements()));
MOZ_ASSERT(!(dataLen % sizeof(*data)));
MOZ_ASSERT(!(dataLen % sizeof(uint64_t)));
JSAutoRequest ar(aCx);
JSStructuredCloneData buf;
if (!buf.WriteBytes(data, dataLen)) {
return false;
}
static const JSStructuredCloneCallbacks callbacks = {
CommonStructuredCloneReadCallback<IndexDeserializationHelper>,
nullptr,
nullptr
};
if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
if (!JS_ReadStructuredClone(aCx, buf, JS_STRUCTURED_CLONE_VERSION,
JS::StructuredCloneScope::SameProcessSameThread,
aValue, &callbacks, &aCloneReadInfo)) {
return false;
@ -1145,16 +1152,19 @@ IDBObjectStore::DeserializeUpgradeValue(JSContext* aCx,
return true;
}
size_t dataLen = aCloneReadInfo.mData.Length();
char* data = reinterpret_cast<char*>(aCloneReadInfo.mData.Elements());
uint64_t* data =
const_cast<uint64_t*>(reinterpret_cast<uint64_t*>(
aCloneReadInfo.mData.Elements()));
MOZ_ASSERT(!(dataLen % sizeof(*data)));
MOZ_ASSERT(!(dataLen % sizeof(uint64_t)));
JSAutoRequest ar(aCx);
JSStructuredCloneData buf;
if (!buf.WriteBytes(data, dataLen)) {
return false;
}
static JSStructuredCloneCallbacks callbacks = {
CommonStructuredCloneReadCallback<UpgradeDeserializationHelper>,
nullptr,
@ -1164,7 +1174,7 @@ IDBObjectStore::DeserializeUpgradeValue(JSContext* aCx,
nullptr
};
if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
if (!JS_ReadStructuredClone(aCx, buf, JS_STRUCTURED_CLONE_VERSION,
JS::StructuredCloneScope::SameProcessSameThread,
aValue, &callbacks, &aCloneReadInfo)) {
return false;
@ -1297,15 +1307,21 @@ IDBObjectStore::AddOrPut(JSContext* aCx,
}
FallibleTArray<uint8_t> cloneData;
if (NS_WARN_IF(!cloneData.SetLength(cloneWriteInfo.mCloneBuffer.nbytes(),
fallible))) {
size_t size = cloneWriteInfo.mCloneBuffer.data().Size();
if (NS_WARN_IF(!cloneData.SetLength(size, fallible))) {
aRv = NS_ERROR_OUT_OF_MEMORY;
return nullptr;
}
// XXX Remove this
memcpy(cloneData.Elements(), cloneWriteInfo.mCloneBuffer.data(),
cloneWriteInfo.mCloneBuffer.nbytes());
const char* buf;
auto iter = cloneWriteInfo.mCloneBuffer.data().Iter();
cloneWriteInfo.mCloneBuffer.data().FlattenBytes(iter, &buf, size);
// FIXME Bug XXXXXX Change SerializedStructuredCloneReadInfo and
// SerializedStructuredCloneWriteInfo to use JSStructuredCloneData
// instead of raw buffer.
memcpy(cloneData.Elements(), buf, size);
cloneWriteInfo.mCloneBuffer.clear();

View File

@ -47,8 +47,6 @@ StructuredCloneFile::operator==(const StructuredCloneFile& aOther) const
inline
StructuredCloneReadInfo::StructuredCloneReadInfo()
: mDatabase(nullptr)
, mCloneBuffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr,
nullptr)
{
MOZ_COUNT_CTOR(StructuredCloneReadInfo);
}
@ -58,8 +56,6 @@ StructuredCloneReadInfo::StructuredCloneReadInfo(
SerializedStructuredCloneReadInfo&& aCloneReadInfo)
: mData(Move(aCloneReadInfo.data()))
, mDatabase(nullptr)
, mCloneBuffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr,
nullptr)
{
MOZ_COUNT_CTOR(StructuredCloneReadInfo);
}

View File

@ -28,7 +28,7 @@ namespace ipc {
bool
StructuredCloneData::Copy(const StructuredCloneData& aData)
{
if (!aData.Data()) {
if (!aData.mInitialized) {
return true;
}
@ -36,8 +36,7 @@ StructuredCloneData::Copy(const StructuredCloneData& aData)
mSharedData = aData.SharedData();
} else {
mSharedData =
SharedJSAllocatedData::CreateFromExternalData(aData.Data(),
aData.DataLength());
SharedJSAllocatedData::CreateFromExternalData(aData.Data());
NS_ENSURE_TRUE(mSharedData, false);
}
@ -48,6 +47,8 @@ StructuredCloneData::Copy(const StructuredCloneData& aData)
MOZ_ASSERT(GetSurfaces().IsEmpty());
mInitialized = true;
return true;
}
@ -56,12 +57,12 @@ StructuredCloneData::Read(JSContext* aCx,
JS::MutableHandle<JS::Value> aValue,
ErrorResult &aRv)
{
MOZ_ASSERT(Data());
MOZ_ASSERT(mInitialized);
nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
MOZ_ASSERT(global);
ReadFromBuffer(global, aCx, Data(), DataLength(), aValue, aRv);
ReadFromBuffer(global, aCx, Data(), aValue, aRv);
}
void
@ -78,64 +79,50 @@ StructuredCloneData::Write(JSContext* aCx,
JS::Handle<JS::Value> aTransfer,
ErrorResult &aRv)
{
MOZ_ASSERT(!Data());
MOZ_ASSERT(!mInitialized);
StructuredCloneHolder::Write(aCx, aValue, aTransfer, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
uint64_t* data = nullptr;
size_t dataLength = 0;
mBuffer->steal(&data, &dataLength);
JSStructuredCloneData data;
mBuffer->abandon();
mBuffer->steal(&data);
mBuffer = nullptr;
mSharedData = new SharedJSAllocatedData(data, dataLength);
mSharedData = new SharedJSAllocatedData(Move(data));
mInitialized = true;
}
void
StructuredCloneData::WriteIPCParams(IPC::Message* aMsg) const
{
WriteParam(aMsg, DataLength());
if (DataLength()) {
aMsg->WriteBytes(Data(), DataLength());
}
WriteParam(aMsg, Data());
}
bool
StructuredCloneData::ReadIPCParams(const IPC::Message* aMsg,
PickleIterator* aIter)
{
MOZ_ASSERT(!Data());
size_t dataLength = 0;
if (!ReadParam(aMsg, aIter, &dataLength)) {
MOZ_ASSERT(!mInitialized);
JSStructuredCloneData data;
if (!ReadParam(aMsg, aIter, &data)) {
return false;
}
if (!dataLength) {
return true;
}
mSharedData = SharedJSAllocatedData::AllocateForExternalData(dataLength);
NS_ENSURE_TRUE(mSharedData, false);
if (!aMsg->ReadBytesInto(aIter, mSharedData->Data(), dataLength)) {
mSharedData = nullptr;
return false;
}
mSharedData = new SharedJSAllocatedData(Move(data));
mInitialized = true;
return true;
}
bool
StructuredCloneData::CopyExternalData(const void* aData,
StructuredCloneData::CopyExternalData(const char* aData,
size_t aDataLength)
{
MOZ_ASSERT(!Data());
MOZ_ASSERT(!mInitialized);
mSharedData = SharedJSAllocatedData::CreateFromExternalData(aData,
aDataLength);
NS_ENSURE_TRUE(mSharedData, false);
mInitialized = true;
return true;
}

View File

@ -24,54 +24,43 @@ namespace ipc {
class SharedJSAllocatedData final
{
public:
SharedJSAllocatedData(uint64_t* aData, size_t aDataLength)
: mData(aData), mDataLength(aDataLength)
{
MOZ_ASSERT(mData);
}
explicit SharedJSAllocatedData(JSStructuredCloneData&& aData)
: mData(Move(aData))
{ }
static already_AddRefed<SharedJSAllocatedData>
AllocateForExternalData(size_t aDataLength)
CreateFromExternalData(const char* aData, size_t aDataLength)
{
uint64_t* data = Allocate64bitSafely(aDataLength);
if (!data) {
return nullptr;
}
JSStructuredCloneData buf;
buf.WriteBytes(aData, aDataLength);
RefPtr<SharedJSAllocatedData> sharedData =
new SharedJSAllocatedData(data, aDataLength);
new SharedJSAllocatedData(Move(buf));
return sharedData.forget();
}
static already_AddRefed<SharedJSAllocatedData>
CreateFromExternalData(const void* aData, size_t aDataLength)
CreateFromExternalData(const JSStructuredCloneData& aData)
{
JSStructuredCloneData buf;
auto iter = aData.Iter();
while (!iter.Done()) {
buf.WriteBytes(iter.Data(), iter.RemainingInSegment());
iter.Advance(aData, iter.RemainingInSegment());
}
RefPtr<SharedJSAllocatedData> sharedData =
AllocateForExternalData(aDataLength);
memcpy(sharedData->Data(), aData, aDataLength);
new SharedJSAllocatedData(Move(buf));
return sharedData.forget();
}
NS_INLINE_DECL_REFCOUNTING(SharedJSAllocatedData)
uint64_t* Data() const { return mData; }
size_t DataLength() const { return mDataLength; }
JSStructuredCloneData& Data() { return mData; }
size_t DataLength() const { return mData.Size(); }
private:
~SharedJSAllocatedData()
{
js_free(mData);
}
~SharedJSAllocatedData() { }
static uint64_t*
Allocate64bitSafely(size_t aSize)
{
// Structured cloning requires 64-bit aligment.
return static_cast<uint64_t*>(js_malloc(std::max(sizeof(uint64_t), aSize)));
}
uint64_t* mData;
size_t mDataLength;
JSStructuredCloneData mData;
};
class StructuredCloneData : public StructuredCloneHolder
@ -81,16 +70,13 @@ public:
: StructuredCloneHolder(StructuredCloneHolder::CloningSupported,
StructuredCloneHolder::TransferringSupported,
StructuredCloneHolder::StructuredCloneScope::DifferentProcess)
, mExternalData(nullptr)
, mExternalDataLength(0)
, mInitialized(false)
{}
StructuredCloneData(const StructuredCloneData&) = delete;
~StructuredCloneData()
{
MOZ_ASSERT(!(mExternalData && mSharedData));
}
{}
StructuredCloneData&
operator=(const StructuredCloneData& aOther) = delete;
@ -120,23 +106,31 @@ public:
JS::Handle<JS::Value> aTransfers,
ErrorResult &aRv);
void UseExternalData(uint64_t* aData, size_t aDataLength)
bool UseExternalData(const JSStructuredCloneData& aData)
{
MOZ_ASSERT(!Data());
mExternalData = aData;
mExternalDataLength = aDataLength;
auto iter = aData.Iter();
bool success = false;
mExternalData =
aData.Borrow<js::SystemAllocPolicy>(iter, aData.Size(), &success);
mInitialized = true;
return success;
}
bool CopyExternalData(const void* aData, size_t aDataLength);
bool CopyExternalData(const char* aData, size_t aDataLength);
uint64_t* Data() const
JSStructuredCloneData& Data()
{
return mSharedData ? mSharedData->Data() : mExternalData;
}
const JSStructuredCloneData& Data() const
{
return mSharedData ? mSharedData->Data() : mExternalData;
}
size_t DataLength() const
{
return mSharedData ? mSharedData->DataLength() : mExternalDataLength;
return mSharedData ? mSharedData->DataLength() : mExternalData.Size();
}
SharedJSAllocatedData* SharedData() const
@ -149,10 +143,9 @@ public:
bool ReadIPCParams(const IPC::Message* aMessage, PickleIterator* aIter);
private:
uint64_t* MOZ_NON_OWNING_REF mExternalData;
size_t mExternalDataLength;
JSStructuredCloneData mExternalData;
RefPtr<SharedJSAllocatedData> mSharedData;
bool mInitialized;
};
} // namespace ipc

View File

@ -7,14 +7,18 @@ include protocol PBlob;
include DOMTypes;
using struct mozilla::SerializedStructuredCloneBuffer
from "ipc/IPCMessageUtils.h";
namespace mozilla {
namespace dom {
struct MessagePortMessage
{
MessagePortIdentifier[] transferredPorts;
uint8_t[] data;
SerializedStructuredCloneBuffer data;
PBlob[] blobs;
MessagePortIdentifier[] transferredPorts;
};
// This protocol is used for the MessageChannel/MessagePort API

View File

@ -20,66 +20,6 @@ using namespace ipc;
namespace dom {
void
SharedMessagePortMessage::Read(nsISupports* aParent,
JSContext* aCx,
JS::MutableHandle<JS::Value> aValue,
ErrorResult& aRv)
{
if (mData.IsEmpty()) {
return;
}
auto* data = reinterpret_cast<uint64_t*>(mData.Elements());
size_t dataLen = mData.Length();
MOZ_ASSERT(!(dataLen % sizeof(*data)));
ReadFromBuffer(aParent, aCx, data, dataLen, aValue, aRv);
NS_WARN_IF(aRv.Failed());
Free();
}
void
SharedMessagePortMessage::Write(JSContext* aCx,
JS::Handle<JS::Value> aValue,
JS::Handle<JS::Value> aTransfer,
ErrorResult& aRv)
{
StructuredCloneHolder::Write(aCx, aValue, aTransfer, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
FallibleTArray<uint8_t> cloneData;
MoveBufferDataToArray(cloneData, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
MOZ_ASSERT(mData.IsEmpty());
mData.SwapElements(cloneData);
}
void
SharedMessagePortMessage::Free()
{
if (!mData.IsEmpty()) {
auto* data = reinterpret_cast<uint64_t*>(mData.Elements());
size_t dataLen = mData.Length();
MOZ_ASSERT(!(dataLen % sizeof(*data)));
FreeBuffer(data, dataLen);
mData.Clear();
}
}
SharedMessagePortMessage::~SharedMessagePortMessage()
{
Free();
}
/* static */ void
SharedMessagePortMessage::FromSharedToMessagesChild(
MessagePortChild* aActor,
@ -95,7 +35,8 @@ SharedMessagePortMessage::FromSharedToMessagesChild(
for (auto& data : aData) {
MessagePortMessage* message = aArray.AppendElement();
message->data().SwapElements(data->mData);
data->mBuffer->abandon();
data->mBuffer->steal(&message->data().data);
const nsTArray<RefPtr<BlobImpl>>& blobImpls = data->BlobImpls();
if (!blobImpls.IsEmpty()) {
@ -127,7 +68,10 @@ SharedMessagePortMessage::FromMessagesToSharedChild(
for (auto& message : aArray) {
RefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
data->mData.SwapElements(message.data());
data->mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>(
JS::StructuredCloneScope::DifferentProcess, nullptr, nullptr);
data->mBuffer->adopt(Move(message.data().data), JS_STRUCTURED_CLONE_VERSION,
&StructuredCloneHolder::sCallbacks, data.get());
const nsTArray<PBlobChild*>& blobs = message.blobsChild();
if (!blobs.IsEmpty()) {
@ -167,7 +111,8 @@ SharedMessagePortMessage::FromSharedToMessagesParent(
for (auto& data : aData) {
MessagePortMessage* message = aArray.AppendElement(mozilla::fallible);
message->data().SwapElements(data->mData);
data->mBuffer->abandon();
data->mBuffer->steal(&message->data().data);
const nsTArray<RefPtr<BlobImpl>>& blobImpls = data->BlobImpls();
if (!blobImpls.IsEmpty()) {
@ -201,7 +146,10 @@ SharedMessagePortMessage::FromMessagesToSharedParent(
for (auto& message : aArray) {
RefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
data->mData.SwapElements(message.data());
data->mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>(
JS::StructuredCloneScope::DifferentProcess, nullptr, nullptr);
data->mBuffer->adopt(Move(message.data().data), JS_STRUCTURED_CLONE_VERSION,
&StructuredCloneHolder::sCallbacks, data.get());
const nsTArray<PBlobParent*>& blobs = message.blobsParent();
if (!blobs.IsEmpty()) {

View File

@ -20,25 +20,11 @@ class SharedMessagePortMessage final : public StructuredCloneHolder
public:
NS_INLINE_DECL_REFCOUNTING(SharedMessagePortMessage)
nsTArray<uint8_t> mData;
SharedMessagePortMessage()
: StructuredCloneHolder(CloningSupported, TransferringSupported,
StructuredCloneScope::DifferentProcess)
{}
void Read(nsISupports* aParent,
JSContext* aCx,
JS::MutableHandle<JS::Value> aValue,
ErrorResult& aRv);
void Write(JSContext* aCx,
JS::Handle<JS::Value> aValue,
JS::Handle<JS::Value> aTransfer,
ErrorResult& aRv);
void Free();
static void
FromSharedToMessagesChild(
MessagePortChild* aActor,
@ -62,7 +48,7 @@ public:
FallibleTArray<RefPtr<SharedMessagePortMessage>>& aData);
private:
~SharedMessagePortMessage();
~SharedMessagePortMessage() {}
};
} // namespace dom

View File

@ -421,12 +421,23 @@ bool Pickle::FlattenBytes(PickleIterator* iter, const char** data, uint32_t leng
return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length);
}
bool Pickle::ExtractBuffers(PickleIterator* iter, size_t length, BufferList* buffers) const
bool Pickle::ExtractBuffers(PickleIterator* iter, size_t length, BufferList* buffers,
uint32_t alignment) const
{
DCHECK(iter);
DCHECK(buffers);
DCHECK(alignment == 4 || alignment == 8);
DCHECK(intptr_t(header_) % alignment == 0);
if (AlignInt(length) < length) {
return false;
}
uint32_t padding_len = intptr_t(iter->iter_.Data()) % alignment;
if (!iter->iter_.AdvanceAcrossSegments(buffers_, padding_len)) {
return false;
}
bool success;
*buffers = const_cast<BufferList*>(&buffers_)->Extract(iter->iter_, length, &success);
if (!success) {

View File

@ -113,7 +113,8 @@ class Pickle {
MOZ_MUST_USE bool ReadBytesInto(PickleIterator* iter, void* data, uint32_t length) const;
MOZ_MUST_USE bool FlattenBytes(PickleIterator* iter, const char** data, uint32_t length,
uint32_t alignment = sizeof(memberAlignmentType));
MOZ_MUST_USE bool ExtractBuffers(PickleIterator* iter, size_t length, BufferList* buffers) const;
MOZ_MUST_USE bool ExtractBuffers(PickleIterator* iter, size_t length, BufferList* buffers,
uint32_t alignment = sizeof(memberAlignmentType)) const;
// Safer version of ReadInt() checks for the result not being negative.
// Use it for reading the object sizes.

View File

@ -64,21 +64,31 @@ struct null_t {
bool operator==(const null_t&) const { return true; }
};
struct MOZ_STACK_CLASS SerializedStructuredCloneBuffer
struct SerializedStructuredCloneBuffer final
{
SerializedStructuredCloneBuffer()
: data(nullptr), dataLength(0)
{ }
SerializedStructuredCloneBuffer&
operator=(const SerializedStructuredCloneBuffer& aOther)
{
data.Clear();
auto iter = aOther.data.Iter();
while (!iter.Done()) {
data.WriteBytes(iter.Data(), iter.RemainingInSegment());
iter.Advance(aOther.data, iter.RemainingInSegment());
}
return *this;
}
bool
operator==(const SerializedStructuredCloneBuffer& aOther) const
{
return this->data == aOther.data &&
this->dataLength == aOther.dataLength;
// The copy assignment operator and the equality operator are
// needed by the IPDL generated code. We relied on the copy
// assignment operator at some places but we never use the
// equality operator.
return false;
}
uint64_t* data;
size_t dataLength;
JSStructuredCloneData data;
};
} // namespace mozilla
@ -703,6 +713,55 @@ struct ParamTraits<mozilla::net::WebSocketFrameData>
}
};
template <>
struct ParamTraits<JSStructuredCloneData>
{
typedef JSStructuredCloneData paramType;
static void Write(Message* aMsg, const paramType& aParam)
{
MOZ_ASSERT(!(aParam.Size() % sizeof(uint64_t)));
WriteParam(aMsg, aParam.Size());
auto iter = aParam.Iter();
while (!iter.Done()) {
aMsg->WriteBytes(iter.Data(), iter.RemainingInSegment(), sizeof(uint64_t));
iter.Advance(aParam, iter.RemainingInSegment());
}
}
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
{
size_t length = 0;
if (!ReadParam(aMsg, aIter, &length)) {
return false;
}
MOZ_ASSERT(!(length % sizeof(uint64_t)));
mozilla::BufferList<InfallibleAllocPolicy> buffers(0, 0, 4096);
// Borrowing is not suitable to use for IPC to hand out data
// because we often want to store the data somewhere for
// processing after IPC has released the underlying buffers. One
// case is PContentChild::SendGetXPCOMProcessAttributes. We can't
// return a borrowed buffer because the out param outlives the
// IPDL callback.
if (length && !aMsg->ExtractBuffers(aIter, length, &buffers, sizeof(uint64_t))) {
return false;
}
bool success;
mozilla::BufferList<js::SystemAllocPolicy> out =
buffers.MoveFallible<js::SystemAllocPolicy>(&success);
if (!success) {
return false;
}
*aResult = JSStructuredCloneData(Move(out));
return true;
}
};
template <>
struct ParamTraits<mozilla::SerializedStructuredCloneBuffer>
{
@ -710,38 +769,17 @@ struct ParamTraits<mozilla::SerializedStructuredCloneBuffer>
static void Write(Message* aMsg, const paramType& aParam)
{
WriteParam(aMsg, aParam.dataLength);
if (aParam.dataLength) {
// Structured clone data must be 64-bit aligned.
aMsg->WriteBytes(aParam.data, aParam.dataLength, sizeof(uint64_t));
}
WriteParam(aMsg, aParam.data);
}
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
{
if (!ReadParam(aMsg, aIter, &aResult->dataLength)) {
return false;
}
if (!aResult->dataLength) {
aResult->data = nullptr;
return true;
}
const char** buffer =
const_cast<const char**>(reinterpret_cast<char**>(&aResult->data));
// Structured clone data must be 64-bit aligned.
if (!const_cast<Message*>(aMsg)->FlattenBytes(aIter, buffer, aResult->dataLength,
sizeof(uint64_t))) {
return false;
}
return true;
return ReadParam(aMsg, aIter, &aResult->data);
}
static void Log(const paramType& aParam, std::wstring* aLog)
{
LogParam(aParam.dataLength, aLog);
LogParam(aParam.data.Size(), aLog);
}
};

View File

@ -7,6 +7,8 @@
#ifndef js_StructuredClone_h
#define js_StructuredClone_h
#include "mozilla/BufferList.h"
#include <stdint.h>
#include "jstypes.h"
@ -130,9 +132,9 @@ typedef bool (*TransferStructuredCloneOp)(JSContext* cx,
uint64_t* extraData);
/**
* Called when JS_ClearStructuredClone has to free an unknown transferable
* object. Note that it should never trigger a garbage collection (and will
* assert in a debug build if it does.)
* Called when freeing an unknown transferable object. Note that it
* should never trigger a garbage collection (and will assert in a
* debug build if it does.)
*/
typedef void (*FreeTransferStructuredCloneOp)(uint32_t tag, JS::TransferableOwnership ownership,
void* content, uint64_t extraData, void* closure);
@ -152,30 +154,71 @@ struct JSStructuredCloneCallbacks {
FreeTransferStructuredCloneOp freeTransfer;
};
enum OwnTransferablePolicy {
OwnsTransferablesIfAny,
IgnoreTransferablesIfAny,
NoTransferables
};
class JSStructuredCloneData : public mozilla::BufferList<js::SystemAllocPolicy>
{
typedef js::SystemAllocPolicy AllocPolicy;
typedef mozilla::BufferList<js::SystemAllocPolicy> BufferList;
static const size_t kInitialSize = 0;
static const size_t kInitialCapacity = 4096;
static const size_t kStandardCapacity = 4096;
const JSStructuredCloneCallbacks* callbacks_;
void* closure_;
OwnTransferablePolicy ownTransferables_;
void setOptionalCallbacks(const JSStructuredCloneCallbacks* callbacks,
void* closure,
OwnTransferablePolicy policy) {
callbacks_ = callbacks;
closure_ = closure;
ownTransferables_ = policy;
}
friend struct JSStructuredCloneWriter;
friend class JS_PUBLIC_API(JSAutoStructuredCloneBuffer);
public:
explicit JSStructuredCloneData(AllocPolicy aAP = AllocPolicy())
: BufferList(kInitialSize, kInitialCapacity, kStandardCapacity, aAP)
, callbacks_(nullptr)
, closure_(nullptr)
, ownTransferables_(OwnTransferablePolicy::NoTransferables)
{}
MOZ_IMPLICIT JSStructuredCloneData(BufferList&& buffers)
: BufferList(Move(buffers))
, callbacks_(nullptr)
, closure_(nullptr)
, ownTransferables_(OwnTransferablePolicy::NoTransferables)
{}
JSStructuredCloneData(JSStructuredCloneData&& other) = default;
JSStructuredCloneData& operator=(JSStructuredCloneData&& other) = default;
~JSStructuredCloneData();
using BufferList::BufferList;
};
/** Note: if the *data contains transferable objects, it can be read only once. */
JS_PUBLIC_API(bool)
JS_ReadStructuredClone(JSContext* cx, uint64_t* data, size_t nbytes, uint32_t version,
JS_ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data, uint32_t version,
JS::StructuredCloneScope scope,
JS::MutableHandleValue vp,
const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
/**
* Note: On success, the caller is responsible for calling
* JS_ClearStructuredClone(*datap, nbytes, optionalCallbacks, closure).
*/
JS_PUBLIC_API(bool)
JS_WriteStructuredClone(JSContext* cx, JS::HandleValue v, uint64_t** datap, size_t* nbytesp,
JS_WriteStructuredClone(JSContext* cx, JS::HandleValue v, JSStructuredCloneData* data,
JS::StructuredCloneScope scope,
const JSStructuredCloneCallbacks* optionalCallbacks,
void* closure, JS::HandleValue transferable);
JS_PUBLIC_API(bool)
JS_ClearStructuredClone(uint64_t* data, size_t nbytes,
const JSStructuredCloneCallbacks* optionalCallbacks,
void *closure, bool freeData = true);
JS_PUBLIC_API(bool)
JS_StructuredCloneHasTransferables(const uint64_t* data, size_t nbytes, bool* hasTransferable);
JS_StructuredCloneHasTransferables(JSStructuredCloneData& data, bool* hasTransferable);
JS_PUBLIC_API(bool)
JS_StructuredClone(JSContext* cx, JS::HandleValue v, JS::MutableHandleValue vp,
@ -184,38 +227,29 @@ JS_StructuredClone(JSContext* cx, JS::HandleValue v, JS::MutableHandleValue vp,
/** RAII sugar for JS_WriteStructuredClone. */
class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
const JS::StructuredCloneScope scope_;
uint64_t* data_;
size_t nbytes_;
JSStructuredCloneData data_;
uint32_t version_;
enum {
OwnsTransferablesIfAny,
IgnoreTransferablesIfAny,
NoTransferables
} ownTransferables_;
const JSStructuredCloneCallbacks* callbacks_;
void* closure_;
public:
JSAutoStructuredCloneBuffer(JS::StructuredCloneScope scope,
const JSStructuredCloneCallbacks* callbacks, void* closure)
: scope_(scope), data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION),
ownTransferables_(NoTransferables),
callbacks_(callbacks), closure_(closure)
{}
: scope_(scope), version_(JS_STRUCTURED_CLONE_VERSION)
{
data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables);
}
JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other);
JSAutoStructuredCloneBuffer& operator=(JSAutoStructuredCloneBuffer&& other);
~JSAutoStructuredCloneBuffer() { clear(); }
uint64_t* data() const { return data_; }
size_t nbytes() const { return nbytes_; }
JSStructuredCloneData& data() { return data_; }
bool empty() const { return !data_.Size(); }
void clear(const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
/** Copy some memory. It will be automatically freed by the destructor. */
bool copy(const uint64_t* data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION,
bool copy(const JSStructuredCloneData& data, uint32_t version=JS_STRUCTURED_CLONE_VERSION,
const JSStructuredCloneCallbacks* callbacks=nullptr, void* closure=nullptr);
/**
@ -223,24 +257,22 @@ class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
* data must have been allocated by the JS engine (e.g., extracted via
* JSAutoStructuredCloneBuffer::steal).
*/
void adopt(uint64_t* data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION,
void adopt(JSStructuredCloneData&& data, uint32_t version=JS_STRUCTURED_CLONE_VERSION,
const JSStructuredCloneCallbacks* callbacks=nullptr, void* closure=nullptr);
/**
* 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.
* Release the buffer and transfer ownership to the caller.
*/
void steal(uint64_t** datap, size_t* nbytesp, uint32_t* versionp=nullptr,
void steal(JSStructuredCloneData* data, uint32_t* versionp=nullptr,
const JSStructuredCloneCallbacks** callbacks=nullptr, void** closure=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.
* an external container, though note that you will need to use adopt() to
* properly release that data eventually.
*/
void abandon() { ownTransferables_ = IgnoreTransferablesIfAny; }
void abandon() { data_.ownTransferables_ = OwnTransferablePolicy::IgnoreTransferablesIfAny; }
bool read(JSContext* cx, JS::MutableHandleValue vp,
const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);

View File

@ -2075,36 +2075,31 @@ class CloneBufferObject : public NativeObject {
Rooted<CloneBufferObject*> obj(cx, Create(cx));
if (!obj)
return nullptr;
uint64_t* datap;
size_t nbytes;
buffer->steal(&datap, &nbytes);
obj->setData(datap);
obj->setNBytes(nbytes);
auto data = js::MakeUnique<JSStructuredCloneData>();
if (!data) {
ReportOutOfMemory(cx);
return nullptr;
}
buffer->steal(data.get());
obj->setData(data.release());
return obj;
}
uint64_t* data() const {
return static_cast<uint64_t*>(getReservedSlot(DATA_SLOT).toPrivate());
JSStructuredCloneData* data() const {
return static_cast<JSStructuredCloneData*>(getReservedSlot(DATA_SLOT).toPrivate());
}
void setData(uint64_t* aData) {
void setData(JSStructuredCloneData* aData) {
MOZ_ASSERT(!data());
setReservedSlot(DATA_SLOT, PrivateValue(aData));
}
size_t nbytes() const {
return getReservedSlot(LENGTH_SLOT).toInt32();
}
void setNBytes(size_t nbytes) {
MOZ_ASSERT(nbytes <= UINT32_MAX);
setReservedSlot(LENGTH_SLOT, Int32Value(nbytes));
}
// Discard an owned clone buffer.
void discard() {
if (data())
JS_ClearStructuredClone(data(), nbytes(), nullptr, nullptr);
if (data()) {
JSAutoStructuredCloneBuffer clonebuf(JS::StructuredCloneScope::SameProcessSameThread, nullptr, nullptr);
clonebuf.adopt(Move(*data()));
}
setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
}
@ -2131,8 +2126,12 @@ class CloneBufferObject : public NativeObject {
char* str = JS_EncodeString(cx, args[0].toString());
if (!str)
return false;
obj->setData(reinterpret_cast<uint64_t*>(str));
obj->setNBytes(JS_GetStringLength(args[0].toString()));
size_t nbytes = JS_GetStringLength(args[0].toString());
MOZ_ASSERT(nbytes % sizeof(uint64_t) == 0);
auto buf = js::MakeUnique<JSStructuredCloneData>(nbytes, nbytes, nbytes);
js_memcpy(buf->Start(), str, nbytes);
JS_free(cx, str);
obj->setData(buf.release());
args.rval().setUndefined();
return true;
@ -2160,7 +2159,7 @@ class CloneBufferObject : public NativeObject {
}
bool hasTransferable;
if (!JS_StructuredCloneHasTransferables(obj->data(), obj->nbytes(), &hasTransferable))
if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable))
return false;
if (hasTransferable) {
@ -2168,7 +2167,11 @@ class CloneBufferObject : public NativeObject {
return false;
}
JSString* str = JS_NewStringCopyN(cx, reinterpret_cast<char*>(obj->data()), obj->nbytes());
size_t size = obj->data()->Size();
UniqueChars buffer(static_cast<char*>(js_malloc(size)));
auto iter = obj->data()->Iter();
obj->data()->ReadBytes(iter, buffer.get(), size);
JSString* str = JS_NewStringCopyN(cx, buffer.get(), size);
if (!str)
return false;
args.rval().setString(str);
@ -2249,15 +2252,14 @@ Deserialize(JSContext* cx, unsigned argc, Value* vp)
}
bool hasTransferable;
if (!JS_StructuredCloneHasTransferables(obj->data(), obj->nbytes(), &hasTransferable))
if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable))
return false;
RootedValue deserialized(cx);
if (!JS_ReadStructuredClone(cx, obj->data(), obj->nbytes(),
if (!JS_ReadStructuredClone(cx, *obj->data(),
JS_STRUCTURED_CLONE_VERSION,
JS::StructuredCloneScope::SameProcessSameThread,
&deserialized, nullptr, nullptr))
{
&deserialized, nullptr, nullptr)) {
return false;
}
args.rval().set(deserialized);

View File

@ -152,6 +152,60 @@ PairToUInt64(uint32_t tag, uint32_t data)
namespace js {
template<typename T, typename AllocPolicy>
struct BufferIterator {
typedef mozilla::BufferList<AllocPolicy> BufferList;
explicit BufferIterator(BufferList& buffer)
: mBuffer(buffer)
, mIter(buffer.Iter())
{
JS_STATIC_ASSERT(8 % sizeof(T) == 0);
}
BufferIterator operator++(int) {
BufferIterator ret = *this;
if (!mIter.AdvanceAcrossSegments(mBuffer, sizeof(T))) {
MOZ_ASSERT(false, "Failed to read StructuredCloneData. Data incomplete");
}
return ret;
}
BufferIterator& operator+=(size_t size) {
if (!mIter.AdvanceAcrossSegments(mBuffer, size)) {
MOZ_ASSERT(false, "Failed to read StructuredCloneData. Data incomplete");
}
return *this;
}
void next() {
if (!mIter.AdvanceAcrossSegments(mBuffer, sizeof(T))) {
MOZ_ASSERT(false, "Failed to read StructuredCloneData. Data incomplete");
}
}
bool done() const {
return mIter.Done();
}
bool readBytes(char* outData, size_t size) {
return mBuffer.ReadBytes(mIter, outData, size);
}
void write(const T& data) {
MOZ_ASSERT(mIter.HasRoomFor(sizeof(T)));
*reinterpret_cast<T*>(mIter.Data()) = data;
}
T peek() const {
MOZ_ASSERT(mIter.HasRoomFor(sizeof(T)));
return *reinterpret_cast<T*>(mIter.Data());
}
BufferList& mBuffer;
typename BufferList::IterImpl mIter;
};
struct SCOutput {
public:
explicit SCOutput(JSContext* cx);
@ -169,24 +223,29 @@ struct SCOutput {
template <class T>
bool writeArray(const T* p, size_t nbytes);
bool extractBuffer(uint64_t** datap, size_t* sizep);
bool extractBuffer(JSStructuredCloneData* data);
void discardTransferables(const JSStructuredCloneCallbacks* cb, void* cbClosure);
uint64_t count() const { return buf.length(); }
uint64_t* rawBuffer() { return buf.begin(); }
uint64_t count() const { return buf.Size() / sizeof(uint64_t); }
BufferIterator<uint64_t, TempAllocPolicy> iter() {
return BufferIterator<uint64_t, TempAllocPolicy>(buf);
}
private:
JSContext* cx;
Vector<uint64_t> buf;
mozilla::BufferList<TempAllocPolicy> buf;
};
class SCInput {
typedef js::BufferIterator<uint64_t, SystemAllocPolicy> BufferIterator;
public:
SCInput(JSContext* cx, uint64_t* data, size_t nbytes);
SCInput(JSContext* cx, JSStructuredCloneData& data);
JSContext* context() const { return cx; }
static void getPtr(const uint64_t* buffer, void** ptr);
static void getPair(const uint64_t* buffer, uint32_t* tagp, uint32_t* datap);
static void getPtr(uint64_t data, void** ptr);
static void getPair(uint64_t data, uint32_t* tagp, uint32_t* datap);
bool read(uint64_t* p);
bool readNativeEndian(uint64_t* p);
@ -200,8 +259,7 @@ class SCInput {
bool get(uint64_t* p);
bool getPair(uint32_t* tagp, uint32_t* datap);
uint64_t* tell() const { return point; }
uint64_t* end() const { return bufEnd; }
BufferIterator tell() const { return point; }
template <class T>
bool readArray(T* p, size_t nelems);
@ -219,8 +277,7 @@ class SCInput {
}
JSContext* cx;
uint64_t* point;
uint64_t* bufEnd;
BufferIterator point;
};
} /* namespace js */
@ -302,8 +359,13 @@ struct JSStructuredCloneWriter {
SCOutput& output() { return out; }
bool extractBuffer(uint64_t** datap, size_t* sizep) {
return out.extractBuffer(datap, sizep);
bool extractBuffer(JSStructuredCloneData* data) {
bool success = out.extractBuffer(data);
if (success) {
data->setOptionalCallbacks(callbacks, closure,
OwnTransferablePolicy::OwnsTransferablesIfAny);
}
return success;
}
JS::StructuredCloneScope cloneScope() const { return scope; }
@ -422,21 +484,21 @@ ReportDataCloneError(JSContext* cx,
}
bool
WriteStructuredClone(JSContext* cx, HandleValue v, uint64_t** bufp, size_t* nbytesp,
WriteStructuredClone(JSContext* cx, HandleValue v, JSStructuredCloneData* bufp,
JS::StructuredCloneScope scope,
const JSStructuredCloneCallbacks* cb, void* cbClosure,
Value transferable)
{
JSStructuredCloneWriter w(cx, scope, cb, cbClosure, transferable);
return w.init() && w.write(v) && w.extractBuffer(bufp, nbytesp);
return w.init() && w.write(v) && w.extractBuffer(bufp);
}
bool
ReadStructuredClone(JSContext* cx, uint64_t* data, size_t nbytes,
ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data,
JS::StructuredCloneScope scope, MutableHandleValue vp,
const JSStructuredCloneCallbacks* cb, void* cbClosure)
{
SCInput in(cx, data, nbytes);
SCInput in(cx, data);
JSStructuredCloneReader r(in, scope, cb, cbClosure);
return r.read(vp);
}
@ -444,24 +506,25 @@ ReadStructuredClone(JSContext* cx, uint64_t* data, size_t nbytes,
// If the given buffer contains Transferables, free them. Note that custom
// Transferables will use the JSStructuredCloneCallbacks::freeTransfer() to
// delete their transferables.
template<typename AllocPolicy>
static void
DiscardTransferables(uint64_t* buffer, size_t nbytes,
DiscardTransferables(mozilla::BufferList<AllocPolicy>& buffer,
const JSStructuredCloneCallbacks* cb, void* cbClosure)
{
MOZ_ASSERT(nbytes % sizeof(uint64_t) == 0);
uint64_t* end = buffer + nbytes / sizeof(uint64_t);
uint64_t* point = buffer;
if (point == end)
auto point = BufferIterator<uint64_t, AllocPolicy>(buffer);
if (point.done())
return; // Empty buffer
uint32_t tag, data;
SCInput::getPair(point++, &tag, &data);
SCInput::getPair(point.peek(), &tag, &data);
point.next();
if (tag == SCTAG_HEADER) {
if (point == end)
if (point.done())
return;
SCInput::getPair(point++, &tag, &data);
SCInput::getPair(point.peek(), &tag, &data);
point.next();
}
if (tag != SCTAG_TRANSFER_MAP_HEADER)
@ -473,26 +536,30 @@ DiscardTransferables(uint64_t* buffer, size_t nbytes,
// freeTransfer should not GC
JS::AutoSuppressGCAnalysis nogc;
if (point == end)
if (point.done())
return;
uint64_t numTransferables = LittleEndian::readUint64(point++);
uint64_t numTransferables = NativeEndian::swapFromLittleEndian(point.peek());
point.next();
while (numTransferables--) {
if (point == end)
if (point.done())
return;
uint32_t ownership;
SCInput::getPair(point++, &tag, &ownership);
SCInput::getPair(point.peek(), &tag, &ownership);
point.next();
MOZ_ASSERT(tag >= SCTAG_TRANSFER_MAP_PENDING_ENTRY);
if (point == end)
if (point.done())
return;
void* content;
SCInput::getPtr(point++, &content);
if (point == end)
SCInput::getPtr(point.peek(), &content);
point.next();
if (point.done())
return;
uint64_t extraData = LittleEndian::readUint64(point++);
uint64_t extraData = NativeEndian::swapFromLittleEndian(point.peek());
point.next();
if (ownership < JS::SCTAG_TMO_FIRST_OWNED)
continue;
@ -514,46 +581,51 @@ DiscardTransferables(uint64_t* buffer, size_t nbytes,
}
static bool
StructuredCloneHasTransferObjects(const uint64_t* data, size_t nbytes)
StructuredCloneHasTransferObjects(const JSStructuredCloneData& data)
{
if (!data)
auto iter = data.Iter();
if (data.Size() < sizeof(uint64_t))
return false;
uint64_t u = LittleEndian::readUint64(data);
uint64_t u;
data.ReadBytes(iter, reinterpret_cast<char*>(&u), sizeof(u));
uint32_t tag = uint32_t(u >> 32);
return (tag == SCTAG_TRANSFER_MAP_HEADER);
}
namespace js {
SCInput::SCInput(JSContext* cx, uint64_t* data, size_t nbytes)
: cx(cx), point(data), bufEnd(data + nbytes / 8)
SCInput::SCInput(JSContext* cx, JSStructuredCloneData& data)
: cx(cx), point(data)
{
// On 32-bit, we sometimes construct an SCInput from an SCOutput buffer,
// which is not guaranteed to be 8-byte aligned
MOZ_ASSERT((uintptr_t(data) & (sizeof(int) - 1)) == 0);
MOZ_ASSERT((nbytes & 7) == 0);
static_assert(JSStructuredCloneData::kSegmentAlignment % 8 == 0,
"structured clone buffer reads should be aligned");
MOZ_ASSERT(data.Size() % 8 == 0);
}
bool
SCInput::read(uint64_t* p)
{
if (point == bufEnd) {
if (point.done()) {
*p = 0; /* initialize to shut GCC up */
return reportTruncated();
}
*p = LittleEndian::readUint64(point++);
*p = NativeEndian::swapFromLittleEndian(point.peek());
point.next();
return true;
}
bool
SCInput::readNativeEndian(uint64_t* p)
{
if (point == bufEnd) {
if (point.done()) {
*p = 0; /* initialize to shut GCC up */
return reportTruncated();
}
*p = *(point++);
*p = point.peek();
point.next();
return true;
}
@ -572,9 +644,9 @@ SCInput::readPair(uint32_t* tagp, uint32_t* datap)
bool
SCInput::get(uint64_t* p)
{
if (point == bufEnd)
if (point.done())
return reportTruncated();
*p = LittleEndian::readUint64(point);
*p = NativeEndian::swapFromLittleEndian(point.peek());
return true;
}
@ -591,9 +663,9 @@ SCInput::getPair(uint32_t* tagp, uint32_t* datap)
}
void
SCInput::getPair(const uint64_t* p, uint32_t* tagp, uint32_t* datap)
SCInput::getPair(uint64_t data, uint32_t* tagp, uint32_t* datap)
{
uint64_t u = LittleEndian::readUint64(p);
uint64_t u = NativeEndian::swapFromLittleEndian(data);
*tagp = uint32_t(u >> 32);
*datap = uint32_t(u);
}
@ -613,23 +685,24 @@ SCInput::readDouble(double* p)
template <typename T>
static void
copyAndSwapFromLittleEndian(T* dest, const void* src, size_t nelems)
swapFromLittleEndianInPlace(T* ptr, size_t nelems)
{
if (nelems > 0)
NativeEndian::copyAndSwapFromLittleEndian(dest, src, nelems);
NativeEndian::swapFromLittleEndianInPlace(ptr, nelems);
}
template <>
void
copyAndSwapFromLittleEndian(uint8_t* dest, const void* src, size_t nelems)
{
memcpy(dest, src, nelems);
}
swapFromLittleEndianInPlace(uint8_t* ptr, size_t nelems)
{}
template <class T>
bool
SCInput::readArray(T* p, size_t nelems)
{
if (!nelems)
return true;
JS_STATIC_ASSERT(sizeof(uint64_t) % sizeof(T) == 0);
/*
@ -637,11 +710,17 @@ SCInput::readArray(T* p, size_t nelems)
* larger than the remaining data.
*/
size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T));
if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems || nwords > size_t(bufEnd - point))
if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems)
return reportTruncated();
copyAndSwapFromLittleEndian(p, point, nelems);
point += nwords;
size_t size = sizeof(T) * nelems;
if (!point.readBytes(reinterpret_cast<char*>(p), size))
return false;
swapFromLittleEndianInPlace(p, nelems);
point += sizeof(uint64_t) * nwords - size;
return true;
}
@ -666,11 +745,11 @@ SCInput::readChars(char16_t* p, size_t nchars)
}
void
SCInput::getPtr(const uint64_t* p, void** ptr)
SCInput::getPtr(uint64_t data, void** ptr)
{
// No endianness conversion is used for pointers, since they are not sent
// across address spaces anyway.
*ptr = reinterpret_cast<void*>(*p);
*ptr = reinterpret_cast<void*>(data);
}
bool
@ -683,12 +762,17 @@ SCInput::readPtr(void** p)
return true;
}
SCOutput::SCOutput(JSContext* cx) : cx(cx), buf(cx) {}
SCOutput::SCOutput(JSContext* cx)
: cx(cx)
, buf(0, 0, 4096, cx)
{
}
bool
SCOutput::write(uint64_t u)
{
return buf.append(NativeEndian::swapToLittleEndian(u));
uint64_t v = NativeEndian::swapToLittleEndian(u);
return buf.WriteBytes(reinterpret_cast<char*>(&v), sizeof(u));
}
bool
@ -719,26 +803,25 @@ SCOutput::writeDouble(double d)
}
template <typename T>
static void
copyAndSwapToLittleEndian(void* dest, const T* src, size_t nelems)
static T
swapToLittleEndian(T value)
{
if (nelems > 0)
NativeEndian::copyAndSwapToLittleEndian(dest, src, nelems);
return NativeEndian::swapToLittleEndian(value);
}
template <>
void
copyAndSwapToLittleEndian(void* dest, const uint8_t* src, size_t nelems)
uint8_t
swapToLittleEndian(uint8_t value)
{
memcpy(dest, src, nelems);
return value;
}
template <class T>
bool
SCOutput::writeArray(const T* p, size_t nelems)
{
MOZ_ASSERT(8 % sizeof(T) == 0);
MOZ_ASSERT(sizeof(uint64_t) % sizeof(T) == 0);
JS_STATIC_ASSERT(8 % sizeof(T) == 0);
JS_STATIC_ASSERT(sizeof(uint64_t) % sizeof(T) == 0);
if (nelems == 0)
return true;
@ -747,15 +830,22 @@ SCOutput::writeArray(const T* p, size_t nelems)
ReportAllocationOverflow(context());
return false;
}
for (size_t i = 0; i < nelems; i++) {
T value = swapToLittleEndian(p[i]);
if (!buf.WriteBytes(reinterpret_cast<char*>(&value), sizeof(value)))
return false;
}
// zero-pad to 8 bytes boundary
size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T));
size_t start = buf.length();
if (!buf.growByUninitialized(nwords))
return false;
size_t padbytes = sizeof(uint64_t) * nwords - sizeof(T) * nelems;
char zero = 0;
for (size_t i = 0; i < padbytes; i++) {
if (!buf.WriteBytes(&zero, sizeof(zero)))
return false;
}
buf.back() = 0; /* zero-pad to an 8-byte boundary */
T* q = (T*) &buf[start];
copyAndSwapToLittleEndian(q, p, nelems);
return true;
}
@ -788,27 +878,42 @@ SCOutput::writePtr(const void* p)
}
bool
SCOutput::extractBuffer(uint64_t** datap, size_t* sizep)
SCOutput::extractBuffer(JSStructuredCloneData* data)
{
*sizep = buf.length() * sizeof(uint64_t);
return (*datap = buf.extractOrCopyRawBuffer()) != nullptr;
bool success;
mozilla::BufferList<SystemAllocPolicy> out =
buf.MoveFallible<SystemAllocPolicy>(&success);
if (!success) {
ReportOutOfMemory(cx);
return false;
}
*data = JSStructuredCloneData(Move(out));
return true;
}
void
SCOutput::discardTransferables(const JSStructuredCloneCallbacks* cb, void* cbClosure)
{
DiscardTransferables(buf, cb, cbClosure);
}
} /* namespace js */
JSStructuredCloneData::~JSStructuredCloneData()
{
if (!Size())
return;
if (ownTransferables_ == OwnTransferablePolicy::OwnsTransferablesIfAny)
DiscardTransferables(*this, callbacks_, closure_);
}
JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX);
JSStructuredCloneWriter::~JSStructuredCloneWriter()
{
// Free any transferable data left lying around in the buffer
uint64_t* data;
size_t size;
{
AutoEnterOOMUnsafeRegion oomUnsafe;
if (!extractBuffer(&data, &size))
oomUnsafe.crash("Unable to extract clone buffer");
DiscardTransferables(data, size, callbacks, closure);
js_free(data);
if (out.count()) {
out.discardTransferables(callbacks, closure);
}
}
@ -1334,12 +1439,12 @@ JSStructuredCloneWriter::transferOwnership()
// Walk along the transferables and the transfer map at the same time,
// grabbing out pointers from the transferables and stuffing them into the
// transfer map.
uint64_t* point = out.rawBuffer();
MOZ_ASSERT(uint32_t(LittleEndian::readUint64(point) >> 32) == SCTAG_HEADER);
auto point = out.iter();
MOZ_ASSERT(uint32_t(NativeEndian::swapFromLittleEndian(point.peek()) >> 32) == SCTAG_HEADER);
point++;
MOZ_ASSERT(uint32_t(LittleEndian::readUint64(point) >> 32) == SCTAG_TRANSFER_MAP_HEADER);
MOZ_ASSERT(uint32_t(NativeEndian::swapFromLittleEndian(point.peek()) >> 32) == SCTAG_TRANSFER_MAP_HEADER);
point++;
MOZ_ASSERT(LittleEndian::readUint64(point) == transferableObjects.count());
MOZ_ASSERT(NativeEndian::swapFromLittleEndian(point.peek()) == transferableObjects.count());
point++;
RootedObject obj(context());
@ -1352,7 +1457,7 @@ JSStructuredCloneWriter::transferOwnership()
uint64_t extraData;
#if DEBUG
SCInput::getPair(point, &tag, (uint32_t*) &ownership);
SCInput::getPair(point.peek(), &tag, (uint32_t*) &ownership);
MOZ_ASSERT(tag == SCTAG_TRANSFER_MAP_PENDING_ENTRY);
MOZ_ASSERT(ownership == JS::SCTAG_TMO_UNFILLED);
#endif
@ -1407,22 +1512,23 @@ JSStructuredCloneWriter::transferOwnership()
MOZ_ASSERT(tag > SCTAG_TRANSFER_MAP_PENDING_ENTRY);
}
LittleEndian::writeUint64(point++, PairToUInt64(tag, ownership));
LittleEndian::writeUint64(point++, reinterpret_cast<uint64_t>(content));
LittleEndian::writeUint64(point++, extraData);
point.write(NativeEndian::swapToLittleEndian(PairToUInt64(tag, ownership)));
point.next();
point.write(NativeEndian::swapToLittleEndian(reinterpret_cast<uint64_t>(content)));
point.next();
point.write(NativeEndian::swapToLittleEndian(extraData));
point.next();
}
MOZ_ASSERT(point <= out.rawBuffer() + out.count());
#if DEBUG
// Make sure there aren't any more transfer map entries after the expected
// number we read out.
if (point < out.rawBuffer() + out.count()) {
if (!point.done()) {
uint32_t tag, data;
SCInput::getPair(point, &tag, &data);
SCInput::getPair(point.peek(), &tag, &data);
MOZ_ASSERT(tag < SCTAG_TRANSFER_MAP_HEADER || tag >= SCTAG_TRANSFER_MAP_END_OF_BUILTIN_TYPES);
}
#endif
return true;
}
@ -1955,7 +2061,7 @@ bool
JSStructuredCloneReader::readTransferMap()
{
JSContext* cx = context();
uint64_t* headerPos = in.tell();
auto headerPos = in.tell();
uint32_t tag, data;
if (!in.getPair(&tag, &data))
@ -1970,7 +2076,7 @@ JSStructuredCloneReader::readTransferMap()
return false;
for (uint64_t i = 0; i < numTransferables; i++) {
uint64_t* pos = in.tell();
auto pos = in.tell();
if (!in.readPair(&tag, &data))
return false;
@ -2026,21 +2132,20 @@ JSStructuredCloneReader::readTransferMap()
// Mark the SCTAG_TRANSFER_MAP_* entry as no longer owned by the input
// buffer.
*pos = PairToUInt64(tag, JS::SCTAG_TMO_UNOWNED);
MOZ_ASSERT(headerPos < pos && pos < in.end());
pos.write(PairToUInt64(tag, JS::SCTAG_TMO_UNOWNED));
MOZ_ASSERT(!pos.done());
if (!allObjs.append(ObjectValue(*obj)))
return false;
}
// Mark the whole transfer map as consumed.
MOZ_ASSERT(headerPos <= in.tell());
#ifdef DEBUG
SCInput::getPair(headerPos, &tag, &data);
SCInput::getPair(headerPos.peek(), &tag, &data);
MOZ_ASSERT(tag == SCTAG_TRANSFER_MAP_HEADER);
MOZ_ASSERT(TransferableMapHeader(data) != SCTAG_TM_TRANSFERRED);
#endif
*headerPos = PairToUInt64(SCTAG_TRANSFER_MAP_HEADER, SCTAG_TM_TRANSFERRED);
headerPos.write(PairToUInt64(SCTAG_TRANSFER_MAP_HEADER, SCTAG_TM_TRANSFERRED));
return true;
}
@ -2235,7 +2340,7 @@ JSStructuredCloneReader::read(MutableHandleValue vp)
using namespace js;
JS_PUBLIC_API(bool)
JS_ReadStructuredClone(JSContext* cx, uint64_t* buf, size_t nbytes,
JS_ReadStructuredClone(JSContext* cx, JSStructuredCloneData& buf,
uint32_t version, JS::StructuredCloneScope scope,
MutableHandleValue vp,
const JSStructuredCloneCallbacks* optionalCallbacks,
@ -2249,11 +2354,11 @@ JS_ReadStructuredClone(JSContext* cx, uint64_t* buf, size_t nbytes,
return false;
}
const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
return ReadStructuredClone(cx, buf, nbytes, scope, vp, callbacks, closure);
return ReadStructuredClone(cx, buf, scope, vp, callbacks, closure);
}
JS_PUBLIC_API(bool)
JS_WriteStructuredClone(JSContext* cx, HandleValue value, uint64_t** bufp, size_t* nbytesp,
JS_WriteStructuredClone(JSContext* cx, HandleValue value, JSStructuredCloneData* bufp,
JS::StructuredCloneScope scope,
const JSStructuredCloneCallbacks* optionalCallbacks,
void* closure, HandleValue transferable)
@ -2263,26 +2368,14 @@ JS_WriteStructuredClone(JSContext* cx, HandleValue value, uint64_t** bufp, size_
assertSameCompartment(cx, value);
const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
return WriteStructuredClone(cx, value, bufp, nbytesp, scope, callbacks, closure, transferable);
return WriteStructuredClone(cx, value, bufp, scope, callbacks, closure, transferable);
}
JS_PUBLIC_API(bool)
JS_ClearStructuredClone(uint64_t* data, size_t nbytes,
const JSStructuredCloneCallbacks* optionalCallbacks,
void* closure, bool freeData)
{
DiscardTransferables(data, nbytes, optionalCallbacks, closure);
if (freeData) {
js_free(data);
}
return true;
}
JS_PUBLIC_API(bool)
JS_StructuredCloneHasTransferables(const uint64_t* data, size_t nbytes,
JS_StructuredCloneHasTransferables(JSStructuredCloneData& data,
bool* hasTransferable)
{
*hasTransferable = StructuredCloneHasTransferObjects(data, nbytes);
*hasTransferable = StructuredCloneHasTransferObjects(data);
return true;
}
@ -2328,8 +2421,8 @@ JS_StructuredClone(JSContext* cx, HandleValue value, MutableHandleValue vp,
JSAutoStructuredCloneBuffer::JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other)
: scope_(other.scope_)
{
ownTransferables_ = other.ownTransferables_;
other.steal(&data_, &nbytes_, &version_, &callbacks_, &closure_);
data_.ownTransferables_ = other.data_.ownTransferables_;
other.steal(&data_, &version_, &data_.callbacks_, &data_.closure_);
}
JSAutoStructuredCloneBuffer&
@ -2338,8 +2431,8 @@ JSAutoStructuredCloneBuffer::operator=(JSAutoStructuredCloneBuffer&& other)
MOZ_ASSERT(&other != this);
MOZ_ASSERT(scope_ == other.scope_);
clear();
ownTransferables_ = other.ownTransferables_;
other.steal(&data_, &nbytes_, &version_, &callbacks_, &closure_);
data_.ownTransferables_ = other.data_.ownTransferables_;
other.steal(&data_, &version_, &data_.callbacks_, &data_.closure_);
return *this;
}
@ -2347,81 +2440,68 @@ void
JSAutoStructuredCloneBuffer::clear(const JSStructuredCloneCallbacks* optionalCallbacks,
void* optionalClosure)
{
if (!data_)
if (!data_.Size())
return;
const JSStructuredCloneCallbacks* callbacks =
optionalCallbacks ? optionalCallbacks : callbacks_;
void* closure = optionalClosure ? optionalClosure : closure_;
optionalCallbacks ? optionalCallbacks : data_.callbacks_;
void* closure = optionalClosure ? optionalClosure : data_.closure_;
if (ownTransferables_ == OwnsTransferablesIfAny)
DiscardTransferables(data_, nbytes_, callbacks, closure);
ownTransferables_ = NoTransferables;
js_free(data_);
data_ = nullptr;
nbytes_ = 0;
if (data_.ownTransferables_ == OwnTransferablePolicy::OwnsTransferablesIfAny)
DiscardTransferables(data_, callbacks, closure);
data_.ownTransferables_ = OwnTransferablePolicy::NoTransferables;
data_.Clear();
version_ = 0;
}
bool
JSAutoStructuredCloneBuffer::copy(const uint64_t* srcData, size_t nbytes, uint32_t version,
JSAutoStructuredCloneBuffer::copy(const JSStructuredCloneData& srcData, uint32_t version,
const JSStructuredCloneCallbacks* callbacks,
void* closure)
{
// transferable objects cannot be copied
if (StructuredCloneHasTransferObjects(data_, nbytes_))
if (StructuredCloneHasTransferObjects(srcData))
return false;
uint64_t* newData = static_cast<uint64_t*>(js_malloc(nbytes));
if (!newData)
return false;
js_memcpy(newData, srcData, nbytes);
clear();
data_ = newData;
nbytes_ = nbytes;
auto iter = srcData.Iter();
while (!iter.Done()) {
data_.WriteBytes(iter.Data(), iter.RemainingInSegment());
iter.Advance(srcData, iter.RemainingInSegment());
}
version_ = version;
callbacks_ = callbacks;
closure_ = closure;
ownTransferables_ = NoTransferables;
data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables);
return true;
}
void
JSAutoStructuredCloneBuffer::adopt(uint64_t* data, size_t nbytes, uint32_t version,
JSAutoStructuredCloneBuffer::adopt(JSStructuredCloneData&& data, uint32_t version,
const JSStructuredCloneCallbacks* callbacks,
void* closure)
{
clear();
data_ = data;
nbytes_ = nbytes;
data_ = Move(data);
version_ = version;
callbacks_ = callbacks;
closure_ = closure;
ownTransferables_ = OwnsTransferablesIfAny;
data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::OwnsTransferablesIfAny);
}
void
JSAutoStructuredCloneBuffer::steal(uint64_t** datap, size_t* nbytesp, uint32_t* versionp,
JSAutoStructuredCloneBuffer::steal(JSStructuredCloneData* data, uint32_t* versionp,
const JSStructuredCloneCallbacks** callbacks,
void** closure)
{
*datap = data_;
*nbytesp = nbytes_;
if (versionp)
*versionp = version_;
if (callbacks)
*callbacks = callbacks_;
*callbacks = data_.callbacks_;
if (closure)
*closure = closure_;
*closure = data_.closure_;
*data = Move(data_);
data_ = nullptr;
nbytes_ = 0;
version_ = 0;
callbacks_ = 0;
closure_ = 0;
ownTransferables_ = NoTransferables;
data_.setOptionalCallbacks(nullptr, nullptr, OwnTransferablePolicy::NoTransferables);
}
bool
@ -2430,8 +2510,7 @@ JSAutoStructuredCloneBuffer::read(JSContext* cx, MutableHandleValue vp,
void* closure)
{
MOZ_ASSERT(cx);
MOZ_ASSERT(data_);
return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, scope_, vp,
return !!JS_ReadStructuredClone(cx, data_, version_, scope_, vp,
optionalCallbacks, closure);
}
@ -2451,18 +2530,16 @@ JSAutoStructuredCloneBuffer::write(JSContext* cx, HandleValue value,
void* closure)
{
clear();
bool ok = JS_WriteStructuredClone(cx, value, &data_, &nbytes_,
bool ok = JS_WriteStructuredClone(cx, value, &data_,
scope_,
optionalCallbacks, closure,
transferable);
if (ok) {
ownTransferables_ = OwnsTransferablesIfAny;
data_.ownTransferables_ = OwnTransferablePolicy::OwnsTransferablesIfAny;
} else {
data_ = nullptr;
nbytes_ = 0;
version_ = JS_STRUCTURED_CLONE_VERSION;
ownTransferables_ = NoTransferables;
data_.ownTransferables_ = OwnTransferablePolicy::NoTransferables;
}
return ok;
}

View File

@ -78,6 +78,7 @@ class BufferList : private AllocPolicy
AllocPolicy aAP = AllocPolicy())
: AllocPolicy(aAP),
mOwning(true),
mSegments(aAP),
mSize(0),
mStandardCapacity(aStandardCapacity)
{
@ -259,7 +260,7 @@ class BufferList : private AllocPolicy
// AllocPolicy is only used for the buffer vector.
template<typename BorrowingAllocPolicy>
BufferList<BorrowingAllocPolicy> Borrow(IterImpl& aIter, size_t aSize, bool* aSuccess,
BorrowingAllocPolicy aAP = BorrowingAllocPolicy());
BorrowingAllocPolicy aAP = BorrowingAllocPolicy()) const;
// Return a new BufferList and move storage from this BufferList to it. The
// new BufferList owns the buffers. Move can fail, in which case *aSuccess
@ -432,7 +433,7 @@ BufferList<AllocPolicy>::FlattenBytes(IterImpl& aIter, const char** aOutData, si
template<typename AllocPolicy> template<typename BorrowingAllocPolicy>
BufferList<BorrowingAllocPolicy>
BufferList<AllocPolicy>::Borrow(IterImpl& aIter, size_t aSize, bool* aSuccess,
BorrowingAllocPolicy aAP)
BorrowingAllocPolicy aAP) const
{
BufferList<BorrowingAllocPolicy> result(aAP);