Bug 1061288 - Make it harder for ArrayBuffer data pointers to be held across invalidations. r=smaug,terrence,jdm,roc,khuey

This commit is contained in:
Steve Fink 2014-10-07 10:44:07 -07:00
parent 5a0f8a1033
commit 7b80880b2e
26 changed files with 327 additions and 243 deletions

View File

@ -5983,7 +5983,8 @@ nsContentUtils::CreateArrayBuffer(JSContext *aCx, const nsACString& aData,
if (dataLen > 0) {
NS_ASSERTION(JS_IsArrayBufferObject(*aResult), "What happened?");
memcpy(JS_GetArrayBufferData(*aResult), aData.BeginReading(), dataLen);
JS::AutoCheckCannotGC nogc;
memcpy(JS_GetArrayBufferData(*aResult, nogc), aData.BeginReading(), dataLen);
}
return NS_OK;

View File

@ -294,8 +294,22 @@ nsDOMFileReader::DoOnLoadEnd(nsresult aStatus,
nsresult rv = NS_OK;
switch (mDataFormat) {
case FILE_AS_ARRAYBUFFER:
break; //Already accumulated mResultArrayBuffer
case FILE_AS_ARRAYBUFFER: {
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(mozilla::DOMEventTargetHelper::GetParentObject()))) {
return NS_ERROR_FAILURE;
}
RootResultArrayBuffer();
mResultArrayBuffer = JS_NewArrayBufferWithContents(jsapi.cx(), mTotal, mFileData);
if (!mResultArrayBuffer) {
JS_ClearPendingException(jsapi.cx());
rv = NS_ERROR_OUT_OF_MEMORY;
} else {
mFileData = nullptr; // Transfer ownership
}
break;
}
case FILE_AS_BINARY:
break; //Already accumulated mResult
case FILE_AS_TEXT:
@ -342,20 +356,16 @@ nsDOMFileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount)
&bytesRead);
NS_ASSERTION(bytesRead == aCount, "failed to read data");
}
else if (mDataFormat == FILE_AS_ARRAYBUFFER) {
uint32_t bytesRead = 0;
aStream->Read((char*) JS_GetArrayBufferData(mResultArrayBuffer) + mDataLen,
aCount, &bytesRead);
NS_ASSERTION(bytesRead == aCount, "failed to read data");
}
else {
//Update memory buffer to reflect the contents of the file
if (mDataLen + aCount > UINT32_MAX) {
// PR_Realloc doesn't support over 4GB memory size even if 64-bit OS
return NS_ERROR_OUT_OF_MEMORY;
}
mFileData = (char *) moz_realloc(mFileData, mDataLen + aCount);
NS_ENSURE_TRUE(mFileData, NS_ERROR_OUT_OF_MEMORY);
if (mDataFormat != FILE_AS_ARRAYBUFFER) {
mFileData = (char *) moz_realloc(mFileData, mDataLen + aCount);
NS_ENSURE_TRUE(mFileData, NS_ERROR_OUT_OF_MEMORY);
}
uint32_t bytesRead = 0;
aStream->Read(mFileData + mDataLen, aCount, &bytesRead);
@ -369,8 +379,7 @@ nsDOMFileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount)
// Helper methods
void
nsDOMFileReader::ReadFileContent(JSContext* aCx,
File& aFile,
nsDOMFileReader::ReadFileContent(File& aFile,
const nsAString &aCharset,
eDataFormat aDataFormat,
ErrorResult& aRv)
@ -443,11 +452,10 @@ nsDOMFileReader::ReadFileContent(JSContext* aCx,
DispatchProgressEvent(NS_LITERAL_STRING(LOADSTART_STR));
if (mDataFormat == FILE_AS_ARRAYBUFFER) {
RootResultArrayBuffer();
mResultArrayBuffer = JS_NewArrayBuffer(aCx, mTotal);
if (!mResultArrayBuffer) {
NS_WARNING("Failed to create JS array buffer");
aRv.Throw(NS_ERROR_FAILURE);
mFileData = js_pod_malloc<char>(mTotal);
if (!mFileData) {
NS_WARNING("Preallocation failed for ReadFileData");
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
}
}
}

View File

@ -70,17 +70,17 @@ public:
Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
void ReadAsArrayBuffer(JSContext* aCx, File& aBlob, ErrorResult& aRv)
{
ReadFileContent(aCx, aBlob, EmptyString(), FILE_AS_ARRAYBUFFER, aRv);
ReadFileContent(aBlob, EmptyString(), FILE_AS_ARRAYBUFFER, aRv);
}
void ReadAsText(File& aBlob, const nsAString& aLabel, ErrorResult& aRv)
{
ReadFileContent(nullptr, aBlob, aLabel, FILE_AS_TEXT, aRv);
ReadFileContent(aBlob, aLabel, FILE_AS_TEXT, aRv);
}
void ReadAsDataURL(File& aBlob, ErrorResult& aRv)
{
ReadFileContent(nullptr, aBlob, EmptyString(), FILE_AS_DATAURL, aRv);
ReadFileContent(aBlob, EmptyString(), FILE_AS_DATAURL, aRv);
}
using FileIOObject::Abort;
@ -104,7 +104,7 @@ public:
void ReadAsBinaryString(File& aBlob, ErrorResult& aRv)
{
ReadFileContent(nullptr, aBlob, EmptyString(), FILE_AS_BINARY, aRv);
ReadFileContent(aBlob, EmptyString(), FILE_AS_BINARY, aRv);
}
@ -124,7 +124,7 @@ protected:
FILE_AS_DATAURL
};
void ReadFileContent(JSContext* aCx, File& aBlob,
void ReadFileContent(File& aBlob,
const nsAString &aCharset, eDataFormat aDataFormat,
ErrorResult& aRv);
nsresult GetAsText(nsIDOMBlob *aFile, const nsACString &aCharset,

View File

@ -115,7 +115,8 @@ AudioBuffer::RestoreJSChannelData(JSContext* aJSContext)
if (!array) {
return false;
}
memcpy(JS_GetFloat32ArrayData(array), data, sizeof(float)*mLength);
JS::AutoCheckCannotGC nogc;
mozilla::PodCopy(JS_GetFloat32ArrayData(array, nogc), data, mLength);
mJSChannels[i] = array;
}
@ -146,9 +147,10 @@ AudioBuffer::CopyFromChannel(const Float32Array& aDestination, uint32_t aChannel
return;
}
JS::AutoCheckCannotGC nogc;
const float* sourceData = mSharedChannels ?
mSharedChannels->GetData(aChannelNumber) :
JS_GetFloat32ArrayData(mJSChannels[aChannelNumber]);
JS_GetFloat32ArrayData(mJSChannels[aChannelNumber], nogc);
PodMove(aDestination.Data(), sourceData + aStartInChannel, length);
}
@ -179,7 +181,8 @@ AudioBuffer::CopyToChannel(JSContext* aJSContext, const Float32Array& aSource,
return;
}
PodMove(JS_GetFloat32ArrayData(mJSChannels[aChannelNumber]) + aStartInChannel,
JS::AutoCheckCannotGC nogc;
PodMove(JS_GetFloat32ArrayData(mJSChannels[aChannelNumber], nogc) + aStartInChannel,
aSource.Data(), length);
}
@ -188,7 +191,8 @@ AudioBuffer::SetRawChannelContents(uint32_t aChannel, float* aContents)
{
MOZ_ASSERT(!GetWrapperPreserveColor() && !mSharedChannels,
"The AudioBuffer object should not have been handed to JS or have C++ callers neuter its typed array");
PodCopy(JS_GetFloat32ArrayData(mJSChannels[aChannel]), aContents, mLength);
JS::AutoCheckCannotGC nogc;
PodCopy(JS_GetFloat32ArrayData(mJSChannels[aChannel], nogc), aContents, mLength);
}
void

View File

@ -162,6 +162,20 @@ nsSpeechTask::Setup(nsISpeechTaskCallback* aCallback,
return NS_OK;
}
static nsRefPtr<mozilla::SharedBuffer>
makeSamples(int16_t* aData, uint32_t aDataLen)
{
nsRefPtr<mozilla::SharedBuffer> samples =
SharedBuffer::Create(aDataLen * sizeof(int16_t));
int16_t* frames = static_cast<int16_t*>(samples->Data());
for (uint32_t i = 0; i < aDataLen; i++) {
frames[i] = aData[i];
}
return samples;
}
NS_IMETHODIMP
nsSpeechTask::SendAudio(JS::Handle<JS::Value> aData, JS::Handle<JS::Value> aLandmarks,
JSContext* aCx)
@ -194,8 +208,13 @@ nsSpeechTask::SendAudio(JS::Handle<JS::Value> aData, JS::Handle<JS::Value> aLand
return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
}
SendAudioImpl(JS_GetInt16ArrayData(tsrc),
JS_GetTypedArrayLength(tsrc));
uint32_t dataLen = JS_GetTypedArrayLength(tsrc);
nsRefPtr<mozilla::SharedBuffer> samples;
{
JS::AutoCheckCannotGC nogc;
samples = makeSamples(JS_GetInt16ArrayData(tsrc, nogc), dataLen);
}
SendAudioImpl(samples, dataLen);
return NS_OK;
}
@ -214,31 +233,24 @@ nsSpeechTask::SendAudioNative(int16_t* aData, uint32_t aDataLen)
return NS_ERROR_FAILURE;
}
SendAudioImpl(aData, aDataLen);
nsRefPtr<mozilla::SharedBuffer> samples = makeSamples(aData, aDataLen);
SendAudioImpl(samples, aDataLen);
return NS_OK;
}
void
nsSpeechTask::SendAudioImpl(int16_t* aData, uint32_t aDataLen)
nsSpeechTask::SendAudioImpl(nsRefPtr<mozilla::SharedBuffer>& aSamples, uint32_t aDataLen)
{
if (aDataLen == 0) {
mStream->EndAllTrackAndFinish();
return;
}
nsRefPtr<mozilla::SharedBuffer> samples =
SharedBuffer::Create(aDataLen * sizeof(int16_t));
int16_t* frames = static_cast<int16_t*>(samples->Data());
for (uint32_t i = 0; i < aDataLen; i++) {
frames[i] = aData[i];
}
AudioSegment segment;
nsAutoTArray<const int16_t*, 1> channelData;
channelData.AppendElement(frames);
segment.AppendFrames(samples.forget(), channelData, aDataLen);
channelData.AppendElement(static_cast<int16_t*>(aSamples->Data()));
segment.AppendFrames(aSamples.forget(), channelData, aDataLen);
mStream->AppendToTrack(1, &segment);
mStream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
}

