From 7b80880b2ef83b8e16ec1404e7a1643e3cddd263 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Tue, 7 Oct 2014 10:44:07 -0700 Subject: [PATCH] Bug 1061288 - Make it harder for ArrayBuffer data pointers to be held across invalidations. r=smaug,terrence,jdm,roc,khuey --- content/base/src/nsContentUtils.cpp | 3 +- content/base/src/nsDOMFileReader.cpp | 42 +++++++----- content/base/src/nsDOMFileReader.h | 10 +-- content/media/webaudio/AudioBuffer.cpp | 12 ++-- .../media/webspeech/synth/nsSpeechTask.cpp | 40 +++++++---- content/media/webspeech/synth/nsSpeechTask.h | 2 +- dom/bindings/TypedArray.h | 5 +- dom/canvas/CanvasRenderingContext2D.cpp | 14 ++-- dom/fmradio/FMRadio.cpp | 3 +- dom/network/TCPSocketChild.cpp | 33 ++++++---- dom/network/TCPSocketParent.cpp | 28 +++++--- dom/workers/FileReaderSync.cpp | 26 ++++---- ipc/ril/Ril.cpp | 27 +++++--- js/src/ctypes/CTypes.cpp | 41 ++++++++---- js/src/devtools/rootAnalysis/annotations.js | 1 + js/src/jsapi-tests/testArrayBuffer.cpp | 24 ++++--- js/src/jsapi-tests/testMappedArrayBuffer.cpp | 4 +- js/src/jsapi-tests/testTypedArrays.cpp | 62 +++++++++++------ js/src/jsfriendapi.h | 31 ++++----- js/src/shell/js.cpp | 4 +- js/src/vm/ArrayBufferObject.cpp | 41 +++--------- js/src/vm/TypedArrayObject.cpp | 20 +++--- js/xpconnect/src/XPCConvert.cpp | 3 +- netwerk/base/src/ArrayBufferInputStream.cpp | 66 +++++++++++-------- netwerk/base/src/ArrayBufferInputStream.h | 3 +- xpcom/io/nsBinaryStream.cpp | 25 +++---- 26 files changed, 327 insertions(+), 243 deletions(-) diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 0cca795286d..45c70b427c0 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -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; diff --git a/content/base/src/nsDOMFileReader.cpp b/content/base/src/nsDOMFileReader.cpp index 7afab40039d..b23c2b46935 100644 --- a/content/base/src/nsDOMFileReader.cpp +++ b/content/base/src/nsDOMFileReader.cpp @@ -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(mTotal); + if (!mFileData) { + NS_WARNING("Preallocation failed for ReadFileData"); + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); } } } diff --git a/content/base/src/nsDOMFileReader.h b/content/base/src/nsDOMFileReader.h index 060661a0919..33af0c8caf5 100644 --- a/content/base/src/nsDOMFileReader.h +++ b/content/base/src/nsDOMFileReader.h @@ -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, diff --git a/content/media/webaudio/AudioBuffer.cpp b/content/media/webaudio/AudioBuffer.cpp index fc2c90b2f49..aad4879ed7c 100644 --- a/content/media/webaudio/AudioBuffer.cpp +++ b/content/media/webaudio/AudioBuffer.cpp @@ -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 diff --git a/content/media/webspeech/synth/nsSpeechTask.cpp b/content/media/webspeech/synth/nsSpeechTask.cpp index 788f3209be5..457b94a265b 100644 --- a/content/media/webspeech/synth/nsSpeechTask.cpp +++ b/content/media/webspeech/synth/nsSpeechTask.cpp @@ -162,6 +162,20 @@ nsSpeechTask::Setup(nsISpeechTaskCallback* aCallback, return NS_OK; } +static nsRefPtr +makeSamples(int16_t* aData, uint32_t aDataLen) +{ + nsRefPtr samples = + SharedBuffer::Create(aDataLen * sizeof(int16_t)); + int16_t* frames = static_cast(samples->Data()); + + for (uint32_t i = 0; i < aDataLen; i++) { + frames[i] = aData[i]; + } + + return samples; +} + NS_IMETHODIMP nsSpeechTask::SendAudio(JS::Handle aData, JS::Handle aLandmarks, JSContext* aCx) @@ -194,8 +208,13 @@ nsSpeechTask::SendAudio(JS::Handle aData, JS::Handle aLand return NS_ERROR_DOM_TYPE_MISMATCH_ERR; } - SendAudioImpl(JS_GetInt16ArrayData(tsrc), - JS_GetTypedArrayLength(tsrc)); + uint32_t dataLen = JS_GetTypedArrayLength(tsrc); + nsRefPtr 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 samples = makeSamples(aData, aDataLen); + SendAudioImpl(samples, aDataLen); return NS_OK; } void -nsSpeechTask::SendAudioImpl(int16_t* aData, uint32_t aDataLen) +nsSpeechTask::SendAudioImpl(nsRefPtr& aSamples, uint32_t aDataLen) { if (aDataLen == 0) { mStream->EndAllTrackAndFinish(); return; } - nsRefPtr samples = - SharedBuffer::Create(aDataLen * sizeof(int16_t)); - int16_t* frames = static_cast(samples->Data()); - - for (uint32_t i = 0; i < aDataLen; i++) { - frames[i] = aData[i]; - } - AudioSegment segment; nsAutoTArray channelData; - channelData.AppendElement(frames); - segment.AppendFrames(samples.forget(), channelData, aDataLen); + channelData.AppendElement(static_cast(aSamples->Data())); + segment.AppendFrames(aSamples.forget(), channelData, aDataLen); mStream->AppendToTrack(1, &segment); mStream->AdvanceKnownTracksTime(STREAM_TIME_MAX); } diff --git a/content/media/webspeech/synth/nsSpeechTask.h b/content/media/webspeech/synth/nsSpeechTask.h index cf7966e7c11..a64c6009c1a 100644 --- a/content/media/webspeech/synth/nsSpeechTask.h +++ b/content/media/webspeech/synth/nsSpeechTask.h @@ -74,7 +74,7 @@ protected: private: void End(); - void SendAudioImpl(int16_t* aData, uint32_t aDataLen); + void SendAudioImpl(nsRefPtr& aSamples, uint32_t aDataLen); nsRefPtr mStream; diff --git a/dom/bindings/TypedArray.h b/dom/bindings/TypedArray.h index 1d062383646..770ba296f7a 100644 --- a/dom/bindings/TypedArray.h +++ b/dom/bindings/TypedArray.h @@ -139,7 +139,7 @@ private: template struct TypedArray : public TypedArray_base { @@ -181,7 +181,8 @@ private: return nullptr; } if (data) { - T* buf = static_cast(GetData(obj)); + JS::AutoCheckCannotGC nogc; + T* buf = static_cast(GetData(obj, nogc)); memcpy(buf, data, length*sizeof(T)); } return obj; diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index dc9b119b477..cc3f9e3562c 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -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 diff --git a/dom/fmradio/FMRadio.cpp b/dom/fmradio/FMRadio.cpp index d172c29228f..32eb01a37f5 100644 --- a/dom/fmradio/FMRadio.cpp +++ b/dom/fmradio/FMRadio.cpp @@ -343,7 +343,8 @@ FMRadio::GetRdsgroup(JSContext* cx, JS::MutableHandle 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; diff --git a/dom/network/TCPSocketChild.cpp b/dom/network/TCPSocketChild.cpp index 377e84129d9..0d33ee8b555 100644 --- a/dom/network/TCPSocketChild.cpp +++ b/dom/network/TCPSocketChild.cpp @@ -5,6 +5,7 @@ #include #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 aObj, mozilla::AutoSafeJSContext cx; JSAutoCompartment ac(cx, aObj); - JS::Rooted obj(cx, JS_NewArrayBuffer(cx, aBuffer.Length())); - if (!obj) - return false; - uint8_t* data = JS_GetArrayBufferData(obj); + mozilla::UniquePtr data(js_pod_malloc(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 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 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 arr; arr.SwapElements(fallibleArr); diff --git a/dom/network/TCPSocketParent.cpp b/dom/network/TCPSocketParent.cpp index ba3c3987c38..35122cfe03c 100644 --- a/dom/network/TCPSocketParent.cpp +++ b/dom/network/TCPSocketParent.cpp @@ -285,17 +285,27 @@ TCPSocketParent::SendEvent(const nsAString& aType, JS::Handle aDataVa } else if (aDataVal.isObject()) { JS::Rooted 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 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 arr; arr.SwapElements(fallibleArr); data = SendableData(arr); diff --git a/dom/workers/FileReaderSync.cpp b/dom/workers/FileReaderSync.cpp index 0e6f195468e..600c4a07a9b 100644 --- a/dom/workers/FileReaderSync.cpp +++ b/dom/workers/FileReaderSync.cpp @@ -61,17 +61,8 @@ FileReaderSync::ReadAsArrayBuffer(JSContext* aCx, return; } - JS::Rooted 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 bufferData(js_pod_malloc(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 diff --git a/ipc/ril/Ril.cpp b/ipc/ril/Ril.cpp index 473f160f751..bd602df558e 100644 --- a/ipc/ril/Ril.cpp +++ b/ipc/ril/Ril.cpp @@ -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 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 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); diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 5a61f9a8569..bdcdb28e89e 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -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(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(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(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(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 diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index cf5a7eca1ba..79b29165688 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -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) diff --git a/js/src/jsapi-tests/testArrayBuffer.cpp b/js/src/jsapi-tests/testArrayBuffer.cpp index 20c9c3f19b2..13439e48f59 100644 --- a/js/src/jsapi-tests/testArrayBuffer.cpp +++ b/js/src/jsapi-tests/testArrayBuffer.cpp @@ -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(data) = MAGIC_VALUE_2; + { + JS::AutoCheckCannotGC nogc; + uint8_t *data = JS_GetArrayBufferData(obj, nogc); + CHECK(data != nullptr); + *reinterpret_cast(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(data), MAGIC_VALUE_2); + { + JS::AutoCheckCannotGC nogc; + uint8_t *data = JS_GetArrayBufferData(dst, nogc); + CHECK(data != nullptr); + CHECK_EQUAL(*reinterpret_cast(data), MAGIC_VALUE_2); + } CHECK(JS_GetElement(cx, dstview, 0, &v)); CHECK_SAME(v, INT_TO_JSVAL(MAGIC_VALUE_2)); } diff --git a/js/src/jsapi-tests/testMappedArrayBuffer.cpp b/js/src/jsapi-tests/testMappedArrayBuffer.cpp index 4f211f6c3a5..513848ad9eb 100644 --- a/js/src/jsapi-tests/testMappedArrayBuffer.cpp +++ b/js/src/jsapi-tests/testMappedArrayBuffer.cpp @@ -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(JS_GetArrayBufferData(obj)); + const char *data = reinterpret_cast(JS_GetArrayBufferData(obj, nogc)); CHECK(data); CHECK(memcmp(data, test_data + offset, length) == 0); diff --git a/js/src/jsapi-tests/testTypedArrays.cpp b/js/src/jsapi-tests/testTypedArrays.cpp index 5f96facbc3d..b92c23ac189 100644 --- a/js/src/jsapi-tests/testTypedArrays.cpp +++ b/js/src/jsapi-tests/testTypedArrays.cpp @@ -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(cx) && @@ -55,7 +58,7 @@ BEGIN_TEST(testTypedArrays) template + 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 + 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(data), 1u); + CHECK_EQUAL(*reinterpret_cast(JS_GetArrayBufferData(buffer, nogc)), 1u); + CHECK_EQUAL(*reinterpret_cast(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(data)[0])); + { + JS::AutoCheckCannotGC nogc; + Element *data; + CHECK(data = GetData(array, nogc)); + CHECK_EQUAL(long(v.toInt32()), long(reinterpret_cast(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(data)[elts / 2])); + { + JS::AutoCheckCannotGC nogc; + Element *data; + CHECK(data = GetData(array, nogc)); + CHECK_EQUAL(long(v.toInt32()), long(reinterpret_cast(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(data)[elts - 1])); + { + JS::AutoCheckCannotGC nogc; + Element *data; + CHECK(data = GetData(array, nogc)); + CHECK_EQUAL(long(v.toInt32()), long(reinterpret_cast(data)[elts - 1])); + } JS::RootedObject copy(cx, CreateFromArray(cx, array)); CHECK(JS_GetElement(cx, array, 0, &v)); diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index bef1e716908..086cf1309ec 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -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 { diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 646813d9239..cc109b1fe9e 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -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(buffer); Rooted 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)); diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index 778beb4bb88..4f9bd901831 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -377,8 +377,13 @@ ArrayBufferObject::prepareForAsmJSNoSignals(JSContext *cx, HandleisAsmJSArrayBuffer()) 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(cx, args); } -/* static */ bool -ArrayBufferObject::ensureNonInline(JSContext *cx, Handle 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 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 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) diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 68b5f71c6e8..351a5fe9ce2 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -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) diff --git a/js/xpconnect/src/XPCConvert.cpp b/js/xpconnect/src/XPCConvert.cpp index d06c2f954ae..40379f9b199 100644 --- a/js/xpconnect/src/XPCConvert.cpp +++ b/js/xpconnect/src/XPCConvert.cpp @@ -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; } diff --git a/netwerk/base/src/ArrayBufferInputStream.cpp b/netwerk/base/src/ArrayBufferInputStream.cpp index e08cf10f8bb..3921b3cf75a 100644 --- a/netwerk/base/src/ArrayBufferInputStream.cpp +++ b/netwerk/base/src/ArrayBufferInputStream.cpp @@ -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 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; diff --git a/netwerk/base/src/ArrayBufferInputStream.h b/netwerk/base/src/ArrayBufferInputStream.h index c2b3a8c14e8..9abe6970262 100644 --- a/netwerk/base/src/ArrayBufferInputStream.h +++ b/netwerk/base/src/ArrayBufferInputStream.h @@ -28,8 +28,7 @@ public: private: virtual ~ArrayBufferInputStream() {} - mozilla::Maybe > mArrayBuffer; - uint8_t* mBuffer; // start of actual buffer + mozilla::Maybe > 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 diff --git a/xpcom/io/nsBinaryStream.cpp b/xpcom/io/nsBinaryStream.cpp index 372bec6ba24..13f7f8c15a1 100644 --- a/xpcom/io/nsBinaryStream.cpp +++ b/xpcom/io/nsBinaryStream.cpp @@ -841,20 +841,15 @@ nsBinaryInputStream::ReadArrayBuffer(uint32_t aLength, return NS_ERROR_FAILURE; } - char* data = reinterpret_cast(JS_GetStableArrayBufferData(aCx, buffer)); - if (!data) { - return NS_ERROR_FAILURE; - } - uint32_t bufSize = std::min(aLength, 4096); UniquePtr buf = MakeUnique(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(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; }