commit 9aeffb05a552f1beb1ff37adc88df0640ffa8ca3 Author: Jeff Walden jwalden@mit.edu Date: Mon Jun 2 11:25:43 2014 -0700
Bug 999651, bug 995679, bug 1009952, bug 1011007, bug 991981. r=sfink, r=shu, r=jandem, r=jdm, r=luke, r=bbouvier, r=nmatsakis, r=bz, r=ehsan, r=jgilbert, r=smaug, r=sicking, r=terrence, r=bholley, r=bent, r=efaust, r=jorendorff, a=lmandel
--HG-- extra : rebase_source : 2487b6e09f4caf827c3ba5e0283e3d30c74e5d42 --- CLOBBER | 2 +- content/base/src/WebSocket.cpp | 10 +- content/base/src/nsDOMDataChannel.cpp | 10 +- content/base/src/nsDOMParser.cpp | 2 + content/base/src/nsXMLHttpRequest.cpp | 11 +- content/canvas/src/CanvasRenderingContext2D.cpp | 14 +- content/canvas/src/CanvasRenderingContext2D.h | 3 +- content/canvas/src/WebGLContext.h | 15 ++ content/canvas/src/WebGLContextGL.cpp | 43 ++++- content/html/content/public/HTMLAudioElement.h | 1 + content/media/webaudio/AnalyserNode.cpp | 6 + content/media/webaudio/AudioContext.cpp | 17 +- content/media/webaudio/AudioParam.h | 1 + content/media/webaudio/BiquadFilterNode.cpp | 4 + content/media/webaudio/WaveShaperNode.cpp | 2 + dom/bindings/TypedArray.h | 88 +++++++---- dom/encoding/TextDecoder.h | 1 + dom/workers/TextDecoder.h | 1 + js/src/builtin/TestingFunctions.cpp | 50 ++++++ js/src/jit/IonBuilder.cpp | 92 +---------- js/src/jit/MIR.h | 10 +- js/src/js.msg | 4 +- js/src/jsfriendapi.h | 111 +++++++++++++ js/src/jstypedarray.cpp | 190 +++++++++++++++++------ js/src/jstypedarray.h | 16 +- js/src/jstypedarrayinlines.h | 8 + netwerk/base/src/ArrayBufferInputStream.cpp | 11 ++ xpcom/io/nsBinaryStream.cpp | 56 +++++-- 28 files changed, 561 insertions(+), 218 deletions(-)
diff --git a/CLOBBER b/CLOBBER index 469d72c..550e8c1 100644 --- a/CLOBBER +++ b/CLOBBER @@ -18,4 +18,4 @@ # Modifying this file will now automatically clobber the buildbot machines \o/ #
-Bug 902908 renamed js/src/ion to js/src/jit and required a clobber \ No newline at end of file +Bug 999651 et al. require a clobber for some unknown reason diff --git a/content/base/src/WebSocket.cpp b/content/base/src/WebSocket.cpp index aba7007..2953aa3 100644 --- a/content/base/src/WebSocket.cpp +++ b/content/base/src/WebSocket.cpp @@ -1221,7 +1221,10 @@ WebSocket::Send(ArrayBuffer& aData, { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
- MOZ_ASSERT(sizeof(*aData.Data()) == 1); + aData.ComputeLengthAndData(); + + static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required"); + uint32_t len = aData.Length(); char* data = reinterpret_cast<char*>(aData.Data());
@@ -1235,7 +1238,10 @@ WebSocket::Send(ArrayBufferView& aData, { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
- MOZ_ASSERT(sizeof(*aData.Data()) == 1); + aData.ComputeLengthAndData(); + + static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required"); + uint32_t len = aData.Length(); char* data = reinterpret_cast<char*>(aData.Data());
diff --git a/content/base/src/nsDOMDataChannel.cpp b/content/base/src/nsDOMDataChannel.cpp index 3e038de..a7f4d93 100644 --- a/content/base/src/nsDOMDataChannel.cpp +++ b/content/base/src/nsDOMDataChannel.cpp @@ -292,7 +292,10 @@ nsDOMDataChannel::Send(ArrayBuffer& aData, ErrorResult& aRv) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
- MOZ_ASSERT(sizeof(*aData.Data()) == 1); + aData.ComputeLengthAndData(); + + static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required"); + uint32_t len = aData.Length(); char* data = reinterpret_cast<char*>(aData.Data());
@@ -305,7 +308,10 @@ nsDOMDataChannel::Send(ArrayBufferView& aData, ErrorResult& aRv) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
- MOZ_ASSERT(sizeof(*aData.Data()) == 1); + aData.ComputeLengthAndData(); + + static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required"); + uint32_t len = aData.Length(); char* data = reinterpret_cast<char*>(aData.Data());
diff --git a/content/base/src/nsDOMParser.cpp b/content/base/src/nsDOMParser.cpp index 0ab06d9..c6bc60d 100644 --- a/content/base/src/nsDOMParser.cpp +++ b/content/base/src/nsDOMParser.cpp @@ -137,6 +137,8 @@ already_AddRefed<nsIDocument> nsDOMParser::ParseFromBuffer(const Uint8Array& aBuf, uint32_t aBufLen, SupportedType aType, ErrorResult& rv) { + aBuf.ComputeLengthAndData(); + if (aBufLen > aBuf.Length()) { rv.Throw(NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY); return nullptr; diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp index 22fe0c8..418edae 100644 --- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -2440,6 +2440,7 @@ GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult, uint64_t* aContentLe JS::Rooted<JSObject*> obj(cx, JSVAL_TO_OBJECT(realVal)); if (JS_IsArrayBufferObject(obj)) { ArrayBuffer buf(obj); + buf.ComputeLengthAndData(); return GetRequestBody(buf.Data(), buf.Length(), aResult, aContentLength, aContentType, aCharset); } @@ -2483,14 +2484,16 @@ nsXMLHttpRequest::GetRequestBody(nsIVariant* aVariant, switch (body.GetType()) { case nsXMLHttpRequest::RequestBody::ArrayBuffer: { - return ::GetRequestBody(value.mArrayBuffer->Data(), - value.mArrayBuffer->Length(), aResult, + const ArrayBuffer* buffer = value.mArrayBuffer; + buffer->ComputeLengthAndData(); + return ::GetRequestBody(buffer->Data(), buffer->Length(), aResult, aContentLength, aContentType, aCharset); } case nsXMLHttpRequest::RequestBody::ArrayBufferView: { - return ::GetRequestBody(value.mArrayBufferView->Data(), - value.mArrayBufferView->Length(), aResult, + const ArrayBufferView* view = value.mArrayBufferView; + view->ComputeLengthAndData(); + return ::GetRequestBody(view->Data(), view->Length(), aResult, aContentLength, aContentType, aCharset); } case nsXMLHttpRequest::RequestBody::Blob: diff --git a/content/canvas/src/CanvasRenderingContext2D.cpp b/content/canvas/src/CanvasRenderingContext2D.cpp index 8c67a87..ea685f1 100644 --- a/content/canvas/src/CanvasRenderingContext2D.cpp +++ b/content/canvas/src/CanvasRenderingContext2D.cpp @@ -3559,7 +3559,7 @@ CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy), imageData.Width(), imageData.Height(), - arr.Data(), arr.Length(), false, 0, 0, 0, 0); + &arr, false, 0, 0, 0, 0); }
void @@ -3573,7 +3573,7 @@ CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy), imageData.Width(), imageData.Height(), - arr.Data(), arr.Length(), true, + &arr, true, JS_DoubleToInt32(dirtyX), JS_DoubleToInt32(dirtyY), JS_DoubleToInt32(dirtyWidth), @@ -3585,7 +3585,7 @@ CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
nsresult CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h, - unsigned char *aData, uint32_t aDataLen, + dom::Uint8ClampedArray* aArray, bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY, int32_t dirtyWidth, int32_t dirtyHeight) { @@ -3638,8 +3638,12 @@ CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w return NS_OK; }
+ aArray->ComputeLengthAndData(); + + uint32_t dataLen = aArray->Length(); + uint32_t len = w * h * 4; - if (aDataLen != len) { + if (dataLen != len) { return NS_ERROR_DOM_SYNTAX_ERR; }
@@ -3650,7 +3654,7 @@ CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w return NS_ERROR_FAILURE; }
- uint8_t *src = aData; + uint8_t *src = aArray->Data(); uint8_t *dst = imgsurf->Data();
for (uint32_t j = 0; j < h; j++) { diff --git a/content/canvas/src/CanvasRenderingContext2D.h b/content/canvas/src/CanvasRenderingContext2D.h index d0f8fa0..0aff60d 100644 --- a/content/canvas/src/CanvasRenderingContext2D.h +++ b/content/canvas/src/CanvasRenderingContext2D.h @@ -21,6 +21,7 @@ #include "mozilla/dom/CanvasGradient.h" #include "mozilla/dom/CanvasRenderingContext2DBinding.h" #include "mozilla/dom/CanvasPattern.h" +#include "mozilla/dom/TypedArray.h" #include "mozilla/gfx/Rect.h"
class nsXULElement; @@ -443,7 +444,7 @@ protected: JSObject** aRetval);
nsresult PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h, - unsigned char *aData, uint32_t aDataLen, + dom::Uint8ClampedArray* aArray, bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY, int32_t dirtyWidth, int32_t dirtyHeight);
diff --git a/content/canvas/src/WebGLContext.h b/content/canvas/src/WebGLContext.h index e8e55db..db0c20a 100644 --- a/content/canvas/src/WebGLContext.h +++ b/content/canvas/src/WebGLContext.h @@ -585,6 +585,7 @@ public: WebGLfloat z, WebGLfloat w);
void Uniform1iv(WebGLUniformLocation* location, dom::Int32Array& arr) { + arr.ComputeLengthAndData(); Uniform1iv_base(location, arr.Length(), arr.Data()); } void Uniform1iv(WebGLUniformLocation* location, @@ -595,6 +596,7 @@ public: const WebGLint* data);
void Uniform2iv(WebGLUniformLocation* location, dom::Int32Array& arr) { + arr.ComputeLengthAndData(); Uniform2iv_base(location, arr.Length(), arr.Data()); } void Uniform2iv(WebGLUniformLocation* location, @@ -605,6 +607,7 @@ public: const WebGLint* data);
void Uniform3iv(WebGLUniformLocation* location, dom::Int32Array& arr) { + arr.ComputeLengthAndData(); Uniform3iv_base(location, arr.Length(), arr.Data()); } void Uniform3iv(WebGLUniformLocation* location, @@ -615,6 +618,7 @@ public: const WebGLint* data);
void Uniform4iv(WebGLUniformLocation* location, dom::Int32Array& arr) { + arr.ComputeLengthAndData(); Uniform4iv_base(location, arr.Length(), arr.Data()); } void Uniform4iv(WebGLUniformLocation* location, @@ -625,6 +629,7 @@ public: const WebGLint* data);
void Uniform1fv(WebGLUniformLocation* location, dom::Float32Array& arr) { + arr.ComputeLengthAndData(); Uniform1fv_base(location, arr.Length(), arr.Data()); } void Uniform1fv(WebGLUniformLocation* location, @@ -635,6 +640,7 @@ public: const WebGLfloat* data);
void Uniform2fv(WebGLUniformLocation* location, dom::Float32Array& arr) { + arr.ComputeLengthAndData(); Uniform2fv_base(location, arr.Length(), arr.Data()); } void Uniform2fv(WebGLUniformLocation* location, @@ -645,6 +651,7 @@ public: const WebGLfloat* data);
void Uniform3fv(WebGLUniformLocation* location, dom::Float32Array& arr) { + arr.ComputeLengthAndData(); Uniform3fv_base(location, arr.Length(), arr.Data()); } void Uniform3fv(WebGLUniformLocation* location, @@ -655,6 +662,7 @@ public: const WebGLfloat* data);
void Uniform4fv(WebGLUniformLocation* location, dom::Float32Array& arr) { + arr.ComputeLengthAndData(); Uniform4fv_base(location, arr.Length(), arr.Data()); } void Uniform4fv(WebGLUniformLocation* location, @@ -667,6 +675,7 @@ public: void UniformMatrix2fv(WebGLUniformLocation* location, WebGLboolean transpose, dom::Float32Array &value) { + value.ComputeLengthAndData(); UniformMatrix2fv_base(location, transpose, value.Length(), value.Data()); } void UniformMatrix2fv(WebGLUniformLocation* location, @@ -682,6 +691,7 @@ public: void UniformMatrix3fv(WebGLUniformLocation* location, WebGLboolean transpose, dom::Float32Array &value) { + value.ComputeLengthAndData(); UniformMatrix3fv_base(location, transpose, value.Length(), value.Data()); } void UniformMatrix3fv(WebGLUniformLocation* location, @@ -697,6 +707,7 @@ public: void UniformMatrix4fv(WebGLUniformLocation* location, WebGLboolean transpose, dom::Float32Array &value) { + value.ComputeLengthAndData(); UniformMatrix4fv_base(location, transpose, value.Length(), value.Data()); } void UniformMatrix4fv(WebGLUniformLocation* location, @@ -731,6 +742,7 @@ public: WebGLfloat x2, WebGLfloat x3);
void VertexAttrib1fv(WebGLuint idx, dom::Float32Array &arr) { + arr.ComputeLengthAndData(); VertexAttrib1fv_base(idx, arr.Length(), arr.Data()); } void VertexAttrib1fv(WebGLuint idx, const dom::Sequence<WebGLfloat>& arr) { @@ -740,6 +752,7 @@ public: const WebGLfloat* ptr);
void VertexAttrib2fv(WebGLuint idx, dom::Float32Array &arr) { + arr.ComputeLengthAndData(); VertexAttrib2fv_base(idx, arr.Length(), arr.Data()); } void VertexAttrib2fv(WebGLuint idx, const dom::Sequence<WebGLfloat>& arr) { @@ -749,6 +762,7 @@ public: const WebGLfloat* ptr);
void VertexAttrib3fv(WebGLuint idx, dom::Float32Array &arr) { + arr.ComputeLengthAndData(); VertexAttrib3fv_base(idx, arr.Length(), arr.Data()); } void VertexAttrib3fv(WebGLuint idx, const dom::Sequence<WebGLfloat>& arr) { @@ -758,6 +772,7 @@ public: const WebGLfloat* ptr);
void VertexAttrib4fv(WebGLuint idx, dom::Float32Array &arr) { + arr.ComputeLengthAndData(); VertexAttrib4fv_base(idx, arr.Length(), arr.Data()); } void VertexAttrib4fv(WebGLuint idx, const dom::Sequence<WebGLfloat>& arr) { diff --git a/content/canvas/src/WebGLContextGL.cpp b/content/canvas/src/WebGLContextGL.cpp index 6b4c77b..c1f943f 100644 --- a/content/canvas/src/WebGLContextGL.cpp +++ b/content/canvas/src/WebGLContextGL.cpp @@ -431,6 +431,8 @@ WebGLContext::BufferData(WebGLenum target, ArrayBuffer *data, WebGLenum usage) MakeContextCurrent(); InvalidateCachedMinInUseAttribArrayLength();
+ data->ComputeLengthAndData(); + GLenum error = CheckedBufferData(target, data->Length(), data->Data(), usage);
if (error) { @@ -469,6 +471,8 @@ WebGLContext::BufferData(WebGLenum target, ArrayBufferView& data, WebGLenum usag InvalidateCachedMinInUseAttribArrayLength(); MakeContextCurrent();
+ data.ComputeLengthAndData(); + GLenum error = CheckedBufferData(target, data.Length(), data.Data(), usage); if (error) { GenerateWarning("bufferData generated error %s", ErrorName(error)); @@ -509,6 +513,8 @@ WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr byteOffset, if (!boundBuffer) return ErrorInvalidOperation("bufferData: no buffer bound!");
+ data->ComputeLengthAndData(); + CheckedUint32 checked_neededByteLength = CheckedUint32(byteOffset) + data->Length(); if (!checked_neededByteLength.isValid()) return ErrorInvalidValue("bufferSubData: integer overflow computing the needed byte length"); @@ -547,6 +553,8 @@ WebGLContext::BufferSubData(WebGLenum target, WebGLsizeiptr byteOffset, if (!boundBuffer) return ErrorInvalidOperation("bufferSubData: no buffer bound!");
+ data.ComputeLengthAndData(); + CheckedUint32 checked_neededByteLength = CheckedUint32(byteOffset) + data.Length(); if (!checked_neededByteLength.isValid()) return ErrorInvalidValue("bufferSubData: integer overflow computing the needed byte length"); @@ -3382,7 +3390,11 @@ WebGLContext::ReadPixels(WebGLint x, WebGLint y, WebGLsizei width, if (!checked_neededByteLength.isValid()) return ErrorInvalidOperation("readPixels: integer overflow computing the needed buffer size");
- uint32_t dataByteLen = JS_GetTypedArrayByteLength(pixels->Obj()); + // Compute length and data. Don't reenter after this point, lest the + // precomputed go out of sync with the instant length/data. + pixels->ComputeLengthAndData(); + + uint32_t dataByteLen = pixels->Length(); if (checked_neededByteLength.value() > dataByteLen) return ErrorInvalidOperation("readPixels: buffer too small");
@@ -4584,6 +4596,8 @@ WebGLContext::CompressedTexImage2D(WebGLenum target, WebGLint level, WebGLenum i return; }
+ view.ComputeLengthAndData(); + uint32_t byteLength = view.Length(); if (!ValidateCompressedTextureSize(target, level, internalformat, width, height, byteLength, "compressedTexImage2D")) { return; @@ -4633,6 +4647,8 @@ WebGLContext::CompressedTexSubImage2D(WebGLenum target, WebGLint level, WebGLint return; }
+ view.ComputeLengthAndData(); + uint32_t byteLength = view.Length(); if (!ValidateCompressedTextureSize(target, level, format, width, height, byteLength, "compressedTexSubImage2D")) { return; @@ -5135,10 +5151,23 @@ WebGLContext::TexImage2D(WebGLenum target, WebGLint level, if (!IsContextStable()) return;
+ void* data; + uint32_t length; + int jsArrayType; + if (!pixels) { + data = nullptr; + length = 0; + jsArrayType = -1; + } else { + pixels->ComputeLengthAndData(); + + data = pixels->Data(); + length = pixels->Length(); + jsArrayType = int(JS_GetArrayBufferViewType(pixels->Obj())); + } + return TexImage2D_base(target, level, internalformat, width, height, 0, border, format, type, - pixels ? pixels->Data() : 0, - pixels ? pixels->Length() : 0, - pixels ? (int)JS_GetArrayBufferViewType(pixels->Obj()) : -1, + data, length, jsArrayType, WebGLTexelConversions::Auto, false); }
@@ -5156,6 +5185,8 @@ WebGLContext::TexImage2D(WebGLenum target, WebGLint level, }
Uint8ClampedArray arr(pixels->GetDataObject()); + arr.ComputeLengthAndData(); + return TexImage2D_base(target, level, internalformat, pixels->Width(), pixels->Height(), 4*pixels->Width(), 0, format, type, arr.Data(), arr.Length(), -1, @@ -5289,6 +5320,8 @@ WebGLContext::TexSubImage2D(WebGLenum target, WebGLint level, if (!pixels) return ErrorInvalidValue("texSubImage2D: pixels must not be null!");
+ pixels->ComputeLengthAndData(); + return TexSubImage2D_base(target, level, xoffset, yoffset, width, height, 0, format, type, pixels->Data(), pixels->Length(), @@ -5309,6 +5342,8 @@ WebGLContext::TexSubImage2D(WebGLenum target, WebGLint level, return ErrorInvalidValue("texSubImage2D: pixels must not be null!");
Uint8ClampedArray arr(pixels->GetDataObject()); + arr.ComputeLengthAndData(); + return TexSubImage2D_base(target, level, xoffset, yoffset, pixels->Width(), pixels->Height(), 4*pixels->Width(), format, type, diff --git a/content/html/content/public/HTMLAudioElement.h b/content/html/content/public/HTMLAudioElement.h index 49fcd23..3c19e84 100644 --- a/content/html/content/public/HTMLAudioElement.h +++ b/content/html/content/public/HTMLAudioElement.h @@ -63,6 +63,7 @@ public:
uint32_t MozWriteAudio(const Float32Array& aData, ErrorResult& aRv) { + aData.ComputeLengthAndData(); return MozWriteAudio(aData.Data(), aData.Length(), aRv); } uint32_t MozWriteAudio(const Sequence<float>& aData, ErrorResult& aRv) diff --git a/content/media/webaudio/AnalyserNode.cpp b/content/media/webaudio/AnalyserNode.cpp index d5fbe9c..613852d 100644 --- a/content/media/webaudio/AnalyserNode.cpp +++ b/content/media/webaudio/AnalyserNode.cpp @@ -150,6 +150,8 @@ AnalyserNode::GetFloatFrequencyData(Float32Array& aArray) return; }
+ aArray.ComputeLengthAndData(); + float* buffer = aArray.Data(); uint32_t length = std::min(aArray.Length(), mOutputBuffer.Length());
@@ -168,6 +170,8 @@ AnalyserNode::GetByteFrequencyData(Uint8Array& aArray)
const double rangeScaleFactor = 1.0 / (mMaxDecibels - mMinDecibels);
+ aArray.ComputeLengthAndData(); + unsigned char* buffer = aArray.Data(); uint32_t length = std::min(aArray.Length(), mOutputBuffer.Length());
@@ -183,6 +187,8 @@ AnalyserNode::GetByteFrequencyData(Uint8Array& aArray) void AnalyserNode::GetByteTimeDomainData(Uint8Array& aArray) { + aArray.ComputeLengthAndData(); + unsigned char* buffer = aArray.Data(); uint32_t length = std::min(aArray.Length(), mBuffer.Length());
diff --git a/content/media/webaudio/AudioContext.cpp b/content/media/webaudio/AudioContext.cpp index 1bf78ef..3ef2ff4 100644 --- a/content/media/webaudio/AudioContext.cpp +++ b/content/media/webaudio/AudioContext.cpp @@ -169,18 +169,20 @@ AudioContext::CreateBuffer(JSContext* aJSContext, ArrayBuffer& aBuffer, return nullptr; }
+ aBuffer.ComputeLengthAndData(); + + uint32_t len = aBuffer.Length(); + uint8_t* data = aBuffer.Data(); + // Sniff the content of the media. // Failed type sniffing will be handled by SyncDecodeMedia. nsAutoCString contentType; - NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, nullptr, - aBuffer.Data(), aBuffer.Length(), - contentType); + NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, nullptr, data, len, contentType);
nsRefPtr<WebAudioDecodeJob> job = new WebAudioDecodeJob(contentType, this, aBuffer);
- if (mDecoder.SyncDecodeMedia(contentType.get(), - aBuffer.Data(), aBuffer.Length(), *job) && + if (mDecoder.SyncDecodeMedia(contentType.get(), data, len, *job) && job->mOutput) { nsRefPtr<AudioBuffer> buffer = job->mOutput.forget(); if (aMixToMono) { @@ -342,6 +344,9 @@ AudioContext::CreatePeriodicWave(const Float32Array& aRealData, const Float32Array& aImagData, ErrorResult& aRv) { + aRealData.ComputeLengthAndData(); + aImagData.ComputeLengthAndData(); + if (aRealData.Length() != aImagData.Length() || aRealData.Length() == 0 || aRealData.Length() > 4096) { @@ -369,6 +374,8 @@ AudioContext::DecodeAudioData(const ArrayBuffer& aBuffer, DecodeSuccessCallback& aSuccessCallback, const Optional<OwningNonNull<DecodeErrorCallback> >& aFailureCallback) { + aBuffer.ComputeLengthAndData(); + // Sniff the content of the media. // Failed type sniffing will be handled by AsyncDecodeMedia. nsAutoCString contentType; diff --git a/content/media/webaudio/AudioParam.h b/content/media/webaudio/AudioParam.h index e735b59..e6d95b0 100644 --- a/content/media/webaudio/AudioParam.h +++ b/content/media/webaudio/AudioParam.h @@ -57,6 +57,7 @@ public: aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return; } + aValues.ComputeLengthAndData(); AudioParamTimeline::SetValueCurveAtTime(aValues.Data(), aValues.Length(), aStartTime, aDuration, aRv); mCallback(mNode); diff --git a/content/media/webaudio/BiquadFilterNode.cpp b/content/media/webaudio/BiquadFilterNode.cpp index fe45106..fa4f1d1 100644 --- a/content/media/webaudio/BiquadFilterNode.cpp +++ b/content/media/webaudio/BiquadFilterNode.cpp @@ -247,6 +247,10 @@ BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz, Float32Array& aMagResponse, Float32Array& aPhaseResponse) { + aFrequencyHz.ComputeLengthAndData(); + aMagResponse.ComputeLengthAndData(); + aPhaseResponse.ComputeLengthAndData(); + uint32_t length = std::min(std::min(aFrequencyHz.Length(), aMagResponse.Length()), aPhaseResponse.Length()); if (!length) { diff --git a/content/media/webaudio/WaveShaperNode.cpp b/content/media/webaudio/WaveShaperNode.cpp index 22df163..fa39637 100644 --- a/content/media/webaudio/WaveShaperNode.cpp +++ b/content/media/webaudio/WaveShaperNode.cpp @@ -125,6 +125,8 @@ WaveShaperNode::SetCurve(const Float32Array* aCurve) if (aCurve) { mCurve = aCurve->Obj();
+ aCurve->ComputeLengthAndData(); + curve.SetLength(aCurve->Length()); PodCopy(curve.Elements(), aCurve->Data(), aCurve->Length()); } else { diff --git a/dom/bindings/TypedArray.h b/dom/bindings/TypedArray.h index 6531f2f..c7c2bbd 100644 --- a/dom/bindings/TypedArray.h +++ b/dom/bindings/TypedArray.h @@ -20,30 +20,43 @@ namespace dom { * or array buffer object. */ template<typename T, - JSObject* UnboxArray(JSObject*, uint32_t*, T**)> + JSObject* UnwrapArray(JSObject*), + void GetLengthAndData(JSObject*, uint32_t*, T**)> struct TypedArray_base { TypedArray_base(JSObject* obj) + : mObj(obj), + mData(NULL), + mLength(0), + mComputed(false) { - mObj = UnboxArray(obj, &mLength, &mData); + MOZ_ASSERT(obj != NULL); }
private: - T* mData; - uint32_t mLength; JSObject* mObj; + mutable T* mData; + mutable uint32_t mLength; + mutable bool mComputed;
public: + inline bool Init(JSObject* obj) + { + MOZ_ASSERT(!inited()); + DoInit(obj); + return inited(); + } + inline bool inited() const { return !!mObj; }
inline T *Data() const { - MOZ_ASSERT(inited()); + MOZ_ASSERT(mComputed); return mData; }
inline uint32_t Length() const { - MOZ_ASSERT(inited()); + MOZ_ASSERT(mComputed); return mLength; }
@@ -51,16 +64,31 @@ public: MOZ_ASSERT(inited()); return mObj; } + + inline void ComputeLengthAndData() const + { + MOZ_ASSERT(inited()); + MOZ_ASSERT(!mComputed); + GetLengthAndData(mObj, &mLength, &mData); + mComputed = true; + } + +protected: + inline void DoInit(JSObject* obj) + { + mObj = UnwrapArray(obj); + } };
template<typename T, + JSObject* UnwrapArray(JSObject*), T* GetData(JSObject*), - JSObject* UnboxArray(JSObject*, uint32_t*, T**), + void GetLengthAndData(JSObject*, uint32_t*, T**), JSObject* CreateNew(JSContext*, uint32_t)> -struct TypedArray : public TypedArray_base<T,UnboxArray> { +struct TypedArray : public TypedArray_base<T, UnwrapArray, GetLengthAndData> { TypedArray(JSObject* obj) : - TypedArray_base<T,UnboxArray>(obj) + TypedArray_base<T, UnwrapArray, GetLengthAndData>(obj) {}
static inline JSObject* @@ -83,37 +111,37 @@ struct TypedArray : public TypedArray_base<T,UnboxArray> { } };
-typedef TypedArray<int8_t, JS_GetInt8ArrayData, JS_GetObjectAsInt8Array, - JS_NewInt8Array> +typedef TypedArray<int8_t, js::UnwrapInt8Array, JS_GetInt8ArrayData, + js::GetInt8ArrayLengthAndData, JS_NewInt8Array> Int8Array; -typedef TypedArray<uint8_t, JS_GetUint8ArrayData, - JS_GetObjectAsUint8Array, JS_NewUint8Array> +typedef TypedArray<uint8_t, js::UnwrapUint8Array, JS_GetUint8ArrayData, + js::GetUint8ArrayLengthAndData, JS_NewUint8Array> Uint8Array; -typedef TypedArray<uint8_t, JS_GetUint8ClampedArrayData, - JS_GetObjectAsUint8ClampedArray, JS_NewUint8ClampedArray> +typedef TypedArray<uint8_t, js::UnwrapUint8ClampedArray, JS_GetUint8ClampedArrayData, + js::GetUint8ClampedArrayLengthAndData, JS_NewUint8ClampedArray> Uint8ClampedArray; -typedef TypedArray<int16_t, JS_GetInt16ArrayData, - JS_GetObjectAsInt16Array, JS_NewInt16Array> +typedef TypedArray<int16_t, js::UnwrapInt16Array, JS_GetInt16ArrayData, + js::GetInt16ArrayLengthAndData, JS_NewInt16Array> Int16Array; -typedef TypedArray<uint16_t, JS_GetUint16ArrayData, - JS_GetObjectAsUint16Array, JS_NewUint16Array> +typedef TypedArray<uint16_t, js::UnwrapUint16Array, JS_GetUint16ArrayData, + js::GetUint16ArrayLengthAndData, JS_NewUint16Array> Uint16Array; -typedef TypedArray<int32_t, JS_GetInt32ArrayData, - JS_GetObjectAsInt32Array, JS_NewInt32Array> +typedef TypedArray<int32_t, js::UnwrapInt32Array, JS_GetInt32ArrayData, + js::GetInt32ArrayLengthAndData, JS_NewInt32Array> Int32Array; -typedef TypedArray<uint32_t, JS_GetUint32ArrayData, - JS_GetObjectAsUint32Array, JS_NewUint32Array> +typedef TypedArray<uint32_t, js::UnwrapUint32Array, JS_GetUint32ArrayData, + js::GetUint32ArrayLengthAndData, JS_NewUint32Array> Uint32Array; -typedef TypedArray<float, JS_GetFloat32ArrayData, - JS_GetObjectAsFloat32Array, JS_NewFloat32Array> +typedef TypedArray<float, js::UnwrapFloat32Array, JS_GetFloat32ArrayData, + js::GetFloat32ArrayLengthAndData, JS_NewFloat32Array> Float32Array; -typedef TypedArray<double, JS_GetFloat64ArrayData, - JS_GetObjectAsFloat64Array, JS_NewFloat64Array> +typedef TypedArray<double, js::UnwrapFloat64Array, JS_GetFloat64ArrayData, + js::GetFloat64ArrayLengthAndData, JS_NewFloat64Array> Float64Array; -typedef TypedArray_base<uint8_t, JS_GetObjectAsArrayBufferView> +typedef TypedArray_base<uint8_t, js::UnwrapArrayBufferView, js::GetArrayBufferViewLengthAndData> ArrayBufferView; -typedef TypedArray<uint8_t, JS_GetArrayBufferData, - JS_GetObjectAsArrayBuffer, JS_NewArrayBuffer> +typedef TypedArray<uint8_t, js::UnwrapArrayBuffer, JS_GetArrayBufferData, + js::GetArrayBufferLengthAndData, JS_NewArrayBuffer> ArrayBuffer;
} // namespace dom diff --git a/dom/encoding/TextDecoder.h b/dom/encoding/TextDecoder.h index d309209..3db00a4 100644 --- a/dom/encoding/TextDecoder.h +++ b/dom/encoding/TextDecoder.h @@ -66,6 +66,7 @@ public: const TextDecodeOptions& aOptions, nsAString& aOutDecodedString, ErrorResult& aRv) { + aView.ComputeLengthAndData(); TextDecoderBase::Decode(reinterpret_cast<char*>(aView.Data()), aView.Length(), aOptions.mStream, aOutDecodedString, aRv); diff --git a/dom/workers/TextDecoder.h b/dom/workers/TextDecoder.h index 6c88a70..205afc5 100644 --- a/dom/workers/TextDecoder.h +++ b/dom/workers/TextDecoder.h @@ -49,6 +49,7 @@ public: const TextDecodeOptionsWorkers& aOptions, nsAString& aOutDecodedString, ErrorResult& aRv) { + aView.ComputeLengthAndData(); TextDecoderBase::Decode(reinterpret_cast<char*>(aView.Data()), aView.Length(), aOptions.mStream, aOutDecodedString, aRv); diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 24cb2d7..ce6d633 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -999,6 +999,49 @@ js::IsAsmJSFunction(JSContext *cx, unsigned argc, Value *vp) } #endif
+static JSBool +Neuter(JSContext *cx, unsigned argc, jsval *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() != 2) { + JS_ReportError(cx, "wrong number of arguments to neuter()"); + return false; + } + + RootedObject obj(cx); + if (!JS_ValueToObject(cx, args[0], obj.address())) + return false; + + if (!obj) { + JS_ReportError(cx, "neuter must be passed an object"); + return false; + } + + NeuterDataDisposition changeData; + RootedValue v(cx, args[1]); + RootedString str(cx, ToString<CanGC>(cx, v)); + if (!str) + return false; + JSAutoByteString dataDisposition(cx, str); + if (!dataDisposition) + return false; + if (strcmp(dataDisposition.ptr(), "same-data") == 0) { + changeData = KeepData; + } else if (strcmp(dataDisposition.ptr(), "change-data") == 0) { + changeData = ChangeData; + } else { + JS_ReportError(cx, "unknown parameter 2 to neuter()"); + return false; + } + + if (!js::NeuterArrayBuffer(cx, obj, changeData)) + return false; + + args.rval().setUndefined(); + return true; +} + static JSFunctionSpecWithHelp TestingFunctions[] = { JS_FN_HELP("gc", ::GC, 0, 0, "gc([obj] | 'compartment')", @@ -1177,6 +1220,13 @@ static JSFunctionSpecWithHelp TestingFunctions[] = { "getObjectMetadata(obj)", " Get the metadata for an object."),
+ JS_FN_HELP("neuter", Neuter, 1, 0, +"neuter(buffer, "change-data"|"same-data")", +" Neuter the given ArrayBuffer object as if it had been transferred to a\n" +" WebWorker. "change-data" will update the internal data pointer.\n" +" "same-data" will leave it set to its original value, to mimic eg\n" +" asm.js ArrayBuffer neutering."), + JS_FS_HELP_END };
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index a0c70f5..2ceaf71 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -6446,29 +6446,12 @@ IonBuilder::jsop_getelem_dense() MInstruction * IonBuilder::getTypedArrayLength(MDefinition *obj) { - if (obj->isConstant() && obj->toConstant()->value().isObject()) { - JSObject *array = &obj->toConstant()->value().toObject(); - int32_t length = (int32_t) TypedArray::length(array); - obj->setFoldedUnchecked(); - return MConstant::New(Int32Value(length)); - } return MTypedArrayLength::New(obj); }
MInstruction * IonBuilder::getTypedArrayElements(MDefinition *obj) { - if (obj->isConstant() && obj->toConstant()->value().isObject()) { - JSObject *array = &obj->toConstant()->value().toObject(); - void *data = TypedArray::viewData(array); - - // The 'data' pointer can change in rare circumstances - // (ArrayBufferObject::changeContents). - types::HeapTypeSet::WatchObjectStateChange(cx, array->getType(cx)); - - obj->setFoldedUnchecked(); - return MConstantElements::New(data); - } return MTypedArrayElements::New(obj); }
@@ -6504,49 +6487,6 @@ IonBuilder::jsop_getelem_typed_static(bool *psucceeded) if (!LIRGenerator::allowStaticTypedArrayAccesses()) return true;
- MDefinition *id = current->peek(-1); - MDefinition *obj = current->peek(-2); - - if (ElementAccessHasExtraIndexedProperty(cx, obj)) - return true; - - if (!obj->resultTypeSet()) - return true; - JSObject *typedArray = obj->resultTypeSet()->getSingleton(); - if (!typedArray) - return true; - JS_ASSERT(typedArray->isTypedArray()); - - ArrayBufferView::ViewType viewType = JS_GetArrayBufferViewType(typedArray); - - MDefinition *ptr = convertShiftToMaskForStaticTypedArray(id, viewType); - if (!ptr) - return true; - - obj->setFoldedUnchecked(); - - MLoadTypedArrayElementStatic *load = MLoadTypedArrayElementStatic::New(typedArray, ptr); - current->add(load); - - // The load is infallible if an undefined result will be coerced to the - // appropriate numeric type if the read is out of bounds. The truncation - // analysis picks up some of these cases, but is incomplete with respect - // to others. For now, sniff the bytecode for simple patterns following - // the load which guarantee a truncation or numeric conversion. - if (viewType == ArrayBufferView::TYPE_FLOAT32 || viewType == ArrayBufferView::TYPE_FLOAT64) { - jsbytecode *next = pc + JSOP_GETELEM_LENGTH; - if (*next == JSOP_POS) - load->setInfallible(); - } else { - jsbytecode *next = pc + JSOP_GETELEM_LENGTH; - if (*next == JSOP_ZERO && *(next + JSOP_ZERO_LENGTH) == JSOP_BITOR) - load->setInfallible(); - } - - current->popn(2); - current->push(load); - - *psucceeded = true; return true; }
@@ -6854,37 +6794,7 @@ IonBuilder::jsop_setelem_typed_static(MDefinition *obj, MDefinition *id, MDefini if (!LIRGenerator::allowStaticTypedArrayAccesses()) return true;
- if (ElementAccessHasExtraIndexedProperty(cx, obj)) - return true; - - if (!obj->resultTypeSet()) - return true; - JSObject *typedArray = obj->resultTypeSet()->getSingleton(); - if (!typedArray) - return true; - JS_ASSERT(typedArray->isTypedArray()); - - ArrayBufferView::ViewType viewType = JS_GetArrayBufferViewType(typedArray); - - MDefinition *ptr = convertShiftToMaskForStaticTypedArray(id, viewType); - if (!ptr) - return true; - - obj->setFoldedUnchecked(); - - // Clamp value to [0, 255] for Uint8ClampedArray. - MDefinition *toWrite = value; - if (viewType == ArrayBufferView::TYPE_UINT8_CLAMPED) { - toWrite = MClampToUint8::New(value); - current->add(toWrite->toInstruction()); - } - - MInstruction *store = MStoreTypedArrayElementStatic::New(typedArray, ptr, toWrite); - current->add(store); - current->push(value); - - *psucceeded = true; - return resumeAfter(store); + return true; }
bool diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 96ba2d4..02ba6d7 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -211,14 +211,16 @@ class AliasSet { FixedSlot = 1 << 3, // A member of obj->fixedSlots(). TypedArrayElement = 1 << 4, // A typed array element. DOMProperty = 1 << 5, // A DOM property - Last = DOMProperty, + TypedArrayLength = 1 << 6, // A typed array's length + Last = TypedArrayLength, Any = Last | (Last - 1),
- NumCategories = 6, + NumCategories = 7,
// Indicates load or store. Store_ = 1 << 31 }; + AliasSet(uint32_t flags) : flags_(flags) { @@ -4340,9 +4342,7 @@ class MTypedArrayLength return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { - // The typed array |length| property is immutable, so there is no - // implicit dependency. - return AliasSet::None(); + return AliasSet::Load(AliasSet::TypedArrayLength); } };
diff --git a/js/src/js.msg b/js/src/js.msg index 4047035..05807e0 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -241,11 +241,11 @@ MSG_DEF(JSMSG_UNUSED187, 187, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_INCOMPATIBLE_METHOD, 188, 3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}") MSG_DEF(JSMSG_UNUSED189, 189, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_UNUSED190, 190, 0, JSEXN_NONE, "") -MSG_DEF(JSMSG_UNUSED191, 191, 0, JSEXN_NONE, "") +MSG_DEF(JSMSG_BAD_INDEX, 191, 0, JSEXN_RANGEERR, "invalid or out-of-range index") MSG_DEF(JSMSG_UNUSED192, 192, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP, 193, 0, JSEXN_SYNTAXERR, "invalid for each loop") MSG_DEF(JSMSG_UNUSED194, 194, 0, JSEXN_NONE, "") -MSG_DEF(JSMSG_UNUSED195, 195, 0, JSEXN_NONE, "") +MSG_DEF(JSMSG_TYPE_ERR_BAD_ARGS, 195, 0, JSEXN_TYPEERR, "invalid arguments") MSG_DEF(JSMSG_UNUSED196, 196, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_INTERNAL_INTL_ERROR, 197, 0, JSEXN_ERR, "internal error while computing Intl data") MSG_DEF(JSMSG_DEFAULT_LOCALE_ERROR, 198, 0, JSEXN_ERR, "internal error getting the default locale") diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index d9840b1..da447a5 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -7,6 +7,8 @@ #ifndef jsfriendapi_h #define jsfriendapi_h
+#include "mozilla/Casting.h" + #include "jsclass.h" #include "jspubtd.h" #include "jsprvtd.h" @@ -1289,6 +1291,93 @@ extern JS_FRIEND_API(JSBool) JS_IsFloat64Array(JSObject *obj);
/* + * Test for specific typed array types (ArrayBufferView subtypes) and return + * the unwrapped object if so, else nullptr. Never throws. + */ + +namespace js { + +extern JS_FRIEND_API(JSObject *) +UnwrapInt8Array(JSObject *obj); +extern JS_FRIEND_API(JSObject *) +UnwrapUint8Array(JSObject *obj); +extern JS_FRIEND_API(JSObject *) +UnwrapUint8ClampedArray(JSObject *obj); +extern JS_FRIEND_API(JSObject *) +UnwrapInt16Array(JSObject *obj); +extern JS_FRIEND_API(JSObject *) +UnwrapUint16Array(JSObject *obj); +extern JS_FRIEND_API(JSObject *) +UnwrapInt32Array(JSObject *obj); +extern JS_FRIEND_API(JSObject *) +UnwrapUint32Array(JSObject *obj); +extern JS_FRIEND_API(JSObject *) +UnwrapFloat32Array(JSObject *obj); +extern JS_FRIEND_API(JSObject *) +UnwrapFloat64Array(JSObject *obj); + +extern JS_FRIEND_API(JSObject *) +UnwrapArrayBuffer(JSObject *obj); + +extern JS_FRIEND_API(JSObject *) +UnwrapArrayBufferView(JSObject *obj); + +namespace detail { + +extern JS_FRIEND_DATA(const Class* const) Int8ArrayClassPtr; +extern JS_FRIEND_DATA(const Class* const) Uint8ArrayClassPtr; +extern JS_FRIEND_DATA(const Class* const) Uint8ClampedArrayClassPtr; +extern JS_FRIEND_DATA(const Class* const) Int16ArrayClassPtr; +extern JS_FRIEND_DATA(const Class* const) Uint16ArrayClassPtr; +extern JS_FRIEND_DATA(const Class* const) Int32ArrayClassPtr; +extern JS_FRIEND_DATA(const Class* const) Uint32ArrayClassPtr; +extern JS_FRIEND_DATA(const Class* const) Float32ArrayClassPtr; +extern JS_FRIEND_DATA(const Class* const) Float64ArrayClassPtr; + +const size_t TypedArrayLengthSlot = 5; + +} // namespace detail + +/* + * Test for specific typed array types (ArrayBufferView subtypes) and return + * the unwrapped object if so, else nullptr. Never throws. + */ + +#define JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Type, type) \ +inline void \ +Get ## Type ## ArrayLengthAndData(JSObject *obj, uint32_t *length, type **data) \ +{ \ + JS_ASSERT(GetObjectClass(obj) == detail::Type ## ArrayClassPtr); \ + const JS::Value &slot = GetReservedSlot(obj, detail::TypedArrayLengthSlot); \ + *length = mozilla::SafeCast<uint32_t>(slot.toInt32()); \ + *data = static_cast<type*>(GetObjectPrivate(obj)); \ +} + +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int8, int8_t) +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8, uint8_t) +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8Clamped, uint8_t) +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int16, int16_t) +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint16, uint16_t) +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int32, int32_t) +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint32, uint32_t) +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float32, float) +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float64, double) + +#undef JS_DEFINE_DATA_AND_LENGTH_ACCESSOR + +// This one isn't inlined because it's rather tricky (by dint of having to deal +// with a dozen-plus classes and varying slot layouts. +extern JS_FRIEND_API(void) +GetArrayBufferViewLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data); + +// This one isn't inlined because there are a bunch of different ArrayBuffer +// classes that would have to be individually handled here. +extern JS_FRIEND_API(void) +GetArrayBufferLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data); + +} // namespace js + +/* * Unwrap Typed arrays all at once. Return NULL without throwing if the object * cannot be viewed as the correct typed array, or the typed array object on * success, filling both outparameters. @@ -1446,6 +1535,28 @@ JS_GetArrayBufferViewData(JSObject *obj); extern JS_FRIEND_API(JSObject *) JS_GetArrayBufferViewBuffer(JSObject *obj);
+typedef enum { + ChangeData, + KeepData +} NeuterDataDisposition; + +namespace js { + +/* + * Set an ArrayBuffer's length to 0 and neuter all of its views. + * + * The |changeData| argument is a hint to inform internal behavior with respect + * to the internal pointer to the ArrayBuffer's data after being neutered. + * There is no guarantee it will be respected. But if it is respected, the + * ArrayBuffer's internal data pointer will, or will not, have changed + * accordingly. + */ +extern JS_FRIEND_API(bool) +NeuterArrayBuffer(JSContext *cx, JS::HandleObject obj, + NeuterDataDisposition changeData); + +} /* namespace js */ + /* * Check whether obj supports JS_GetDataView* APIs. */ diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index 9d02d06..a78edd4 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -577,9 +577,12 @@ JSObject * ArrayBufferObject::createSlice(JSContext *cx, ArrayBufferObject &arrayBuffer, uint32_t begin, uint32_t end) { - JS_ASSERT(begin <= arrayBuffer.byteLength()); - JS_ASSERT(end <= arrayBuffer.byteLength()); - JS_ASSERT(begin <= end); + uint32_t bufLength = arrayBuffer.byteLength(); + if (begin > bufLength || end > bufLength || begin > end) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPE_ERR_BAD_ARGS); + return NULL; + } + uint32_t length = end - begin;
if (arrayBuffer.hasData()) @@ -620,8 +623,8 @@ ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp }
bool -ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, void **contents, - uint8_t **data) +ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, NeuterDataDisposition changeData, + void **contents, uint8_t **data) { MOZ_ASSERT(cx);
@@ -635,7 +638,7 @@ ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, void **contents,
// If the ArrayBuffer's elements are transferrable, transfer ownership // directly. Otherwise we have to copy the data into new elements. - bool stolen = buffer.hasStealableContents(); + bool stolen = buffer.hasStealableContents() && changeData == ChangeData; if (stolen) { newHeader = AllocateArrayBufferContents(cx, byteLen, NULL); if (!newHeader) @@ -1190,13 +1193,15 @@ js::IsDataView(JSObject* obj) }
void -TypedArray::neuter(JSObject *tarray) +TypedArray::neuter(JSObject *view) { - JS_ASSERT(tarray->isTypedArray()); - tarray->setSlot(LENGTH_SLOT, Int32Value(0)); - tarray->setSlot(BYTELENGTH_SLOT, Int32Value(0)); - tarray->setSlot(BYTEOFFSET_SLOT, Int32Value(0)); - tarray->setPrivate(NULL); + if (view->isTypedArray()) + view->setSlot(LENGTH_SLOT, Int32Value(0)); + else + MOZ_ASSERT(view->hasClass(&DataViewObject::class_)); + view->setSlot(BYTELENGTH_SLOT, Int32Value(0)); + view->setSlot(BYTEOFFSET_SLOT, Int32Value(0)); + view->setPrivate(NULL); }
JSBool @@ -2023,20 +2028,26 @@ class TypedArrayTemplate uint32_t srcEnd; uint32_t dest;
- uint32_t length = TypedArray::length(tarray); - if (!ToClampedIndex(cx, args[0], length, &srcBegin) || - !ToClampedIndex(cx, args[1], length, &srcEnd) || - !ToClampedIndex(cx, args[2], length, &dest) || + uint32_t originalLength = TypedArray::length(tarray); + if (!ToClampedIndex(cx, args[0], originalLength, &srcBegin) || + !ToClampedIndex(cx, args[1], originalLength, &srcEnd) || + !ToClampedIndex(cx, args[2], originalLength, &dest) || srcBegin > srcEnd) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS); return false; }
+ if (srcBegin > srcEnd) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_INDEX); + return false; + } + + uint32_t lengthDuringMove = TypedArray::length(tarray); // beware ToClampedIndex uint32_t nelts = srcEnd - srcBegin;
- JS_ASSERT(dest + nelts >= dest); - if (dest + nelts > length) { + MOZ_ASSERT(dest <= INT32_MAX, "size limited to 2**31"); + MOZ_ASSERT(nelts <= INT32_MAX, "size limited to 2**31"); + if (dest + nelts > lengthDuringMove || srcEnd > lengthDuringMove) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS); return false; } @@ -2114,8 +2125,7 @@ class TypedArrayTemplate if (!GetLengthProperty(cx, arg0, &len)) return false;
- // avoid overflow; we know that offset <= length - if (len > length(tarray) - offset) { + if (uint32_t(offset) > length(tarray) || len > length(tarray) - offset) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); return false; } @@ -2268,7 +2278,8 @@ class TypedArrayTemplate static const NativeType getIndex(JSObject *obj, uint32_t index) { - return *(static_cast<const NativeType*>(viewData(obj)) + index); + MOZ_ASSERT(index < length(obj)); + return static_cast<const NativeType*>(viewData(obj))[index]; }
static void @@ -2284,13 +2295,14 @@ class TypedArrayTemplate { JS_ASSERT(tarray);
- JS_ASSERT(begin <= length(tarray)); - JS_ASSERT(end <= length(tarray)); + if (begin > length(tarray) || end > length(tarray) || begin > end) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_INDEX); + return NULL; + }
RootedObject bufobj(cx, buffer(tarray)); JS_ASSERT(bufobj);
- JS_ASSERT(begin <= end); uint32_t length = end - begin;
JS_ASSERT(begin < UINT32_MAX / sizeof(NativeType)); @@ -2505,53 +2517,54 @@ class TypedArrayTemplate return false; js_memcpy(srcbuf, viewData(tarray), byteLength);
+ uint32_t len = length(tarray); switch (type(tarray)) { case TypedArray::TYPE_INT8: { int8_t *src = (int8_t*) srcbuf; - for (unsigned i = 0; i < length(tarray); ++i) + for (unsigned i = 0; i < len; ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_UINT8: case TypedArray::TYPE_UINT8_CLAMPED: { uint8_t *src = (uint8_t*) srcbuf; - for (unsigned i = 0; i < length(tarray); ++i) + for (unsigned i = 0; i < len; ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_INT16: { int16_t *src = (int16_t*) srcbuf; - for (unsigned i = 0; i < length(tarray); ++i) + for (unsigned i = 0; i < len; ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_UINT16: { uint16_t *src = (uint16_t*) srcbuf; - for (unsigned i = 0; i < length(tarray); ++i) + for (unsigned i = 0; i < len; ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_INT32: { int32_t *src = (int32_t*) srcbuf; - for (unsigned i = 0; i < length(tarray); ++i) + for (unsigned i = 0; i < len; ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_UINT32: { uint32_t *src = (uint32_t*) srcbuf; - for (unsigned i = 0; i < length(tarray); ++i) + for (unsigned i = 0; i < len; ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_FLOAT32: { float *src = (float*) srcbuf; - for (unsigned i = 0; i < length(tarray); ++i) + for (unsigned i = 0; i < len; ++i) *dest++ = NativeType(*src++); break; } case TypedArray::TYPE_FLOAT64: { double *src = (double*) srcbuf; - for (unsigned i = 0; i < length(tarray); ++i) + for (unsigned i = 0; i < len; ++i) *dest++ = NativeType(*src++); break; } @@ -2822,21 +2835,17 @@ DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp) return construct(cx, bufobj, args, NullPtr()); }
-/* static */ bool -DataViewObject::getDataPointer(JSContext *cx, Handle<DataViewObject*> obj, - CallArgs args, size_t typeSize, uint8_t **data) +template <typename NativeType> +/* static */ uint8_t * +DataViewObject::getDataPointer(JSContext *cx, Handle<DataViewObject*> obj, uint32_t offset) { - uint32_t offset; - JS_ASSERT(args.length() > 0); - if (!ToUint32(cx, args[0], &offset)) - return false; - if (offset > UINT32_MAX - typeSize || offset + typeSize > obj->byteLength()) { + const size_t TypeSize = sizeof(NativeType); + if (offset > UINT32_MAX - TypeSize || offset + TypeSize > obj->byteLength()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); - return false; + return NULL; }
- *data = static_cast<uint8_t*>(obj->dataPointer()) + offset; - return true; + return static_cast<uint8_t*>(obj->dataPointer()) + offset; }
static inline bool @@ -2924,11 +2933,17 @@ DataViewObject::read(JSContext *cx, Handle<DataViewObject*> obj, return false; }
- uint8_t *data; - if (!getDataPointer(cx, obj, args, sizeof(NativeType), &data)) + uint32_t offset; + if (!ToUint32(cx, args[0], &offset)) return false;
bool fromLittleEndian = args.length() >= 2 && ToBoolean(args[1]); + + uint8_t *data = DataViewObject::getDataPointer<NativeType>(cx, obj, offset); + SkipRoot skipData(cx, &data); + if (!data) + return false; + DataViewIO<NativeType>::fromBuffer(val, data, needToSwapBytes(fromLittleEndian)); return true; } @@ -2976,9 +2991,8 @@ DataViewObject::write(JSContext *cx, Handle<DataViewObject*> obj, return false; }
- uint8_t *data; - SkipRoot skipData(cx, &data); - if (!getDataPointer(cx, obj, args, sizeof(NativeType), &data)) + uint32_t offset; + if (!ToUint32(cx, args[0], &offset)) return false;
NativeType value; @@ -2986,6 +3000,12 @@ DataViewObject::write(JSContext *cx, Handle<DataViewObject*> obj, return false;
bool toLittleEndian = args.length() >= 3 && ToBoolean(args[2]); + + uint8_t *data = DataViewObject::getDataPointer<NativeType>(cx, obj, offset); + SkipRoot skipData(cx, &data); + if (!data) + return false; + DataViewIO<NativeType>::toBuffer(data, &value, needToSwapBytes(toLittleEndian)); return true; } @@ -3449,7 +3469,19 @@ const JSFunctionSpec _typedArray::jsfuncs[] = { \ return false; \ Class *clasp = obj->getClass(); \ return (clasp == &TypedArray::classes[TypedArrayTemplate<NativeType>::ArrayTypeID()]); \ - } + } \ + JS_FRIEND_API(JSObject *) js::Unwrap ## Name ## Array(JSObject *obj) \ + { \ + obj = CheckedUnwrap(obj); \ + if (!obj) \ + return NULL; \ + const Class *clasp = obj->getClass(); \ + if (clasp == &TypedArray::classes[TypedArrayTemplate<NativeType>::ArrayTypeID()]) \ + return obj; \ + return NULL; \ + } \ + JS_FRIEND_DATA(const js::Class* const) js::detail::Name ## ArrayClassPtr = \ + &js::TypedArray::classes[TypedArrayTemplate<NativeType>::ArrayTypeID()];
IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int8, int8_t) IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8, uint8_t) @@ -3918,6 +3950,14 @@ JS_IsArrayBufferObject(JSObject *obj) return obj ? obj->is<ArrayBufferObject>() : false; }
+JS_FRIEND_API(JSObject *) +js::UnwrapArrayBuffer(JSObject *obj) +{ + if (JSObject *unwrapped = CheckedUnwrap(obj)) + return unwrapped->is<ArrayBufferObject>() ? unwrapped : NULL; + return NULL; +} + JS_FRIEND_API(JSBool) JS_IsTypedArrayObject(JSObject *obj) { @@ -3932,6 +3972,16 @@ JS_IsArrayBufferViewObject(JSObject *obj) return obj ? (obj->isTypedArray() || obj->is<DataViewObject>()) : false; }
+JS_FRIEND_API(JSObject *) +js::UnwrapArrayBufferView(JSObject *obj) +{ + if (JSObject *unwrapped = CheckedUnwrap(obj)) { + if (unwrapped->isTypedArray() || unwrapped->is<DataViewObject>()) + return unwrapped; + } + return NULL; +} + JS_FRIEND_API(uint32_t) JS_GetArrayBufferByteLength(JSObject *obj) { @@ -3951,6 +4001,24 @@ JS_GetArrayBufferData(JSObject *obj) return buffer.dataPointer(); }
+JS_FRIEND_API(bool) +js::NeuterArrayBuffer(JSContext *cx, HandleObject obj, + NeuterDataDisposition changeData) +{ + if (!obj->is<ArrayBufferObject>()) { + JS_ReportError(cx, "ArrayBuffer object required"); + return false; + } + + void *contents; + uint8_t *data; + if (!ArrayBufferObject::stealContents(cx, obj, changeData, &contents, &data)) + return false; + + JS_free(cx, contents); + return true; +} + JS_FRIEND_API(JSObject *) JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes) { @@ -4010,7 +4078,7 @@ JS_StealArrayBufferContents(JSContext *cx, JSObject *obj, void **contents, return false; }
- if (!ArrayBufferObject::stealContents(cx, obj, contents, data)) + if (!ArrayBufferObject::stealContents(cx, obj, ChangeData, contents, data)) return false;
return true; @@ -4229,6 +4297,18 @@ JS_GetArrayBufferViewByteLength(JSObject *obj) : TypedArray::byteLengthValue(obj).toInt32(); }
+JS_FRIEND_API(void) +js::GetArrayBufferViewLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data) +{ + MOZ_ASSERT(obj->is<DataViewObject>() || obj->isTypedArray()); + + *length = obj->is<DataViewObject>() + ? obj->as<DataViewObject>().byteLength() + : TypedArray::byteLength(obj); + + *data = static_cast<uint8_t*>(obj->getPrivate()); +} + JS_FRIEND_API(JSObject *) JS_GetObjectAsArrayBufferView(JSObject *obj, uint32_t *length, uint8_t **data) { @@ -4259,3 +4339,11 @@ JS_GetObjectAsArrayBuffer(JSObject *obj, uint32_t *length, uint8_t **data)
return obj; } + +JS_FRIEND_API(void) +js::GetArrayBufferLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data) +{ + MOZ_ASSERT(obj->is<ArrayBufferObject>()); + *length = obj->as<ArrayBufferObject>().byteLength(); + *data = obj->as<ArrayBufferObject>().dataPointer(); +} diff --git a/js/src/jstypedarray.h b/js/src/jstypedarray.h index 4751d53..07d7f80 100644 --- a/js/src/jstypedarray.h +++ b/js/src/jstypedarray.h @@ -9,6 +9,7 @@
#include "jsapi.h" #include "jsclass.h" +#include "jsfriendapi.h" #include "jsobj.h"
#include "gc/Barrier.h" @@ -155,8 +156,8 @@ class ArrayBufferObject : public JSObject return !isNeutered(); }
- static bool stealContents(JSContext *cx, JSObject *obj, void **contents, - uint8_t **data); + static bool stealContents(JSContext *cx, JSObject *obj, NeuterDataDisposition changeData, + void **contents, uint8_t **data);
static inline void setElementsHeader(js::ObjectElements *header, uint32_t bytes); static inline uint32_t getElementsHeaderInitializedLength(const js::ObjectElements *header); @@ -308,7 +309,7 @@ struct TypedArray : public BufferView { public: static bool isArrayIndex(JSObject *obj, jsid id, uint32_t *ip = NULL);
- static void neuter(JSObject *tarray); + static void neuter(JSObject *view);
static inline uint32_t slotWidth(int atype); static inline int slotWidth(JSObject *obj); @@ -323,6 +324,9 @@ struct TypedArray : public BufferView { static int dataOffset(); };
+MOZ_STATIC_ASSERT(js::detail::TypedArrayLengthSlot == TypedArray::LENGTH_SLOT, + "bad inlined constant in jsfriendapi.h"); + inline bool IsTypedArrayClass(const Class *clasp) { @@ -376,6 +380,10 @@ private:
static inline bool is(const Value &v);
+ template <typename NativeType> + static uint8_t * + getDataPointer(JSContext *cx, Handle<DataViewObject*> obj, uint32_t offset); + template<Value ValueGetter(DataViewObject &view)> static bool getterImpl(JSContext *cx, CallArgs args); @@ -459,8 +467,6 @@ private: inline void *dataPointer(); inline bool hasBuffer() const; static JSObject *initClass(JSContext *cx); - static bool getDataPointer(JSContext *cx, Handle<DataViewObject*> obj, - CallArgs args, size_t typeSize, uint8_t **data); template<typename NativeType> static bool read(JSContext *cx, Handle<DataViewObject*> obj, CallArgs &args, NativeType *val, const char *method); diff --git a/js/src/jstypedarrayinlines.h b/js/src/jstypedarrayinlines.h index f26e9cc..6c8bf75 100644 --- a/js/src/jstypedarrayinlines.h +++ b/js/src/jstypedarrayinlines.h @@ -129,6 +129,7 @@ inline void * TypedArray::viewData(JSObject *obj) { JS_ASSERT(obj->isTypedArray()); + // Keep synced with js::Get<Type>ArrayLengthAndData in jsfriendapi.h! return (void *)obj->getPrivate(DATA_SLOT); }
@@ -222,6 +223,13 @@ DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength, RootedObject proto(cx, protoArg); RootedObject obj(cx);
+ // This is overflow-safe: 2 * INT32_MAX is still a valid uint32_t. + if (byteOffset + byteLength > arrayBuffer->byteLength()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); + return nullptr; + + } + NewObjectKind newKind = DataViewNewObjectKind(cx, byteLength, proto); obj = NewBuiltinClassInstance(cx, &class_, newKind); if (!obj) diff --git a/netwerk/base/src/ArrayBufferInputStream.cpp b/netwerk/base/src/ArrayBufferInputStream.cpp index 0daa1c5..16322d9 100644 --- a/netwerk/base/src/ArrayBufferInputStream.cpp +++ b/netwerk/base/src/ArrayBufferInputStream.cpp @@ -88,6 +88,17 @@ ArrayBufferInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure, }
uint32_t remaining = mBufferLength - mPos; + if (!mArrayBuffer.isUndefined()) { + JSObject* buf = &mArrayBuffer.toObject(); + uint32_t byteLength = JS_GetArrayBufferByteLength(buf); + if (byteLength == 0 && remaining != 0) { + mClosed = true; + return NS_BASE_STREAM_CLOSED; + } + } else { + MOZ_ASSERT(remaining == 0, "stream inited incorrectly"); + } + if (!remaining) { *result = 0; return NS_OK; diff --git a/xpcom/io/nsBinaryStream.cpp b/xpcom/io/nsBinaryStream.cpp index 8c2cc23..24b6ab3 100644 --- a/xpcom/io/nsBinaryStream.cpp +++ b/xpcom/io/nsBinaryStream.cpp @@ -17,8 +17,15 @@ * @See nsIBinaryInputStream * @See nsIBinaryOutputStream */ +#include <algorithm> #include <string.h> + #include "nsBinaryStream.h" + +#include "mozilla/Endian.h" +#include "mozilla/PodOperations.h" +#include "mozilla/Scoped.h" + #include "nsCRT.h" #include "prlong.h" #include "nsString.h" @@ -26,11 +33,13 @@ #include "nsIClassInfo.h" #include "nsComponentManagerUtils.h" #include "nsIURI.h" // for NS_IURI_IID -#include "mozilla/Endian.h"
#include "jsapi.h" #include "jsfriendapi.h"
+using mozilla::PodCopy; +using mozilla::ScopedDeleteArray; + NS_IMPL_ISUPPORTS3(nsBinaryOutputStream, nsIObjectOutputStream, nsIBinaryOutputStream, nsIOutputStream)
NS_IMETHODIMP @@ -720,22 +729,49 @@ nsBinaryInputStream::ReadArrayBuffer(uint32_t aLength, const JS::Value& aBuffer, return NS_ERROR_FAILURE; } JS::RootedObject buffer(cx, &aBuffer.toObject()); - if (!JS_IsArrayBufferObject(buffer) || - JS_GetArrayBufferByteLength(buffer) < aLength) { + if (!JS_IsArrayBufferObject(buffer)) { return NS_ERROR_FAILURE; } - uint8_t* data = JS_GetArrayBufferData(&aBuffer.toObject()); - if (!data) { + + uint32_t bufferLength = JS_GetArrayBufferByteLength(buffer); + if (bufferLength < aLength) { return NS_ERROR_FAILURE; }
- uint32_t bytesRead; - nsresult rv = Read(reinterpret_cast<char*>(data), aLength, &bytesRead); - NS_ENSURE_SUCCESS(rv, rv); - if (bytesRead != aLength) { + char* data = reinterpret_cast<char*>(JS_GetArrayBufferData(buffer)); + if (!data) { return NS_ERROR_FAILURE; } - return NS_OK; + + uint32_t bufSize = std::min<uint32_t>(aLength, 4096); + ScopedDeleteArray<char> buf(new char[bufSize]); + + uint32_t remaining = aLength; + do { + // Read data into temporary buffer. + uint32_t bytesRead; + uint32_t amount = std::min(remaining, bufSize); + nsresult rv = Read(buf, amount, &bytesRead); + if (NS_FAILED(rv)) { + return rv; + } + MOZ_ASSERT(bytesRead <= amount); + + if (bytesRead == 0) { + break; + } + + // Copy data into actual buffer. + if (bufferLength != JS_GetArrayBufferByteLength(buffer)) { + return NS_ERROR_FAILURE; + } + PodCopy(data, buf.get(), bytesRead); + + remaining -= bytesRead; + data += bytesRead; + } while (remaining > 0); + + return remaining > 0 ? NS_ERROR_FAILURE : NS_OK; }
NS_IMETHODIMP