View File

@ -74,7 +74,7 @@ protected:
private:
void End();
void SendAudioImpl(int16_t* aData, uint32_t aDataLen);
void SendAudioImpl(nsRefPtr<mozilla::SharedBuffer>& aSamples, uint32_t aDataLen);
nsRefPtr<SourceMediaStream> mStream;

View File

@ -139,7 +139,7 @@ private:
template<typename T,
JSObject* UnwrapArray(JSObject*),
T* GetData(JSObject*),
T* GetData(JSObject*, const JS::AutoCheckCannotGC&),
void GetLengthAndData(JSObject*, uint32_t*, T**),
JSObject* CreateNew(JSContext*, uint32_t)>
struct TypedArray : public TypedArray_base<T, UnwrapArray, GetLengthAndData> {
@ -181,7 +181,8 @@ private:
return nullptr;
}
if (data) {
T* buf = static_cast<T*>(GetData(obj));
JS::AutoCheckCannotGC nogc;
T* buf = static_cast<T*>(GetData(obj, nogc));
memcpy(buf, data, length*sizeof(T));
}
return obj;

View File

@ -4515,18 +4515,24 @@ CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx,
return NS_OK;
}
uint8_t* data = JS_GetUint8ClampedArrayData(darray);
IntRect dstWriteRect = srcReadRect;
dstWriteRect.MoveBy(-aX, -aY);
uint8_t* src = data;
uint32_t srcStride = aWidth * 4;
uint8_t* src;
uint32_t srcStride;
if (readback) {
srcStride = readback->Stride();
src = readback->GetData() + srcReadRect.y * srcStride + srcReadRect.x * 4;
}
JS::AutoCheckCannotGC nogc;
uint8_t* data = JS_GetUint8ClampedArrayData(darray, nogc);
if (!readback) {
src = data;
srcStride = aWidth * 4;
}
// NOTE! dst is the same as src, and this relies on reading
// from src and advancing that ptr before writing to dst.
// NOTE! I'm not sure that it is, I think this comment might have been

View File

@ -343,7 +343,8 @@ FMRadio::GetRdsgroup(JSContext* cx, JS::MutableHandle<JSObject*> retval)
}
JSObject *rdsgroup = Uint16Array::Create(cx, this, 4);
uint16_t *data = JS_GetUint16ArrayData(rdsgroup);
JS::AutoCheckCannotGC nogc;
uint16_t *data = JS_GetUint16ArrayData(rdsgroup, nogc);
data[3] = group & 0xFFFF;
group >>= 16;
data[2] = group & 0xFFFF;

View File

@ -5,6 +5,7 @@
#include <algorithm>
#include "TCPSocketChild.h"
#include "mozilla/unused.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/net/NeckoChild.h"
#include "mozilla/dom/PBrowserChild.h"
#include "mozilla/dom/TabChild.h"
@ -27,14 +28,17 @@ DeserializeArrayBuffer(JS::Handle<JSObject*> aObj,
mozilla::AutoSafeJSContext cx;
JSAutoCompartment ac(cx, aObj);
JS::Rooted<JSObject*> obj(cx, JS_NewArrayBuffer(cx, aBuffer.Length()));
if (!obj)
return false;
uint8_t* data = JS_GetArrayBufferData(obj);
mozilla::UniquePtr<uint8_t[], JS::FreePolicy> data(js_pod_malloc<uint8_t>(aBuffer.Length()));
if (!data)
return false;
memcpy(data, aBuffer.Elements(), aBuffer.Length());
aVal.set(OBJECT_TO_JSVAL(obj));
return false;
memcpy(data.get(), aBuffer.Elements(), aBuffer.Length());
JSObject* obj = JS_NewArrayBufferWithContents(cx, aBuffer.Length(), data.get());
if (!obj)
return false;
data.release();
aVal.setObject(*obj);
return true;
}
@ -224,13 +228,16 @@ TCPSocketChild::SendSend(JS::Handle<JS::Value> aData,
uint32_t buflen = JS_GetArrayBufferByteLength(obj);
aByteOffset = std::min(buflen, aByteOffset);
uint32_t nbytes = std::min(buflen - aByteOffset, aByteLength);
uint8_t* data = JS_GetArrayBufferData(obj);
if (!data) {
return NS_ERROR_OUT_OF_MEMORY;
}
FallibleTArray<uint8_t> fallibleArr;
if (!fallibleArr.InsertElementsAt(0, data + aByteOffset, nbytes)) {
return NS_ERROR_OUT_OF_MEMORY;
{
JS::AutoCheckCannotGC nogc;
uint8_t* data = JS_GetArrayBufferData(obj, nogc);
if (!data) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (!fallibleArr.InsertElementsAt(0, data + aByteOffset, nbytes)) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
InfallibleTArray<uint8_t> arr;
arr.SwapElements(fallibleArr);

View File

@ -285,17 +285,27 @@ TCPSocketParent::SendEvent(const nsAString& aType, JS::Handle<JS::Value> aDataVa
} else if (aDataVal.isObject()) {
JS::Rooted<JSObject *> obj(aCx, &aDataVal.toObject());
if (JS_IsArrayBufferObject(obj)) {
uint32_t nbytes = JS_GetArrayBufferByteLength(obj);
uint8_t* buffer = JS_GetArrayBufferData(obj);
if (!buffer) {
FireInteralError(this, __LINE__);
return NS_ERROR_OUT_OF_MEMORY;
}
FallibleTArray<uint8_t> fallibleArr;
if (!fallibleArr.InsertElementsAt(0, buffer, nbytes)) {
FireInteralError(this, __LINE__);
return NS_ERROR_OUT_OF_MEMORY;
uint32_t errLine = 0;
do {
JS::AutoCheckCannotGC nogc;
uint32_t nbytes = JS_GetArrayBufferByteLength(obj);
uint8_t* buffer = JS_GetArrayBufferData(obj, nogc);
if (!buffer) {
errLine = __LINE__;
break;
}
if (!fallibleArr.InsertElementsAt(0, buffer, nbytes)) {
errLine = __LINE__;
break;
}
} while (false);
if (errLine) {
FireInteralError(this, errLine);
return NS_ERROR_OUT_OF_MEMORY;
}
InfallibleTArray<uint8_t> arr;
arr.SwapElements(fallibleArr);
data = SendableData(arr);

View File

@ -61,17 +61,8 @@ FileReaderSync::ReadAsArrayBuffer(JSContext* aCx,
return;
}
JS::Rooted<JSObject*> jsArrayBuffer(aCx, JS_NewArrayBuffer(aCx, blobSize));
if (!jsArrayBuffer) {
// XXXkhuey we need a way to indicate to the bindings that the call failed
// but there's already a pending exception that we should not clobber.
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
uint32_t bufferLength = JS_GetArrayBufferByteLength(jsArrayBuffer);
uint8_t* arrayBuffer = JS_GetStableArrayBufferData(aCx, jsArrayBuffer);
if (!arrayBuffer) {
UniquePtr<char[], JS::FreePolicy> bufferData(js_pod_malloc<char>(blobSize));
if (!bufferData) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
@ -84,14 +75,21 @@ FileReaderSync::ReadAsArrayBuffer(JSContext* aCx,
}
uint32_t numRead;
rv = stream->Read((char*)arrayBuffer, bufferLength, &numRead);
rv = stream->Read(bufferData.get(), blobSize, &numRead);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
NS_ASSERTION(numRead == bufferLength, "failed to read data");
NS_ASSERTION(numRead == blobSize, "failed to read data");
aRetval.set(jsArrayBuffer);
JSObject* arrayBuffer = JS_NewArrayBufferWithContents(aCx, blobSize, bufferData.get());
if (!arrayBuffer) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
bufferData.release();
aRetval.set(arrayBuffer);
}
void

View File

@ -92,17 +92,16 @@ PostToRIL(JSContext *aCx,
int clientId = args[0].toInt32();
JS::Value v = args[1];
JSAutoByteString abs;
void *data;
size_t size;
UnixSocketRawData* raw = nullptr;
if (v.isString()) {
JSAutoByteString abs;
JS::Rooted<JSString*> str(aCx, v.toString());
if (!abs.encodeUtf8(aCx, str)) {
return false;
}
data = abs.ptr();
size = abs.length();
raw = new UnixSocketRawData(abs.ptr(), abs.length());
} else if (!v.isPrimitive()) {
JSObject *obj = v.toObjectOrNull();
if (!JS_IsTypedArrayObject(obj)) {
@ -118,15 +117,20 @@ PostToRIL(JSContext *aCx,
return false;
}
size = JS_GetTypedArrayByteLength(obj);
data = JS_GetArrayBufferViewData(obj);
JS::AutoCheckCannotGC nogc;
size_t size = JS_GetTypedArrayByteLength(obj);
void *data = JS_GetArrayBufferViewData(obj, nogc);
raw = new UnixSocketRawData(data, size);
} else {
JS_ReportError(aCx,
"Incorrect argument. Expecting a string or a typed array");
return false;
}
UnixSocketRawData* raw = new UnixSocketRawData(data, size);
if (!raw) {
JS_ReportError(aCx, "Unable to post to RIL");
return false;
}
nsRefPtr<SendRilSocketDataTask> task =
new SendRilSocketDataTask(clientId, raw);
@ -189,8 +193,11 @@ DispatchRILEvent::RunTask(JSContext *aCx)
if (!array) {
return false;
}
memcpy(JS_GetArrayBufferViewData(array),
mMessage->GetData(), mMessage->GetSize());
{
JS::AutoCheckCannotGC nogc;
memcpy(JS_GetArrayBufferViewData(array, nogc),
mMessage->GetData(), mMessage->GetSize());
}
JS::AutoValueArray<2> args(aCx);
args[0].setNumber((uint32_t)mClientId);

View File

@ -2461,23 +2461,34 @@ ImplicitConvert(JSContext* cx,
}
break;
} else if (val.isObject() && JS_IsArrayBufferObject(valObj)) {
// Convert ArrayBuffer to pointer without any copy.
// Just as with C arrays, we make no effort to
// keep the ArrayBuffer alive.
void* p = JS_GetStableArrayBufferData(cx, valObj);
if (!p)
return false;
*static_cast<void**>(buffer) = p;
// Convert ArrayBuffer to pointer without any copy. Just as with C
// arrays, we make no effort to keep the ArrayBuffer alive. This
// functionality will be removed for all but arguments in bug 1080262.
void* ptr;
{
JS::AutoCheckCannotGC nogc;
ptr = JS_GetArrayBufferData(valObj, nogc);
}
if (!ptr) {
return TypeError(cx, "arraybuffer pointer", val);
}
*static_cast<void**>(buffer) = ptr;
break;
} if (val.isObject() && JS_IsTypedArrayObject(valObj)) {
// Same as ArrayBuffer, above, though note that this will take the offset
// of the view into account.
if(!CanConvertTypedArrayItemTo(baseType, valObj, cx)) {
return TypeError(cx, "typed array with the appropriate type", val);
}
// Convert TypedArray to pointer without any copy.
// Just as with C arrays, we make no effort to
// keep the TypedArray alive.
*static_cast<void**>(buffer) = JS_GetArrayBufferViewData(valObj);
void* ptr;
{
JS::AutoCheckCannotGC nogc;
ptr = JS_GetArrayBufferViewData(valObj, nogc);
}
if (!ptr) {
return TypeError(cx, "typed array pointer", val);
}
*static_cast<void**>(buffer) = ptr;
break;
}
return TypeError(cx, "pointer", val);
@ -2583,7 +2594,8 @@ ImplicitConvert(JSContext* cx,
JS_ReportError(cx, "ArrayType length does not match source ArrayBuffer length");
return false;
}
memcpy(buffer, JS_GetArrayBufferData(valObj), sourceLength);
JS::AutoCheckCannotGC nogc;
memcpy(buffer, JS_GetArrayBufferData(valObj, nogc), sourceLength);
break;
} else if (val.isObject() && JS_IsTypedArrayObject(valObj)) {
// Check that array is consistent with type, then
@ -2599,7 +2611,8 @@ ImplicitConvert(JSContext* cx,
JS_ReportError(cx, "typed array length does not match source TypedArray length");
return false;
}
memcpy(buffer, JS_GetArrayBufferViewData(valObj), sourceLength);
JS::AutoCheckCannotGC nogc;
memcpy(buffer, JS_GetArrayBufferViewData(valObj, nogc), sourceLength);
break;
} else {
// Don't implicitly convert to string. Users can implicitly convert

View File

@ -77,6 +77,7 @@ var ignoreCallees = {
"mozilla::CycleCollectedJSRuntime.NoteCustomGCThingXPCOMChildren" : true, // During tracing, cannot GC.
"PLDHashTableOps.hashKey" : true,
"z_stream_s.zfree" : true,
"GrGLInterface.fCallback" : true,
};
function fieldCallCannotGC(csu, fullfield)

View File

@ -44,16 +44,18 @@ BEGIN_TEST(testArrayBuffer_bug720949_steal)
CHECK_SAME(v, INT_TO_JSVAL(size));
// Modifying the underlying data should update the value returned through the view
uint8_t *data = JS_GetStableArrayBufferData(cx, obj);
CHECK(data != nullptr);
*reinterpret_cast<uint32_t*>(data) = MAGIC_VALUE_2;
{
JS::AutoCheckCannotGC nogc;
uint8_t *data = JS_GetArrayBufferData(obj, nogc);
CHECK(data != nullptr);
*reinterpret_cast<uint32_t*>(data) = MAGIC_VALUE_2;
}
CHECK(JS_GetElement(cx, view, 0, &v));
CHECK_SAME(v, INT_TO_JSVAL(MAGIC_VALUE_2));
// Steal the contents
void *contents = JS_StealArrayBufferContents(cx, obj);
CHECK(contents != nullptr);
CHECK(data != nullptr);
// Check that the original ArrayBuffer is neutered
CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), 0u);
@ -73,15 +75,21 @@ BEGIN_TEST(testArrayBuffer_bug720949_steal)
// Transfer to a new ArrayBuffer
JS::RootedObject dst(cx, JS_NewArrayBufferWithContents(cx, size, contents));
CHECK(JS_IsArrayBufferObject(dst));
data = JS_GetStableArrayBufferData(cx, obj);
{
JS::AutoCheckCannotGC nogc;
(void) JS_GetArrayBufferData(obj, nogc);
}
JS::RootedObject dstview(cx, JS_NewInt32ArrayWithBuffer(cx, dst, 0, -1));
CHECK(dstview != nullptr);
CHECK_EQUAL(JS_GetArrayBufferByteLength(dst), size);
data = JS_GetStableArrayBufferData(cx, dst);
CHECK(data != nullptr);
CHECK_EQUAL(*reinterpret_cast<uint32_t*>(data), MAGIC_VALUE_2);
{
JS::AutoCheckCannotGC nogc;
uint8_t *data = JS_GetArrayBufferData(dst, nogc);
CHECK(data != nullptr);
CHECK_EQUAL(*reinterpret_cast<uint32_t*>(data), MAGIC_VALUE_2);
}
CHECK(JS_GetElement(cx, dstview, 0, &v));
CHECK_SAME(v, INT_TO_JSVAL(MAGIC_VALUE_2));
}

View File

@ -74,6 +74,8 @@ JSObject *CreateNewObject(const int offset, const int length)
bool VerifyObject(JS::HandleObject obj, uint32_t offset, uint32_t length, const bool mapped)
{
JS::AutoCheckCannotGC nogc;
CHECK(obj);
CHECK(JS_IsArrayBufferObject(obj));
CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), length);
@ -81,7 +83,7 @@ bool VerifyObject(JS::HandleObject obj, uint32_t offset, uint32_t length, const
CHECK(JS_IsMappedArrayBufferObject(obj));
else
CHECK(!JS_IsMappedArrayBufferObject(obj));
const char *data = reinterpret_cast<const char *>(JS_GetArrayBufferData(obj));
const char *data = reinterpret_cast<const char *>(JS_GetArrayBufferData(obj, nogc));
CHECK(data);
CHECK(memcmp(data, test_data + offset, length) == 0);

View File

@ -36,8 +36,11 @@ BEGIN_TEST(testTypedArrays)
RootedObject dummy(cx, JS_GetParent(proto));
CHECK(!JS_IsArrayBufferObject(dummy));
CHECK_EQUAL(JS_GetArrayBufferByteLength(buffer), nbytes);
memset(JS_GetStableArrayBufferData(cx, buffer), 1, nbytes);
{
JS::AutoCheckCannotGC nogc;
CHECK_EQUAL(JS_GetArrayBufferByteLength(buffer), nbytes);
memset(JS_GetArrayBufferData(buffer, nogc), 1, nbytes);
}
ok = ok &&
TestArrayFromBuffer<JS_NewInt8ArrayWithBuffer, JS_NewInt8ArrayFromArray, int8_t, JS_GetInt8ArrayData>(cx) &&
@ -55,7 +58,7 @@ BEGIN_TEST(testTypedArrays)
template<JSObject *Create(JSContext *, uint32_t),
typename Element,
Element *GetData(JSObject *)>
Element *GetData(JSObject *, const JS::AutoCheckCannotGC&)>
bool
TestPlainTypedArray(JSContext *cx)
{
@ -76,9 +79,12 @@ TestPlainTypedArray(JSContext *cx)
CHECK_EQUAL(JS_GetTypedArrayByteOffset(array), 0u);
CHECK_EQUAL(JS_GetTypedArrayByteLength(array), sizeof(Element) * 7);
Element *data;
CHECK(data = GetData(array));
*data = 13;
{
JS::AutoCheckCannotGC nogc;
Element *data;
CHECK(data = GetData(array, nogc));
*data = 13;
}
RootedValue v(cx);
CHECK(JS_GetElement(cx, array, 0, &v));
CHECK_SAME(v, INT_TO_JSVAL(13));
@ -89,16 +95,17 @@ TestPlainTypedArray(JSContext *cx)
template<JSObject *CreateWithBuffer(JSContext *, JS::HandleObject, uint32_t, int32_t),
JSObject *CreateFromArray(JSContext *, JS::HandleObject),
typename Element,
Element *GetData(JSObject *)>
Element *GetData(JSObject *, const JS::AutoCheckCannotGC&)>
bool
TestArrayFromBuffer(JSContext *cx)
{
size_t elts = 8;
size_t nbytes = elts * sizeof(Element);
RootedObject buffer(cx, JS_NewArrayBuffer(cx, nbytes));
uint8_t *bufdata;
CHECK(bufdata = JS_GetStableArrayBufferData(cx, buffer));
memset(bufdata, 1, nbytes);
{
JS::AutoCheckCannotGC nogc;
memset(JS_GetArrayBufferData(buffer, nogc), 1, nbytes);
}
{
RootedObject notArray(cx, CreateWithBuffer(cx, buffer, UINT32_MAX, -1));
@ -111,13 +118,15 @@ TestArrayFromBuffer(JSContext *cx)
CHECK_EQUAL(JS_GetTypedArrayByteLength(array), nbytes);
CHECK_EQUAL(JS_GetArrayBufferViewBuffer(cx, array), (JSObject*) buffer);
Element *data;
CHECK(data = GetData(array));
CHECK(bufdata = JS_GetStableArrayBufferData(cx, buffer));
CHECK_EQUAL((void*) data, (void*) bufdata);
{
JS::AutoCheckCannotGC nogc;
Element *data;
CHECK(data = GetData(array, nogc));
CHECK_EQUAL((void*) data, (void*) JS_GetArrayBufferData(buffer, nogc));
CHECK_EQUAL(*bufdata, 1u);
CHECK_EQUAL(*reinterpret_cast<uint8_t*>(data), 1u);
CHECK_EQUAL(*reinterpret_cast<uint8_t*>(JS_GetArrayBufferData(buffer, nogc)), 1u);
CHECK_EQUAL(*reinterpret_cast<uint8_t*>(data), 1u);
}
RootedObject shortArray(cx, CreateWithBuffer(cx, buffer, 0, elts / 2));
CHECK_EQUAL(JS_GetTypedArrayLength(shortArray), elts / 2);
@ -137,7 +146,12 @@ TestArrayFromBuffer(JSContext *cx)
CHECK_SAME(v, v2);
CHECK(JS_GetElement(cx, shortArray, 0, &v2));
CHECK_SAME(v, v2);
CHECK_EQUAL(long(v.toInt32()), long(reinterpret_cast<Element*>(data)[0]));
{
JS::AutoCheckCannotGC nogc;
Element *data;
CHECK(data = GetData(array, nogc));
CHECK_EQUAL(long(v.toInt32()), long(reinterpret_cast<Element*>(data)[0]));
}
v = INT_TO_JSVAL(40);
JS_SetElement(cx, array, elts / 2, v);
@ -145,7 +159,12 @@ TestArrayFromBuffer(JSContext *cx)
CHECK_SAME(v, v2);
CHECK(JS_GetElement(cx, ofsArray, 0, &v2));
CHECK_SAME(v, v2);
CHECK_EQUAL(long(v.toInt32()), long(reinterpret_cast<Element*>(data)[elts / 2]));
{
JS::AutoCheckCannotGC nogc;
Element *data;
CHECK(data = GetData(array, nogc));
CHECK_EQUAL(long(v.toInt32()), long(reinterpret_cast<Element*>(data)[elts / 2]));
}
v = INT_TO_JSVAL(41);
JS_SetElement(cx, array, elts - 1, v);
@ -153,7 +172,12 @@ TestArrayFromBuffer(JSContext *cx)
CHECK_SAME(v, v2);
CHECK(JS_GetElement(cx, ofsArray, elts / 2 - 1, &v2));
CHECK_SAME(v, v2);
CHECK_EQUAL(long(v.toInt32()), long(reinterpret_cast<Element*>(data)[elts - 1]));
{
JS::AutoCheckCannotGC nogc;
Element *data;
CHECK(data = GetData(array, nogc));
CHECK_EQUAL(long(v.toInt32()), long(reinterpret_cast<Element*>(data)[elts - 1]));
}
JS::RootedObject copy(cx, CreateFromArray(cx, array));
CHECK(JS_GetElement(cx, array, 0, &v));

View File

@ -1952,39 +1952,32 @@ JS_GetArrayBufferViewByteLength(JSObject *obj);
*/
extern JS_FRIEND_API(uint8_t *)
JS_GetArrayBufferData(JSObject *obj);
JS_GetArrayBufferData(JSObject *obj, const JS::AutoCheckCannotGC&);
extern JS_FRIEND_API(int8_t *)
JS_GetInt8ArrayData(JSObject *obj);
JS_GetInt8ArrayData(JSObject *obj, const JS::AutoCheckCannotGC&);
extern JS_FRIEND_API(uint8_t *)
JS_GetUint8ArrayData(JSObject *obj);
JS_GetUint8ArrayData(JSObject *obj, const JS::AutoCheckCannotGC&);
extern JS_FRIEND_API(uint8_t *)
JS_GetUint8ClampedArrayData(JSObject *obj);
JS_GetUint8ClampedArrayData(JSObject *obj, const JS::AutoCheckCannotGC&);
extern JS_FRIEND_API(int16_t *)
JS_GetInt16ArrayData(JSObject *obj);
JS_GetInt16ArrayData(JSObject *obj, const JS::AutoCheckCannotGC&);
extern JS_FRIEND_API(uint16_t *)
JS_GetUint16ArrayData(JSObject *obj);
JS_GetUint16ArrayData(JSObject *obj, const JS::AutoCheckCannotGC&);
extern JS_FRIEND_API(int32_t *)
JS_GetInt32ArrayData(JSObject *obj);
JS_GetInt32ArrayData(JSObject *obj, const JS::AutoCheckCannotGC&);
extern JS_FRIEND_API(uint32_t *)
JS_GetUint32ArrayData(JSObject *obj);
JS_GetUint32ArrayData(JSObject *obj, const JS::AutoCheckCannotGC&);
extern JS_FRIEND_API(float *)
JS_GetFloat32ArrayData(JSObject *obj);
JS_GetFloat32ArrayData(JSObject *obj, const JS::AutoCheckCannotGC&);
extern JS_FRIEND_API(double *)
JS_GetFloat64ArrayData(JSObject *obj);
/*
* Stable versions of the above functions where the buffer remains valid as long
* as the object is live.
*/
extern JS_FRIEND_API(uint8_t *)
JS_GetStableArrayBufferData(JSContext *cx, JS::HandleObject obj);
JS_GetFloat64ArrayData(JSObject *obj, const JS::AutoCheckCannotGC&);
/*
* Same as above, but for any kind of ArrayBufferView. Prefer the type-specific
* versions when possible.
*/
extern JS_FRIEND_API(void *)
JS_GetArrayBufferViewData(JSObject *obj);
JS_GetArrayBufferViewData(JSObject *obj, const JS::AutoCheckCannotGC&);
/*
* Return the ArrayBuffer underlying an ArrayBufferView. If the buffer has been
@ -2057,7 +2050,7 @@ JS_GetDataViewByteLength(JSObject *obj);
* unable to assert when unwrapping should be disallowed.
*/
JS_FRIEND_API(void *)
JS_GetDataViewData(JSObject *obj);
JS_GetDataViewData(JSObject *obj, const JS::AutoCheckCannotGC&);
namespace js {

View File

@ -1066,11 +1066,11 @@ static bool
CacheEntry_setBytecode(JSContext *cx, HandleObject cache, uint8_t *buffer, uint32_t length)
{
MOZ_ASSERT(CacheEntry_isCacheEntry(cache));
ArrayBufferObject::BufferContents contents =
ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN_BUFFER>(buffer);
Rooted<ArrayBufferObject*> arrayBuffer(cx, ArrayBufferObject::create(cx, length, contents));
if (!arrayBuffer || !ArrayBufferObject::ensureNonInline(cx, arrayBuffer))
if (!arrayBuffer)
return false;
SetReservedSlot(cache, CacheEntry_BYTECODE, OBJECT_TO_JSVAL(arrayBuffer));

View File

@ -377,8 +377,13 @@ ArrayBufferObject::prepareForAsmJSNoSignals(JSContext *cx, Handle<ArrayBufferObj
if (buffer->isAsmJSArrayBuffer())
return true;
if (!ensureNonInline(cx, buffer))
return false;
if (!buffer->ownsData()) {
BufferContents contents = AllocateArrayBufferContents(cx, buffer->byteLength());
if (!contents)
return false;
memcpy(contents.data(), buffer->dataPointer(), buffer->byteLength());
buffer->changeContents(cx, contents);
}
buffer->setIsAsmJSArrayBuffer();
return true;
@ -697,20 +702,6 @@ ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp
return CallNonGenericMethod<IsArrayBuffer, createDataViewForThisImpl>(cx, args);
}
/* static */ bool
ArrayBufferObject::ensureNonInline(JSContext *cx, Handle<ArrayBufferObject*> buffer)
{
if (!buffer->ownsData()) {
BufferContents contents = AllocateArrayBufferContents(cx, buffer->byteLength());
if (!contents)
return false;
memcpy(contents.data(), buffer->dataPointer(), buffer->byteLength());
buffer->changeContents(cx, contents);
}
return true;
}
/* static */ ArrayBufferObject::BufferContents
ArrayBufferObject::stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffer,
bool hasStealableContents)
@ -1057,7 +1048,7 @@ JS_GetArrayBufferByteLength(JSObject *obj)
}
JS_FRIEND_API(uint8_t *)
JS_GetArrayBufferData(JSObject *obj)
JS_GetArrayBufferData(JSObject *obj, const JS::AutoCheckCannotGC&)
{
obj = CheckedUnwrap(obj);
if (!obj)
@ -1065,20 +1056,6 @@ JS_GetArrayBufferData(JSObject *obj)
return AsArrayBuffer(obj).dataPointer();
}
JS_FRIEND_API(uint8_t *)
JS_GetStableArrayBufferData(JSContext *cx, HandleObject objArg)
{
JSObject *obj = CheckedUnwrap(objArg);
if (!obj)
return nullptr;
Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(obj));
if (!ArrayBufferObject::ensureNonInline(cx, buffer))
return nullptr;
return buffer->dataPointer();
}
JS_FRIEND_API(bool)
JS_NeuterArrayBuffer(JSContext *cx, HandleObject obj,
NeuterDataDisposition changeData)
@ -1219,7 +1196,7 @@ JS_IsMappedArrayBufferObject(JSObject *obj)
}
JS_FRIEND_API(void *)
JS_GetArrayBufferViewData(JSObject *obj)
JS_GetArrayBufferViewData(JSObject *obj, const JS::AutoCheckCannotGC&)
{
obj = CheckedUnwrap(obj);
if (!obj)

View File

@ -2151,7 +2151,7 @@ JS_GetArrayBufferViewType(JSObject *obj)
}
JS_FRIEND_API(int8_t *)
JS_GetInt8ArrayData(JSObject *obj)
JS_GetInt8ArrayData(JSObject *obj, const JS::AutoCheckCannotGC&)
{
obj = CheckedUnwrap(obj);
if (!obj)
@ -2162,7 +2162,7 @@ JS_GetInt8ArrayData(JSObject *obj)
}
JS_FRIEND_API(uint8_t *)
JS_GetUint8ArrayData(JSObject *obj)
JS_GetUint8ArrayData(JSObject *obj, const JS::AutoCheckCannotGC&)
{
obj = CheckedUnwrap(obj);
if (!obj)
@ -2173,7 +2173,7 @@ JS_GetUint8ArrayData(JSObject *obj)
}
JS_FRIEND_API(uint8_t *)
JS_GetUint8ClampedArrayData(JSObject *obj)
JS_GetUint8ClampedArrayData(JSObject *obj, const JS::AutoCheckCannotGC&)
{
obj = CheckedUnwrap(obj);
if (!obj)
@ -2184,7 +2184,7 @@ JS_GetUint8ClampedArrayData(JSObject *obj)
}
JS_FRIEND_API(int16_t *)
JS_GetInt16ArrayData(JSObject *obj)
JS_GetInt16ArrayData(JSObject *obj, const JS::AutoCheckCannotGC&)
{
obj = CheckedUnwrap(obj);
if (!obj)
@ -2195,7 +2195,7 @@ JS_GetInt16ArrayData(JSObject *obj)
}
JS_FRIEND_API(uint16_t *)
JS_GetUint16ArrayData(JSObject *obj)
JS_GetUint16ArrayData(JSObject *obj, const JS::AutoCheckCannotGC&)
{
obj = CheckedUnwrap(obj);
if (!obj)
@ -2206,7 +2206,7 @@ JS_GetUint16ArrayData(JSObject *obj)
}
JS_FRIEND_API(int32_t *)
JS_GetInt32ArrayData(JSObject *obj)
JS_GetInt32ArrayData(JSObject *obj, const JS::AutoCheckCannotGC&)
{
obj = CheckedUnwrap(obj);
if (!obj)
@ -2217,7 +2217,7 @@ JS_GetInt32ArrayData(JSObject *obj)
}
JS_FRIEND_API(uint32_t *)
JS_GetUint32ArrayData(JSObject *obj)
JS_GetUint32ArrayData(JSObject *obj, const JS::AutoCheckCannotGC&)
{
obj = CheckedUnwrap(obj);
if (!obj)
@ -2228,7 +2228,7 @@ JS_GetUint32ArrayData(JSObject *obj)
}
JS_FRIEND_API(float *)
JS_GetFloat32ArrayData(JSObject *obj)
JS_GetFloat32ArrayData(JSObject *obj, const JS::AutoCheckCannotGC&)
{
obj = CheckedUnwrap(obj);
if (!obj)
@ -2239,7 +2239,7 @@ JS_GetFloat32ArrayData(JSObject *obj)
}
JS_FRIEND_API(double *)
JS_GetFloat64ArrayData(JSObject *obj)
JS_GetFloat64ArrayData(JSObject *obj, const JS::AutoCheckCannotGC&)
{
obj = CheckedUnwrap(obj);
if (!obj)
@ -2266,7 +2266,7 @@ JS_GetDataViewByteOffset(JSObject *obj)
}
JS_FRIEND_API(void *)
JS_GetDataViewData(JSObject *obj)
JS_GetDataViewData(JSObject *obj, const JS::AutoCheckCannotGC&)
{
obj = CheckedUnwrap(obj);
if (!obj)

View File

@ -1366,7 +1366,8 @@ CheckTargetAndPopulate(const nsXPTType& type,
return false;
}
memcpy(*output, JS_GetArrayBufferViewData(tArr), byteSize);
JS::AutoCheckCannotGC nogc;
memcpy(*output, JS_GetArrayBufferViewData(tArr, nogc), byteSize);
return true;
}

View File

@ -12,8 +12,7 @@
NS_IMPL_ISUPPORTS(ArrayBufferInputStream, nsIArrayBufferInputStream, nsIInputStream);
ArrayBufferInputStream::ArrayBufferInputStream()
: mBuffer(nullptr)
, mBufferLength(0)
: mBufferLength(0)
, mOffset(0)
, mPos(0)
, mClosed(false)
@ -34,15 +33,11 @@ ArrayBufferInputStream::SetData(JS::Handle<JS::Value> aBuffer,
return NS_ERROR_FAILURE;
}
mArrayBuffer.emplace(aCx, aBuffer);
mArrayBuffer.emplace(aCx, arrayBuffer);
uint32_t buflen = JS_GetArrayBufferByteLength(arrayBuffer);
mOffset = std::min(buflen, aByteOffset);
mBufferLength = std::min(buflen - mOffset, aLength);
mBuffer = JS_GetStableArrayBufferData(aCx, arrayBuffer);
if (!mBuffer) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
@ -59,7 +54,8 @@ ArrayBufferInputStream::Available(uint64_t* aCount)
if (mClosed) {
return NS_BASE_STREAM_CLOSED;
}
*aCount = mBufferLength - mPos;
uint32_t buflen = JS_GetArrayBufferByteLength(mArrayBuffer->get());
*aCount = buflen ? buflen - mPos : 0;
return NS_OK;
}
@ -80,32 +76,46 @@ ArrayBufferInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure,
return NS_BASE_STREAM_CLOSED;
}
uint32_t remaining = mBufferLength - mPos;
if (mArrayBuffer) {
JSObject* buf = &mArrayBuffer->get().toObject();
uint32_t byteLength = JS_GetArrayBufferByteLength(buf);
if (byteLength == 0 && remaining != 0) {
MOZ_ASSERT(mArrayBuffer || (mPos == mBufferLength), "stream inited incorrectly");
*result = 0;
while (mPos < mBufferLength) {
uint32_t remaining = mBufferLength - mPos;
MOZ_ASSERT(mArrayBuffer);
uint32_t byteLength = JS_GetArrayBufferByteLength(mArrayBuffer->get());
if (byteLength == 0) {
mClosed = true;
return NS_BASE_STREAM_CLOSED;
}
} else {
MOZ_ASSERT(remaining == 0, "stream inited incorrectly");
}
if (!remaining) {
*result = 0;
return NS_OK;
}
char buffer[8192];
uint32_t count = std::min(std::min(aCount, remaining), uint32_t(mozilla::ArrayLength(buffer)));
if (count == 0) {
break;
}
if (aCount > remaining) {
aCount = remaining;
}
nsresult rv = writer(this, closure, (char*)(mBuffer + mOffset) + mPos,
0, aCount, result);
if (NS_SUCCEEDED(rv)) {
NS_ASSERTION(*result <= aCount,
// It is just barely possible that writer() will detach the ArrayBuffer's
// data, setting its length to zero. Or move the data to a different memory
// area. (This would only happen in a subclass that passed something other
// than NS_CopySegmentToBuffer as 'writer'). So copy the data out into a
// holding area before passing it to writer().
{
JS::AutoCheckCannotGC nogc;
char* src = (char*) JS_GetArrayBufferData(mArrayBuffer->get(), nogc) + mOffset + mPos;
memcpy(buffer, src, count);
}
uint32_t written;
nsresult rv = writer(this, closure, buffer, 0, count, &written);
if (NS_FAILED(rv)) {
// InputStreams do not propagate errors to caller.
return NS_OK;
}
NS_ASSERTION(written <= count,
"writer should not write more than we asked it to write");
mPos += *result;
mPos += written;
*result += written;
aCount -= written;
}
return NS_OK;

View File

@ -28,8 +28,7 @@ public:
private:
virtual ~ArrayBufferInputStream() {}
mozilla::Maybe<JS::PersistentRooted<JS::Value> > mArrayBuffer;
uint8_t* mBuffer; // start of actual buffer
mozilla::Maybe<JS::PersistentRooted<JSObject*> > mArrayBuffer;
uint32_t mBufferLength; // length of slice
uint32_t mOffset; // permanent offset from start of actual buffer
uint32_t mPos; // offset from start of slice

View File

@ -841,20 +841,15 @@ nsBinaryInputStream::ReadArrayBuffer(uint32_t aLength,
return NS_ERROR_FAILURE;
}
char* data = reinterpret_cast<char*>(JS_GetStableArrayBufferData(aCx, buffer));
if (!data) {
return NS_ERROR_FAILURE;
}
uint32_t bufSize = std::min<uint32_t>(aLength, 4096);
UniquePtr<char[]> buf = MakeUnique<char[]>(bufSize);
uint32_t remaining = aLength;
uint32_t pos = 0;
*aReadLength = 0;
do {
// Read data into temporary buffer.
uint32_t bytesRead;
uint32_t amount = std::min(remaining, bufSize);
uint32_t amount = std::min(aLength - pos, bufSize);
nsresult rv = Read(buf.get(), amount, &bytesRead);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
@ -866,16 +861,22 @@ nsBinaryInputStream::ReadArrayBuffer(uint32_t aLength,
}
// Copy data into actual buffer.
JS::AutoCheckCannotGC nogc;
if (bufferLength != JS_GetArrayBufferByteLength(buffer)) {
return NS_ERROR_FAILURE;
}
*aReadLength += bytesRead;
PodCopy(data, buf.get(), bytesRead);
char* data = reinterpret_cast<char*>(JS_GetArrayBufferData(buffer, nogc));
if (!data) {
return NS_ERROR_FAILURE;
}
remaining -= bytesRead;
data += bytesRead;
} while (remaining > 0);
*aReadLength += bytesRead;
PodCopy(data + pos, buf.get(), bytesRead);
pos += bytesRead;
} while (pos < aLength);
return NS_OK;
}