diff --git a/browser/base/content/test/general/browser_parsable_css.js b/browser/base/content/test/general/browser_parsable_css.js index 559e25b73812..518dad8afc33 100644 --- a/browser/base/content/test/general/browser_parsable_css.js +++ b/browser/base/content/test/general/browser_parsable_css.js @@ -20,6 +20,10 @@ let whitelist = [ {sourceName: /web\/viewer\.css$/i, errorMessage: /Unknown pseudo-class.*(fullscreen|selection)/i, isFromDevTools: false}, + // PDFjs rules needed for compat with other UAs. + {sourceName: /web\/viewer\.css$/i, + errorMessage: /Unknown property.*appearance/i, + isFromDevTools: false}, // Tracked in bug 1004428. {sourceName: /aboutaccounts\/(main|normalize)\.css$/i, isFromDevTools: false}, diff --git a/browser/extensions/pdfjs/README.mozilla b/browser/extensions/pdfjs/README.mozilla index b849113a5a09..2e82c74f2c6a 100644 --- a/browser/extensions/pdfjs/README.mozilla +++ b/browser/extensions/pdfjs/README.mozilla @@ -1,3 +1,3 @@ This is the pdf.js project output, https://github.com/mozilla/pdf.js -Current extension version is: 1.6.401 +Current extension version is: 1.6.418 diff --git a/browser/extensions/pdfjs/content/PdfJs.jsm b/browser/extensions/pdfjs/content/PdfJs.jsm index c25804cdec48..d67a6c5f8486 100644 --- a/browser/extensions/pdfjs/content/PdfJs.jsm +++ b/browser/extensions/pdfjs/content/PdfJs.jsm @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* jshint esnext:true */ /* globals Components, Services, XPCOMUtils, PdfjsChromeUtils, PdfjsContentUtils, PdfStreamConverter */ diff --git a/browser/extensions/pdfjs/content/PdfJsTelemetry.jsm b/browser/extensions/pdfjs/content/PdfJsTelemetry.jsm index 275da9d87b31..0d34cd50e67c 100644 --- a/browser/extensions/pdfjs/content/PdfJsTelemetry.jsm +++ b/browser/extensions/pdfjs/content/PdfJsTelemetry.jsm @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* jshint esnext:true, maxlen: 100 */ +/* eslint max-len: ["error", 100] */ /* globals Components, Services */ 'use strict'; diff --git a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm index 3b9f9de26788..128d8aad2ab0 100644 --- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm +++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* jshint esnext:true */ /* globals Components, Services, XPCOMUtils, NetUtil, PrivateBrowsingUtils, dump, NetworkManager, PdfJsTelemetry, PdfjsContentUtils */ diff --git a/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm index 57f07af92ccd..2d97206f967e 100644 --- a/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm +++ b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* jshint esnext:true */ /* globals Components, Services, XPCOMUtils */ 'use strict'; diff --git a/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm b/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm index 3dec5f38902d..4318fbfdf96e 100644 --- a/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm +++ b/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* jshint esnext:true */ /* globals Components, Services, XPCOMUtils */ 'use strict'; diff --git a/browser/extensions/pdfjs/content/build/pdf.js b/browser/extensions/pdfjs/content/build/pdf.js index d5b17e9b3359..3183afb10e30 100644 --- a/browser/extensions/pdfjs/content/build/pdf.js +++ b/browser/extensions/pdfjs/content/build/pdf.js @@ -23,8 +23,8 @@ } }(this, function (exports) { 'use strict'; - var pdfjsVersion = '1.6.401'; - var pdfjsBuild = 'b629be05'; + var pdfjsVersion = '1.6.418'; + var pdfjsBuild = '59afb4b9'; var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : null; var pdfjsLibs = {}; (function pdfjsWrapper() { @@ -1808,6 +1808,15 @@ switch (fieldType) { case 'Tx': return new TextWidgetAnnotationElement(parameters); + case 'Btn': + if (parameters.data.radioButton) { + return new RadioButtonWidgetAnnotationElement(parameters); + } else if (parameters.data.checkBox) { + return new CheckboxWidgetAnnotationElement(parameters); + } else { + warn('Unimplemented button widget annotation: pushbutton'); + } + break; case 'Ch': return new ChoiceWidgetAnnotationElement(parameters); } @@ -2075,6 +2084,45 @@ }); return TextWidgetAnnotationElement; }(); + var CheckboxWidgetAnnotationElement = function CheckboxWidgetAnnotationElementClosure() { + function CheckboxWidgetAnnotationElement(parameters) { + WidgetAnnotationElement.call(this, parameters, parameters.renderInteractiveForms); + } + Util.inherit(CheckboxWidgetAnnotationElement, WidgetAnnotationElement, { + render: function CheckboxWidgetAnnotationElement_render() { + this.container.className = 'buttonWidgetAnnotation checkBox'; + var element = document.createElement('input'); + element.disabled = this.data.readOnly; + element.type = 'checkbox'; + if (this.data.fieldValue && this.data.fieldValue !== 'Off') { + element.setAttribute('checked', true); + } + this.container.appendChild(element); + return this.container; + } + }); + return CheckboxWidgetAnnotationElement; + }(); + var RadioButtonWidgetAnnotationElement = function RadioButtonWidgetAnnotationElementClosure() { + function RadioButtonWidgetAnnotationElement(parameters) { + WidgetAnnotationElement.call(this, parameters, parameters.renderInteractiveForms); + } + Util.inherit(RadioButtonWidgetAnnotationElement, WidgetAnnotationElement, { + render: function RadioButtonWidgetAnnotationElement_render() { + this.container.className = 'buttonWidgetAnnotation radioButton'; + var element = document.createElement('input'); + element.disabled = this.data.readOnly; + element.type = 'radio'; + element.name = this.data.fieldName; + if (this.data.fieldValue === this.data.buttonValue) { + element.setAttribute('checked', true); + } + this.container.appendChild(element); + return this.container; + } + }); + return RadioButtonWidgetAnnotationElement; + }(); var ChoiceWidgetAnnotationElement = function ChoiceWidgetAnnotationElementClosure() { function ChoiceWidgetAnnotationElement(parameters) { WidgetAnnotationElement.call(this, parameters, parameters.renderInteractiveForms); diff --git a/browser/extensions/pdfjs/content/build/pdf.worker.js b/browser/extensions/pdfjs/content/build/pdf.worker.js index 14746010d357..53587e11b279 100644 --- a/browser/extensions/pdfjs/content/build/pdf.worker.js +++ b/browser/extensions/pdfjs/content/build/pdf.worker.js @@ -23,8 +23,8 @@ } }(this, function (exports) { 'use strict'; - var pdfjsVersion = '1.6.401'; - var pdfjsBuild = 'b629be05'; + var pdfjsVersion = '1.6.418'; + var pdfjsBuild = '59afb4b9'; var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : null; var pdfjsLibs = {}; (function pdfjsWrapper() { @@ -45245,10 +45245,10 @@ } if (!font.vertical) { textChunk.lastAdvanceWidth = width; - textChunk.width += width * textChunk.textAdvanceScale; + textChunk.width += width; } else { textChunk.lastAdvanceHeight = height; - textChunk.height += Math.abs(height * textChunk.textAdvanceScale); + textChunk.height += Math.abs(height); } return textChunk; } @@ -45269,6 +45269,8 @@ if (!textContentItem.initialized) { return; } + textContentItem.width *= textContentItem.textAdvanceScale; + textContentItem.height *= textContentItem.textAdvanceScale; textContent.items.push(runBidiTransform(textContentItem)); textContentItem.initialized = false; textContentItem.str.length = 0; @@ -45381,16 +45383,16 @@ advance = items[j] * textState.fontSize / 1000; var breakTextRun = false; if (textState.font.vertical) { - offset = advance * (textState.textHScale * textState.textMatrix[2] + textState.textMatrix[3]); - textState.translateTextMatrix(0, advance); + offset = advance; + textState.translateTextMatrix(0, offset); breakTextRun = textContentItem.textRunBreakAllowed && advance > textContentItem.fakeMultiSpaceMax; if (!breakTextRun) { textContentItem.height += offset; } } else { advance = -advance; - offset = advance * (textState.textHScale * textState.textMatrix[0] + textState.textMatrix[1]); - textState.translateTextMatrix(advance, 0); + offset = advance * textState.textHScale; + textState.translateTextMatrix(offset, 0); breakTextRun = textContentItem.textRunBreakAllowed && advance > textContentItem.fakeMultiSpaceMax; if (!breakTextRun) { textContentItem.width += offset; @@ -45884,7 +45886,16 @@ } else if (isRef(entry)) { hash.update(entry.toString()); } else if (isArray(entry)) { - hash.update(entry.length.toString()); + var diffLength = entry.length, diffBuf = new Array(diffLength); + for (var j = 0; j < diffLength; j++) { + var diffEntry = entry[j]; + if (isName(diffEntry)) { + diffBuf[j] = diffEntry.name; + } else if (isNum(diffEntry) || isRef(diffEntry)) { + diffBuf[j] = diffEntry.toString(); + } + } + hash.update(diffBuf.join()); } } } @@ -47160,6 +47171,8 @@ switch (fieldType) { case 'Tx': return new TextWidgetAnnotation(parameters); + case 'Btn': + return new ButtonWidgetAnnotation(parameters); case 'Ch': return new ChoiceWidgetAnnotation(parameters); } @@ -47587,17 +47600,72 @@ }); return TextWidgetAnnotation; }(); + var ButtonWidgetAnnotation = function ButtonWidgetAnnotationClosure() { + function ButtonWidgetAnnotation(params) { + WidgetAnnotation.call(this, params); + this.data.checkBox = !this.hasFieldFlag(AnnotationFieldFlag.RADIO) && !this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON); + if (this.data.checkBox) { + if (!isName(this.data.fieldValue)) { + return; + } + this.data.fieldValue = this.data.fieldValue.name; + } + this.data.radioButton = this.hasFieldFlag(AnnotationFieldFlag.RADIO) && !this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON); + if (this.data.radioButton) { + this.data.fieldValue = this.data.buttonValue = null; + var fieldParent = params.dict.get('Parent'); + if (!isDict(fieldParent) || !fieldParent.has('V')) { + return; + } + var fieldParentValue = fieldParent.get('V'); + if (!isName(fieldParentValue)) { + return; + } + this.data.fieldValue = fieldParentValue.name; + var appearanceStates = params.dict.get('AP'); + if (!isDict(appearanceStates)) { + return; + } + var normalAppearanceState = appearanceStates.get('N'); + if (!isDict(normalAppearanceState)) { + return; + } + var keys = normalAppearanceState.getKeys(); + for (var i = 0, ii = keys.length; i < ii; i++) { + if (keys[i] !== 'Off') { + this.data.buttonValue = keys[i]; + break; + } + } + } + } + Util.inherit(ButtonWidgetAnnotation, WidgetAnnotation, { + getOperatorList: function ButtonWidgetAnnotation_getOperatorList(evaluator, task, renderForms) { + var operatorList = new OperatorList(); + if (renderForms) { + return Promise.resolve(operatorList); + } + if (this.appearance) { + return Annotation.prototype.getOperatorList.call(this, evaluator, task, renderForms); + } + return Promise.resolve(operatorList); + } + }); + return ButtonWidgetAnnotation; + }(); var ChoiceWidgetAnnotation = function ChoiceWidgetAnnotationClosure() { function ChoiceWidgetAnnotation(params) { WidgetAnnotation.call(this, params); this.data.options = []; - var options = params.dict.getArray('Opt'); + var options = params.dict.get('Opt'); if (isArray(options)) { + var xref = params.xref; for (var i = 0, ii = options.length; i < ii; i++) { - var option = options[i]; + var option = xref.fetchIfRef(options[i]); + var isOptionArray = isArray(option); this.data.options[i] = { - exportValue: isArray(option) ? option[0] : option, - displayValue: isArray(option) ? option[1] : option + exportValue: isOptionArray ? xref.fetchIfRef(option[0]) : option, + displayValue: isOptionArray ? xref.fetchIfRef(option[1]) : option }; } } diff --git a/browser/extensions/pdfjs/content/pdfjschildbootstrap.js b/browser/extensions/pdfjs/content/pdfjschildbootstrap.js index 1f44b9c2e8d8..2051e3055d56 100644 --- a/browser/extensions/pdfjs/content/pdfjschildbootstrap.js +++ b/browser/extensions/pdfjs/content/pdfjschildbootstrap.js @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* jshint esnext:true */ /* globals Components, PdfjsContentUtils, PdfJs, Services */ 'use strict'; diff --git a/browser/extensions/pdfjs/content/web/l10n.js b/browser/extensions/pdfjs/content/web/l10n.js index 04346ee1d650..e45cf200a5b9 100644 --- a/browser/extensions/pdfjs/content/web/l10n.js +++ b/browser/extensions/pdfjs/content/web/l10n.js @@ -1,4 +1,3 @@ - 'use strict'; // Small subset of the webL10n API by Fabien Cazenave for pdf.js extension. diff --git a/browser/extensions/pdfjs/content/web/viewer.css b/browser/extensions/pdfjs/content/web/viewer.css index cafa90738577..158842ac69fe 100644 --- a/browser/extensions/pdfjs/content/web/viewer.css +++ b/browser/extensions/pdfjs/content/web/viewer.css @@ -103,7 +103,9 @@ .annotationLayer .textWidgetAnnotation input, .annotationLayer .textWidgetAnnotation textarea, -.annotationLayer .choiceWidgetAnnotation select { +.annotationLayer .choiceWidgetAnnotation select, +.annotationLayer .buttonWidgetAnnotation.checkBox input, +.annotationLayer .buttonWidgetAnnotation.radioButton input { background-color: rgba(0, 54, 255, 0.13); border: 1px solid transparent; box-sizing: border-box; @@ -122,7 +124,9 @@ .annotationLayer .textWidgetAnnotation input[disabled], .annotationLayer .textWidgetAnnotation textarea[disabled], -.annotationLayer .choiceWidgetAnnotation select[disabled] { +.annotationLayer .choiceWidgetAnnotation select[disabled], +.annotationLayer .buttonWidgetAnnotation.checkBox input[disabled], +.annotationLayer .buttonWidgetAnnotation.radioButton input[disabled] { background: none; border: 1px solid transparent; cursor: not-allowed; @@ -130,7 +134,9 @@ .annotationLayer .textWidgetAnnotation input:hover, .annotationLayer .textWidgetAnnotation textarea:hover, -.annotationLayer .choiceWidgetAnnotation select:hover { +.annotationLayer .choiceWidgetAnnotation select:hover, +.annotationLayer .buttonWidgetAnnotation.checkBox input:hover, +.annotationLayer .buttonWidgetAnnotation.radioButton input:hover { border: 1px solid #000; } @@ -157,6 +163,12 @@ width: 115%; } +.annotationLayer .buttonWidgetAnnotation.checkBox input, +.annotationLayer .buttonWidgetAnnotation.radioButton input { + -moz-appearance: none; + appearance: none; +} + .annotationLayer .popupWrapper { position: absolute; width: 20em; diff --git a/browser/extensions/pdfjs/content/web/viewer.js b/browser/extensions/pdfjs/content/web/viewer.js index f853da16b7b9..1ee979322a8a 100644 --- a/browser/extensions/pdfjs/content/web/viewer.js +++ b/browser/extensions/pdfjs/content/web/viewer.js @@ -4119,7 +4119,7 @@ var pdfjsWebLibs; } if (error === 'cancelled') { self.error = null; - return; + return Promise.resolve(undefined); } self.renderingState = RenderingStates.FINISHED; if (self.loadingIconDiv) { @@ -4145,21 +4145,25 @@ var pdfjsWebLibs; pageNumber: self.id, cssTransform: false }); + if (error) { + return Promise.reject(error); + } + return Promise.resolve(undefined); }; var paintTask = this.renderer === RendererType.SVG ? this.paintOnSvg(canvasWrapper) : this.paintOnCanvas(canvasWrapper); paintTask.onRenderContinue = renderContinueCallback; this.paintTask = paintTask; var resultPromise = paintTask.promise.then(function () { - finishPaintTask(null); - if (textLayer) { - pdfPage.getTextContent({ normalizeWhitespace: true }).then(function textContentResolved(textContent) { - textLayer.setTextContent(textContent); - textLayer.render(TEXT_LAYER_RENDER_DELAY); - }); - } + return finishPaintTask(null).then(function () { + if (textLayer) { + pdfPage.getTextContent({ normalizeWhitespace: true }).then(function textContentResolved(textContent) { + textLayer.setTextContent(textContent); + textLayer.render(TEXT_LAYER_RENDER_DELAY); + }); + } + }); }, function (reason) { - finishPaintTask(reason); - throw reason; + return finishPaintTask(reason); }); if (this.annotationLayerFactory) { if (!this.annotationLayer) { diff --git a/build/build-clang/clang-tidy-linux64.json b/build/build-clang/clang-tidy-linux64.json new file mode 100644 index 000000000000..626a4259dabb --- /dev/null +++ b/build/build-clang/clang-tidy-linux64.json @@ -0,0 +1,18 @@ +{ + "llvm_revision": "290055", + "stages": "1", + "build_libcxx": true, + "build_type": "Release", + "assertions": false, + "import_clang_tidy": true, + "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk", + "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk", + "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/trunk", + "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk", + "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk", + "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/trunk", + "python_path": "/usr/bin/python2.7", + "gcc_dir": "/home/worker/workspace/build/src/gcc", + "cc": "/home/worker/workspace/build/src/gcc/bin/gcc", + "cxx": "/home/worker/workspace/build/src/gcc/bin/g++" +} diff --git a/dom/canvas/WebGL2Context.cpp b/dom/canvas/WebGL2Context.cpp index 9c659ca7da5d..f9a1eba4bff9 100644 --- a/dom/canvas/WebGL2Context.cpp +++ b/dom/canvas/WebGL2Context.cpp @@ -163,6 +163,8 @@ WebGLContext::InitWebGL2(FailureReason* const out_failReason) mDefaultTransformFeedback = new WebGLTransformFeedback(this, 0); mBoundTransformFeedback = mDefaultTransformFeedback; + gl->fGenTransformFeedbacks(1, &mEmptyTFO); + //// if (!gl->IsGLES()) { diff --git a/dom/canvas/WebGL2ContextBuffers.cpp b/dom/canvas/WebGL2ContextBuffers.cpp index efa18b42bc78..f59fa08b836e 100644 --- a/dom/canvas/WebGL2ContextBuffers.cpp +++ b/dom/canvas/WebGL2ContextBuffers.cpp @@ -132,11 +132,26 @@ WebGL2Context::GetBufferSubData(GLenum target, GLintptr srcByteOffset, const ScopedLazyBind readBind(gl, target, buffer); if (byteLen) { - const auto mappedBytes = gl->fMapBufferRange(target, srcByteOffset, glByteLen, + const bool isTF = (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER); + GLenum mapTarget = target; + if (isTF) { + gl->fBindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, mEmptyTFO); + gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, buffer->mGLName); + mapTarget = LOCAL_GL_ARRAY_BUFFER; + } + + const auto mappedBytes = gl->fMapBufferRange(mapTarget, srcByteOffset, glByteLen, LOCAL_GL_MAP_READ_BIT); - // Warning: Possibly shared memory. See bug 1225033. memcpy(bytes, mappedBytes, byteLen); - gl->fUnmapBuffer(target); + gl->fUnmapBuffer(mapTarget); + + if (isTF) { + const GLuint vbo = (mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0); + gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, vbo); + const GLuint tfo = (mBoundTransformFeedback ? mBoundTransformFeedback->mGLName + : 0); + gl->fBindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, tfo); + } } } diff --git a/dom/canvas/WebGL2ContextTransformFeedback.cpp b/dom/canvas/WebGL2ContextTransformFeedback.cpp index ee2f99341535..48bdf756c97c 100644 --- a/dom/canvas/WebGL2ContextTransformFeedback.cpp +++ b/dom/canvas/WebGL2ContextTransformFeedback.cpp @@ -81,10 +81,18 @@ WebGL2Context::BindTransformFeedback(GLenum target, WebGLTransformFeedback* tf) //// + if (mBoundTransformFeedback) { + mBoundTransformFeedback->AddBufferBindCounts(-1); + } + mBoundTransformFeedback = (tf ? tf : mDefaultTransformFeedback); MakeContextCurrent(); gl->fBindTransformFeedback(target, mBoundTransformFeedback->mGLName); + + if (mBoundTransformFeedback) { + mBoundTransformFeedback->AddBufferBindCounts(+1); + } } void diff --git a/dom/canvas/WebGLBuffer.cpp b/dom/canvas/WebGLBuffer.cpp index 4558a4603f9e..f202c9950d1c 100644 --- a/dom/canvas/WebGLBuffer.cpp +++ b/dom/canvas/WebGLBuffer.cpp @@ -18,6 +18,8 @@ WebGLBuffer::WebGLBuffer(WebGLContext* webgl, GLuint buf) , mContent(Kind::Undefined) , mUsage(LOCAL_GL_STATIC_DRAW) , mByteLength(0) + , mTFBindCount(0) + , mNonTFBindCount(0) { mContext->mBuffers.insertBack(this); } diff --git a/dom/canvas/WebGLBuffer.h b/dom/canvas/WebGLBuffer.h index c60bc9b5d81f..883712aad451 100644 --- a/dom/canvas/WebGLBuffer.h +++ b/dom/canvas/WebGLBuffer.h @@ -26,7 +26,6 @@ class WebGLBuffer final friend class WebGLContext; friend class WebGL2Context; friend class WebGLTexture; - friend class WebGLTransformFeedback; public: enum class Kind { @@ -66,6 +65,35 @@ public: bool ValidateCanBindToTarget(const char* funcName, GLenum target); void BufferData(GLenum target, size_t size, const void* data, GLenum usage); + //// + + static void AddBindCount(GLenum target, WebGLBuffer* buffer, int8_t addVal) { + if (!buffer) + return; + + if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER) { + MOZ_ASSERT_IF(addVal < 0, buffer->mTFBindCount >= size_t(-addVal)); + buffer->mTFBindCount += addVal; + } else { + MOZ_ASSERT_IF(addVal < 0, buffer->mNonTFBindCount >= size_t(-addVal)); + buffer->mNonTFBindCount += addVal; + } + } + + static void SetSlot(GLenum target, WebGLBuffer* newBuffer, + WebGLRefPtr* const out_slot) + { + WebGLBuffer* const oldBuffer = *out_slot; + AddBindCount(target, oldBuffer, -1); + AddBindCount(target, newBuffer, +1); + *out_slot = newBuffer; + } + + bool IsBoundForTF() const { return bool(mTFBindCount); } + bool IsBoundForNonTF() const { return bool(mNonTFBindCount); } + + //// + const GLenum mGLName; NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLBuffer) @@ -78,6 +106,8 @@ protected: GLenum mUsage; size_t mByteLength; UniquePtr mCache; + size_t mTFBindCount; + size_t mNonTFBindCount; }; } // namespace mozilla diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp index 3c0fd978c395..3d8ae73699fd 100644 --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -120,7 +120,7 @@ WebGLContext::WebGLContext() , mMaxFetchedInstances(0) , mLayerIsMirror(false) , mBypassShaderValidation(false) - , mBuffersForUB_Dirty(true) + , mEmptyTFO(0) , mContextLossHandler(this) , mNeedsFakeNoAlpha(false) , mNeedsFakeNoDepth(false) @@ -250,7 +250,6 @@ WebGLContext::DestroyResourcesAndContext() mQuerySlot_TimeElapsed = nullptr; mIndexedUniformBufferBindings.clear(); - OnUBIndexedBindingsChanged(); ////// @@ -268,6 +267,13 @@ WebGLContext::DestroyResourcesAndContext() ////// + if (mEmptyTFO) { + gl->fDeleteTransformFeedbacks(1, &mEmptyTFO); + mEmptyTFO = 0; + } + + ////// + mFakeBlack_2D_0000 = nullptr; mFakeBlack_2D_0001 = nullptr; mFakeBlack_CubeMap_0000 = nullptr; @@ -2385,42 +2391,6 @@ WebGLContext::ValidateArrayBufferView(const char* funcName, return true; } -//// - -const decltype(WebGLContext::mBuffersForUB)& -WebGLContext::BuffersForUB() const -{ - if (mBuffersForUB_Dirty) { - mBuffersForUB.clear(); - for (const auto& cur : mIndexedUniformBufferBindings) { - if (cur.mBufferBinding) { - mBuffersForUB.insert(cur.mBufferBinding.get()); - } - } - mBuffersForUB_Dirty = false; - } - return mBuffersForUB; -} - -//// - -bool -WebGLContext::ValidateForNonTransformFeedback(const char* funcName, WebGLBuffer* buffer) -{ - if (!mBoundTransformFeedback) - return true; - - const auto& buffersForTF = mBoundTransformFeedback->BuffersForTF(); - if (buffersForTF.count(buffer)) { - ErrorInvalidOperation("%s: Specified WebGLBuffer is currently bound for transform" - " feedback.", - funcName); - return false; - } - - return true; -} - //////////////////////////////////////////////////////////////////////////////// // XPCOM goop diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h index 2b657f95a26b..0648c52c4d9f 100644 --- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -1654,8 +1654,6 @@ protected: return true; } - bool ValidateForNonTransformFeedback(const char* funcName, WebGLBuffer* buffer); - public: template bool ValidateNonNull(const char* funcName, const dom::Nullable& maybe) { @@ -1882,17 +1880,9 @@ protected: //////////////////////////////////// -private: - mutable bool mBuffersForUB_Dirty; - mutable std::set mBuffersForUB; - -public: - void OnUBIndexedBindingsChanged() const { mBuffersForUB_Dirty = true; } - const decltype(mBuffersForUB)& BuffersForUB() const; - - //////////////////////////////////// - protected: + GLuint mEmptyTFO; + // Generic Vertex Attributes // Though CURRENT_VERTEX_ATTRIB is listed under "Vertex Shader State" in the spec // state tables, this isn't vertex shader /object/ state. This array is merely state diff --git a/dom/canvas/WebGLContextBuffers.cpp b/dom/canvas/WebGLContextBuffers.cpp index 9fd108632abf..af506c01c1be 100644 --- a/dom/canvas/WebGLContextBuffers.cpp +++ b/dom/canvas/WebGLContextBuffers.cpp @@ -75,8 +75,27 @@ WebGLContext::ValidateBufferSelection(const char* funcName, GLenum target) return nullptr; } - if (!ValidateForNonTransformFeedback(funcName, buffer.get())) - return nullptr; + if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER) { + if (mBoundTransformFeedback->IsActiveAndNotPaused()) { + ErrorInvalidOperation("%s: Cannot select TRANSFORM_FEEDBACK_BUFFER when" + " transform feedback is active and unpaused.", + funcName); + return nullptr; + } + if (buffer->IsBoundForNonTF()) { + ErrorInvalidOperation("%s: Specified WebGLBuffer is currently bound for" + " non-transform-feedback.", + funcName); + return nullptr; + } + } else { + if (buffer->IsBoundForTF()) { + ErrorInvalidOperation("%s: Specified WebGLBuffer is currently bound for" + " transform feedback.", + funcName); + return nullptr; + } + } return buffer.get(); } @@ -132,7 +151,7 @@ WebGLContext::BindBuffer(GLenum target, WebGLBuffer* buffer) gl->MakeCurrent(); gl->fBindBuffer(target, buffer ? buffer->mGLName : 0); - *slot = buffer; + WebGLBuffer::SetSlot(target, buffer, slot); if (buffer) { buffer->SetContentAfterBind(target); } @@ -201,23 +220,14 @@ WebGLContext::BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer) //// - *genericBinding = buffer; - indexedBinding->mBufferBinding = buffer; + WebGLBuffer::SetSlot(target, buffer, genericBinding); + WebGLBuffer::SetSlot(target, buffer, &indexedBinding->mBufferBinding); indexedBinding->mRangeStart = 0; indexedBinding->mRangeSize = 0; if (buffer) { buffer->SetContentAfterBind(target); } - - switch (target) { - case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER: - mBoundTransformFeedback->OnIndexedBindingsChanged(); - break; - case LOCAL_GL_UNIFORM: - OnUBIndexedBindingsChanged(); - break; - } } void @@ -296,23 +306,14 @@ WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer, //// - *genericBinding = buffer; - indexedBinding->mBufferBinding = buffer; + WebGLBuffer::SetSlot(target, buffer, genericBinding); + WebGLBuffer::SetSlot(target, buffer, &indexedBinding->mBufferBinding); indexedBinding->mRangeStart = offset; indexedBinding->mRangeSize = size; if (buffer) { buffer->SetContentAfterBind(target); } - - switch (target) { - case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER: - mBoundTransformFeedback->OnIndexedBindingsChanged(); - break; - case LOCAL_GL_UNIFORM: - OnUBIndexedBindingsChanged(); - break; - } } //////////////////////////////////////// @@ -477,39 +478,41 @@ WebGLContext::DeleteBuffer(WebGLBuffer* buffer) //// - const auto fnClearIfBuffer = [&](WebGLRefPtr& bindPoint) { + const auto fnClearIfBuffer = [&](GLenum target, WebGLRefPtr& bindPoint) { if (bindPoint == buffer) { - bindPoint = nullptr; + WebGLBuffer::SetSlot(target, nullptr, &bindPoint); } }; - fnClearIfBuffer(mBoundArrayBuffer); - fnClearIfBuffer(mBoundVertexArray->mElementArrayBuffer); + fnClearIfBuffer(0, mBoundArrayBuffer); + fnClearIfBuffer(0, mBoundVertexArray->mElementArrayBuffer); + + for (auto& cur : mBoundVertexArray->mAttribs) { + fnClearIfBuffer(0, cur.mBuf); + } // WebGL binding points if (IsWebGL2()) { - fnClearIfBuffer(mBoundCopyReadBuffer); - fnClearIfBuffer(mBoundCopyWriteBuffer); - fnClearIfBuffer(mBoundPixelPackBuffer); - fnClearIfBuffer(mBoundPixelUnpackBuffer); - fnClearIfBuffer(mBoundUniformBuffer); - fnClearIfBuffer(mBoundTransformFeedback->mGenericBufferBinding); + fnClearIfBuffer(0, mBoundCopyReadBuffer); + fnClearIfBuffer(0, mBoundCopyWriteBuffer); + fnClearIfBuffer(0, mBoundPixelPackBuffer); + fnClearIfBuffer(0, mBoundPixelUnpackBuffer); + fnClearIfBuffer(0, mBoundUniformBuffer); + fnClearIfBuffer(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER, + mBoundTransformFeedback->mGenericBufferBinding); if (!mBoundTransformFeedback->mIsActive) { for (auto& binding : mBoundTransformFeedback->mIndexedBindings) { - fnClearIfBuffer(binding.mBufferBinding); + fnClearIfBuffer(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER, + binding.mBufferBinding); } } for (auto& binding : mIndexedUniformBufferBindings) { - fnClearIfBuffer(binding.mBufferBinding); + fnClearIfBuffer(0, binding.mBufferBinding); } } - for (auto& cur : mBoundVertexArray->mAttribs) { - fnClearIfBuffer(cur.mBuf); - } - //// buffer->RequestDelete(); diff --git a/dom/canvas/WebGLContextDraw.cpp b/dom/canvas/WebGLContextDraw.cpp index f895fdced0bf..04cd6bfcac46 100644 --- a/dom/canvas/WebGLContextDraw.cpp +++ b/dom/canvas/WebGLContextDraw.cpp @@ -356,6 +356,7 @@ public: // Check UBO sizes. const auto& linkInfo = mWebGL->mActiveProgramLinkInfo; + for (const auto& cur : linkInfo->uniformBlocks) { const auto& dataSize = cur->mDataSize; const auto& binding = cur->mBinding; @@ -374,18 +375,10 @@ public: *out_error = true; return; } - } - //// - - const auto& tfo = mWebGL->mBoundTransformFeedback; - if (tfo) { - const auto& buffersForTF = tfo->BuffersForTF(); - const auto& buffersForUB = mWebGL->BuffersForUB(); - if (DoSetsIntersect(buffersForTF, buffersForUB)) { - mWebGL->ErrorInvalidOperation("%s: At least one WebGLBuffer is bound for" - " both transform feedback and as a uniform" - " buffer.", + if (binding->mBufferBinding->IsBoundForTF()) { + mWebGL->ErrorInvalidOperation("%s: Buffer for uniform block is bound or" + " in use for transform feedback.", funcName); *out_error = true; return; @@ -394,7 +387,38 @@ public: //// - for (const auto& progAttrib : mWebGL->mActiveProgramLinkInfo->attribs) { + const auto& tfo = mWebGL->mBoundTransformFeedback; + if (tfo && tfo->IsActiveAndNotPaused()) { + uint32_t numUsed; + switch (linkInfo->transformFeedbackBufferMode) { + case LOCAL_GL_INTERLEAVED_ATTRIBS: + numUsed = 1; + break; + + case LOCAL_GL_SEPARATE_ATTRIBS: + numUsed = linkInfo->transformFeedbackVaryings.size(); + break; + + default: + MOZ_CRASH(); + } + + for (uint32_t i = 0; i < numUsed; ++i) { + const auto& buffer = tfo->mIndexedBindings[i].mBufferBinding; + if (buffer->IsBoundForNonTF()) { + mWebGL->ErrorInvalidOperation("%s: Transform feedback varying %u's" + " buffer is bound for" + " non-transform-feedback.", + funcName, i); + *out_error = true; + return; + } + } + } + + //// + + for (const auto& progAttrib : linkInfo->attribs) { const auto& loc = progAttrib.mLoc; if (loc == -1) continue; @@ -404,6 +428,14 @@ public: GLenum attribDataBaseType; if (attribData.mEnabled) { attribDataBaseType = attribData.BaseType(); + + if (attribData.mBuf->IsBoundForTF()) { + mWebGL->ErrorInvalidOperation("%s: Vertex attrib %u's buffer is bound" + " or in use for transform feedback.", + funcName, loc); + *out_error = true; + return; + } } else { attribDataBaseType = mWebGL->mGenericVertexAttribTypes[loc]; } diff --git a/dom/canvas/WebGLContextGL.cpp b/dom/canvas/WebGLContextGL.cpp index 25a54445ca95..dfd52184f869 100644 --- a/dom/canvas/WebGLContextGL.cpp +++ b/dom/canvas/WebGLContextGL.cpp @@ -592,12 +592,19 @@ WebGLContext::GetAttribLocation(const WebGLProgram& prog, const nsAString& name) JS::Value WebGLContext::GetBufferParameter(GLenum target, GLenum pname) { + const char funcName[] = "getBufferParameter"; if (IsContextLost()) return JS::NullValue(); - const auto& buffer = ValidateBufferSelection("getBufferParameter", target); - if (!buffer) + const auto& slot = ValidateBufferSlot(funcName, target); + if (!slot) return JS::NullValue(); + const auto& buffer = *slot; + + if (!buffer) { + ErrorInvalidOperation("%s: Buffer for `target` is null.", funcName); + return JS::NullValue(); + } switch (pname) { case LOCAL_GL_BUFFER_SIZE: diff --git a/dom/canvas/WebGLContextVertexArray.cpp b/dom/canvas/WebGLContextVertexArray.cpp index e9f16d3f75e2..5b7a10f220c7 100644 --- a/dom/canvas/WebGLContextVertexArray.cpp +++ b/dom/canvas/WebGLContextVertexArray.cpp @@ -25,6 +25,10 @@ WebGLContext::BindVertexArray(WebGLVertexArray* array) MakeContextCurrent(); + if (mBoundVertexArray) { + mBoundVertexArray->AddBufferBindCounts(-1); + } + if (array == nullptr) { array = mDefaultVertexArray; } @@ -32,6 +36,9 @@ WebGLContext::BindVertexArray(WebGLVertexArray* array) array->BindVertexArray(); MOZ_ASSERT(mBoundVertexArray == array); + if (mBoundVertexArray) { + mBoundVertexArray->AddBufferBindCounts(+1); + } } already_AddRefed diff --git a/dom/canvas/WebGLProgram.cpp b/dom/canvas/WebGLProgram.cpp index 01c7f8d617e9..73567b8c4ed7 100644 --- a/dom/canvas/WebGLProgram.cpp +++ b/dom/canvas/WebGLProgram.cpp @@ -435,6 +435,7 @@ QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl) webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog) : prog(prog) + , transformFeedbackBufferMode(prog->mNextLink_TransformFeedbackBufferMode) { } webgl::LinkedProgramInfo::~LinkedProgramInfo() @@ -697,21 +698,35 @@ WebGLProgram::GetProgramParameter(GLenum pname) const if (mContext->IsWebGL2()) { switch (pname) { case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS: - return JS::Int32Value(GetProgramiv(gl, mGLName, pname)); + if (!IsLinked()) + return JS::NumberValue(0); + return JS::NumberValue(LinkInfo()->uniformBlocks.size()); case LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS: - return JS::Int32Value(mNextLink_TransformFeedbackVaryings.size()); + if (!IsLinked()) + return JS::NumberValue(0); + return JS::NumberValue(LinkInfo()->transformFeedbackVaryings.size()); case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_MODE: - return JS::Int32Value(mNextLink_TransformFeedbackBufferMode); + if (!IsLinked()) + return JS::NumberValue(LOCAL_GL_INTERLEAVED_ATTRIBS); + return JS::NumberValue(LinkInfo()->transformFeedbackBufferMode); } } switch (pname) { case LOCAL_GL_ATTACHED_SHADERS: + return JS::NumberValue( int(bool(mVertShader.get())) + int(bool(mFragShader)) ); + case LOCAL_GL_ACTIVE_UNIFORMS: + if (!IsLinked()) + return JS::NumberValue(0); + return JS::NumberValue(LinkInfo()->uniforms.size()); + case LOCAL_GL_ACTIVE_ATTRIBUTES: - return JS::Int32Value(GetProgramiv(gl, mGLName, pname)); + if (!IsLinked()) + return JS::NumberValue(0); + return JS::NumberValue(LinkInfo()->attribs.size()); case LOCAL_GL_DELETE_STATUS: return JS::BooleanValue(IsDeleteRequested()); @@ -725,6 +740,7 @@ WebGLProgram::GetProgramParameter(GLenum pname) const if (gl->WorkAroundDriverBugs()) return JS::BooleanValue(true); #endif + // Todo: Implement this in our code. return JS::BooleanValue(bool(GetProgramiv(gl, mGLName, pname))); default: diff --git a/dom/canvas/WebGLProgram.h b/dom/canvas/WebGLProgram.h index 14a6ba86b2fa..0f08d3df3eb9 100644 --- a/dom/canvas/WebGLProgram.h +++ b/dom/canvas/WebGLProgram.h @@ -87,6 +87,7 @@ struct LinkedProgramInfo final ////// WebGLProgram* const prog; + const GLenum transformFeedbackBufferMode; std::vector attribs; std::vector uniforms; // Owns its contents. diff --git a/dom/canvas/WebGLTransformFeedback.cpp b/dom/canvas/WebGLTransformFeedback.cpp index 6e5c0b745444..feec581eaaef 100644 --- a/dom/canvas/WebGLTransformFeedback.cpp +++ b/dom/canvas/WebGLTransformFeedback.cpp @@ -17,7 +17,6 @@ WebGLTransformFeedback::WebGLTransformFeedback(WebGLContext* webgl, GLuint tf) , mIndexedBindings(webgl->mGLMaxTransformFeedbackSeparateAttribs) , mIsPaused(false) , mIsActive(false) - , mBuffersForTF_Dirty(true) { mContext->mTransformFeedbacks.insertBack(this); } @@ -37,28 +36,6 @@ WebGLTransformFeedback::Delete() removeFrom(mContext->mTransformFeedbacks); } -//// - -const decltype(WebGLTransformFeedback::mBuffersForTF)& -WebGLTransformFeedback::BuffersForTF() const -{ - // The generic bind point cannot incur undefined read/writes because otherwise it - // would be impossible to read back from this. The spec implies that readback from - // the TRANSFORM_FEEDBACK target is possible, just not simultaneously with being - // "bound or in use for transform feedback". - // Therefore, only the indexed bindings of the TFO count. - if (mBuffersForTF_Dirty) { - mBuffersForTF.clear(); - for (const auto& cur : mIndexedBindings) { - if (cur.mBufferBinding) { - mBuffersForTF.insert(cur.mBufferBinding.get()); - } - } - mBuffersForTF_Dirty = false; - } - return mBuffersForTF; -} - //////////////////////////////////////// void @@ -209,6 +186,18 @@ WebGLTransformFeedback::ResumeTransformFeedback() //////////////////////////////////////// +void +WebGLTransformFeedback::AddBufferBindCounts(int8_t addVal) const +{ + const GLenum target = LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER; + WebGLBuffer::AddBindCount(target, mGenericBufferBinding.get(), addVal); + for (const auto& binding : mIndexedBindings) { + WebGLBuffer::AddBindCount(target, binding.mBufferBinding.get(), addVal); + } +} + +//////////////////////////////////////// + JSObject* WebGLTransformFeedback::WrapObject(JSContext* cx, JS::Handle givenProto) { diff --git a/dom/canvas/WebGLTransformFeedback.h b/dom/canvas/WebGLTransformFeedback.h index d97484a99b47..aa57372a5956 100644 --- a/dom/canvas/WebGLTransformFeedback.h +++ b/dom/canvas/WebGLTransformFeedback.h @@ -17,6 +17,7 @@ class WebGLTransformFeedback final , public WebGLRefCountedObject , public LinkedListElement { + friend class ScopedDrawHelper; friend class ScopedDrawWithTransformFeedback; friend class WebGLContext; friend class WebGL2Context; @@ -36,9 +37,6 @@ private: MOZ_INIT_OUTSIDE_CTOR size_t mActive_VertPosition; MOZ_INIT_OUTSIDE_CTOR size_t mActive_VertCapacity; - mutable bool mBuffersForTF_Dirty; - mutable std::set mBuffersForTF; - public: WebGLTransformFeedback(WebGLContext* webgl, GLuint tf); private: @@ -52,10 +50,9 @@ public: WebGLContext* GetParentObject() const { return mContext; } virtual JSObject* WrapObject(JSContext*, JS::Handle) override; - //// + bool IsActiveAndNotPaused() const { return mIsActive && !mIsPaused; } - void OnIndexedBindingsChanged() const { mBuffersForTF_Dirty = true; } - const decltype(mBuffersForTF)& BuffersForTF() const; + void AddBufferBindCounts(int8_t addVal) const; // GL Funcs void BeginTransformFeedback(GLenum primMode); diff --git a/dom/canvas/WebGLVertexArray.cpp b/dom/canvas/WebGLVertexArray.cpp index 180135b6880b..e0c30ab51eb9 100644 --- a/dom/canvas/WebGLVertexArray.cpp +++ b/dom/canvas/WebGLVertexArray.cpp @@ -28,6 +28,21 @@ WebGLVertexArray::WebGLVertexArray(WebGLContext* webgl) mContext->mVertexArrays.insertBack(this); } +WebGLVertexArray::~WebGLVertexArray() +{ + MOZ_ASSERT(IsDeleted()); +} + +void +WebGLVertexArray::AddBufferBindCounts(int8_t addVal) const +{ + const GLenum target = 0; // Anything non-TF is fine. + WebGLBuffer::AddBindCount(target, mElementArrayBuffer.get(), addVal); + for (const auto& attrib : mAttribs) { + WebGLBuffer::AddBindCount(target, attrib.mBuf.get(), addVal); + } +} + WebGLVertexArray* WebGLVertexArray::Create(WebGLContext* webgl) { diff --git a/dom/canvas/WebGLVertexArray.h b/dom/canvas/WebGLVertexArray.h index 516fbaa1bd43..74f714af13b9 100644 --- a/dom/canvas/WebGLVertexArray.h +++ b/dom/canvas/WebGLVertexArray.h @@ -10,7 +10,6 @@ #include "mozilla/LinkedList.h" #include "nsWrapperCache.h" -#include "WebGLBuffer.h" #include "WebGLObjectModel.h" #include "WebGLStrongTypes.h" #include "WebGLVertexAttribData.h" @@ -48,12 +47,11 @@ public: GLuint GLName() const { return mGLName; } + void AddBufferBindCounts(int8_t addVal) const; + protected: explicit WebGLVertexArray(WebGLContext* webgl); - - virtual ~WebGLVertexArray() { - MOZ_ASSERT(IsDeleted()); - } + virtual ~WebGLVertexArray(); virtual void GenVertexArray() = 0; virtual void BindVertexArrayImpl() = 0; diff --git a/dom/canvas/WebGLVertexAttribData.cpp b/dom/canvas/WebGLVertexAttribData.cpp index e62014c3bc01..b5aee18e5c42 100644 --- a/dom/canvas/WebGLVertexAttribData.cpp +++ b/dom/canvas/WebGLVertexAttribData.cpp @@ -6,6 +6,7 @@ #include "WebGLVertexAttribData.h" #include "GLContext.h" +#include "WebGLBuffer.h" namespace mozilla { @@ -71,7 +72,7 @@ WebGLVertexAttribData::VertexAttribPointer(bool integerFunc, WebGLBuffer* buf, uint32_t stride, uint64_t byteOffset) { mIntegerFunc = integerFunc; - mBuf = buf; + WebGLBuffer::SetSlot(0, buf, &mBuf); mType = type; mBaseType = AttribPointerBaseType(integerFunc, type); mSize = size; diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/bound-buffer-size-change-test.html b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/bound-buffer-size-change-test.html index 35532834c99f..a9d13496abd1 100644 --- a/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/bound-buffer-size-change-test.html +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/bound-buffer-size-change-test.html @@ -61,8 +61,6 @@ shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "0"); shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "0"); gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4, gl.STATIC_DRAW); -wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION); - shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer1"); shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "0"); shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "0"); @@ -99,13 +97,11 @@ shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "8"); shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "4"); gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4, gl.STATIC_DRAW); -wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION); shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer3"); shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "8"); shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "4"); gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 12, gl.STATIC_DRAW); -wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION); shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer3"); shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "8"); shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "4"); diff --git a/dom/indexedDB/test/unit/wasm_recompile_profile.zip b/dom/indexedDB/test/unit/wasm_recompile_profile.zip index 5e1144db47be..50ca3ef898de 100644 Binary files a/dom/indexedDB/test/unit/wasm_recompile_profile.zip and b/dom/indexedDB/test/unit/wasm_recompile_profile.zip differ diff --git a/dom/media/gmp/GMPChild.cpp b/dom/media/gmp/GMPChild.cpp index 2eddc1c808dd..daea4f52b7c0 100644 --- a/dom/media/gmp/GMPChild.cpp +++ b/dom/media/gmp/GMPChild.cpp @@ -168,7 +168,10 @@ GetPluginPaths(const nsAString& aPluginPath, // Mac sandbox rules expect paths to actual files and directories -- not // soft links. + libDirectory->Normalize(); aPluginDirectoryPath = GetNativeTarget(libDirectory); + + libFile->Normalize(); aPluginFilePath = GetNativeTarget(libFile); return true; diff --git a/dom/xhr/XMLHttpRequestMainThread.cpp b/dom/xhr/XMLHttpRequestMainThread.cpp index 7bd4a247294b..9a5f417209f2 100644 --- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -3381,7 +3381,10 @@ XMLHttpRequestMainThread::OnRedirectVerifyCallback(nsresult result) mRedirectCallback->OnRedirectVerifyCallback(result); mRedirectCallback = nullptr; - return result; + // It's important that we return success here. If we return the result code + // that we were passed, JavaScript callers who cancel the redirect will wind + // up throwing an exception in the process. + return NS_OK; } ///////////////////////////////////////////////////// diff --git a/intl/icu-patches/bug-1325858-close-key.diff b/intl/icu-patches/bug-1325858-close-key.diff new file mode 100644 index 000000000000..8068f06182d7 --- /dev/null +++ b/intl/icu-patches/bug-1325858-close-key.diff @@ -0,0 +1,26 @@ +getTZKeyName in common/wintz.cpp leaks registry handle. + +https://ssl.icu-project.org/trac/ticket/12908 + +diff --git a/intl/icu/source/common/wintz.c b/intl/icu/source/common/wintz.c +--- a/intl/icu/source/common/wintz.c ++++ b/intl/icu/source/common/wintz.c +@@ -211,16 +211,18 @@ static LONG getTZKeyName(char* tzKeyName + hkey, + "TimeZoneKeyName", + NULL, + NULL, + (LPBYTE)tzKeyName, + &cbData); + } + ++ RegCloseKey(hkey); ++ + return result; + } + + /* + This code attempts to detect the Windows time zone, as set in the + Windows Date and Time control panel. It attempts to work on + multiple flavors of Windows (9x, Me, NT, 2000, XP) and on localized + installs. It works by directly interrogating the registry and diff --git a/intl/icu/source/common/wintz.c b/intl/icu/source/common/wintz.c index a8696af39d4a..14fc476040c7 100644 --- a/intl/icu/source/common/wintz.c +++ b/intl/icu/source/common/wintz.c @@ -216,6 +216,8 @@ static LONG getTZKeyName(char* tzKeyName, int32_t length) { &cbData); } + RegCloseKey(hkey); + return result; } diff --git a/intl/update-icu.sh b/intl/update-icu.sh index 8311dc6cc9fb..7dc54eb6f9d3 100755 --- a/intl/update-icu.sh +++ b/intl/update-icu.sh @@ -61,6 +61,7 @@ for patch in \ bug-1172609-timezone-recreateDefault.diff \ bug-1198952-workaround-make-3.82-bug.diff \ bug-1228227-bug-1263325-libc++-gcc_hidden.diff \ + bug-1325858-close-key.diff \ ucol_getKeywordValuesForLocale-ulist_resetList.diff \ unum_formatDoubleForFields.diff \ ; do diff --git a/js/src/jit-test/tests/ion/bug1188586.js b/js/src/jit-test/tests/ion/bug1188586.js new file mode 100644 index 000000000000..df5f166ba4d8 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1188586.js @@ -0,0 +1,7 @@ +(function(y) { + for (var k = 0; k < 9; ++k) { + try { + y ** y == a; + } catch (e) {} + } +})(); diff --git a/js/src/jit-test/tests/ion/bug1326150.js b/js/src/jit-test/tests/ion/bug1326150.js new file mode 100644 index 000000000000..3c9047c98325 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1326150.js @@ -0,0 +1,4 @@ +this.x = []; +Function.apply(null, this.x); +Object.defineProperty(this, "x", {get: valueOf}); +assertEq(evaluate("this.x;"), this); diff --git a/js/src/jit/BaselineCacheIRCompiler.cpp b/js/src/jit/BaselineCacheIRCompiler.cpp index 9759226ec928..5e38dc101cd5 100644 --- a/js/src/jit/BaselineCacheIRCompiler.cpp +++ b/js/src/jit/BaselineCacheIRCompiler.cpp @@ -415,9 +415,9 @@ BaselineCacheIRCompiler::emitCallScriptedGetterResult() return true; } -typedef bool (*DoCallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, MutableHandleValue); -static const VMFunction DoCallNativeGetterInfo = - FunctionInfo(DoCallNativeGetter, "DoCallNativeGetter"); +typedef bool (*CallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, MutableHandleValue); +static const VMFunction CallNativeGetterInfo = + FunctionInfo(CallNativeGetter, "CallNativeGetter"); bool BaselineCacheIRCompiler::emitCallNativeGetterResult() @@ -439,7 +439,7 @@ BaselineCacheIRCompiler::emitCallNativeGetterResult() masm.Push(obj); masm.Push(scratch); - if (!callVM(masm, DoCallNativeGetterInfo)) + if (!callVM(masm, CallNativeGetterInfo)) return false; stubFrame.leave(masm); diff --git a/js/src/jit/BaselineDebugModeOSR.cpp b/js/src/jit/BaselineDebugModeOSR.cpp index eac27aa3a2da..d3e57ea76a4d 100644 --- a/js/src/jit/BaselineDebugModeOSR.cpp +++ b/js/src/jit/BaselineDebugModeOSR.cpp @@ -698,7 +698,6 @@ RecompileBaselineScriptForDebugMode(JSContext* cx, JSScript* script, _(Call_ScriptedApplyArray) \ _(Call_ScriptedApplyArguments) \ _(Call_ScriptedFunCall) \ - _(GetProp_CallNativeGlobal) \ _(GetProp_Generic) \ _(SetProp_CallScripted) \ _(SetProp_CallNative) diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 1ffc5e41e60a..d901295fd268 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -435,10 +435,6 @@ ICTypeUpdate_ObjectGroup::Compiler::generateStubCode(MacroAssembler& masm) return true; } -typedef bool (*DoCallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, MutableHandleValue); -static const VMFunction DoCallNativeGetterInfo = - FunctionInfo(DoCallNativeGetter, "DoCallNativeGetter"); - // // ToBool_Fallback // @@ -963,7 +959,7 @@ DoGetElemFallback(JSContext* cx, BaselineFrame* frame, ICGetElem_Fallback* stub_ if (!attached && !JitOptions.disableCacheIR) { ICStubEngine engine = ICStubEngine::Baseline; GetPropIRGenerator gen(cx, pc, CacheKind::GetElem, engine, &isTemporarilyUnoptimizable, - lhs, rhs); + lhs, rhs, CanAttachGetter::Yes); if (gen.tryAttachStub()) { ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), engine, info.outerScript(cx), stub); @@ -2357,133 +2353,6 @@ ICIn_Dense::Compiler::generateStubCode(MacroAssembler& masm) return true; } -// Try to update existing SetProp setter call stubs for the given holder in -// place with a new shape and setter. -static bool -UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub, - ICStub::Kind kind, - NativeObject* holder, - JSObject* receiver, - JSFunction* setter) -{ - MOZ_ASSERT(kind == ICStub::SetProp_CallScripted || - kind == ICStub::SetProp_CallNative); - MOZ_ASSERT(holder); - MOZ_ASSERT(receiver); - - bool isOwnSetter = (holder == receiver); - bool foundMatchingStub = false; - ReceiverGuard receiverGuard(receiver); - for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) { - if (iter->kind() == kind) { - ICSetPropCallSetter* setPropStub = static_cast(*iter); - if (setPropStub->holder() == holder && setPropStub->isOwnSetter() == isOwnSetter) { - // If this is an own setter, update the receiver guard as well, - // since that's the shape we'll be guarding on. Furthermore, - // isOwnSetter() relies on holderShape_ and receiverGuard_ being - // the same shape. - if (isOwnSetter) - setPropStub->receiverGuard().update(receiverGuard); - - MOZ_ASSERT(setPropStub->holderShape() != holder->lastProperty() || - !setPropStub->receiverGuard().matches(receiverGuard), - "Why didn't we end up using this stub?"); - - // We want to update the holder shape to match the new one no - // matter what, even if the receiver shape is different. - setPropStub->holderShape() = holder->lastProperty(); - - // Make sure to update the setter, since a shape change might - // have changed which setter we want to use. - setPropStub->setter() = setter; - if (setPropStub->receiverGuard().matches(receiverGuard)) - foundMatchingStub = true; - } - } - } - - return foundMatchingStub; -} - -// Attach an optimized stub for a GETGNAME/CALLGNAME getter op. -static bool -TryAttachGlobalNameAccessorStub(JSContext* cx, HandleScript script, jsbytecode* pc, - ICGetName_Fallback* stub, - Handle globalLexical, - HandlePropertyName name, bool* attached, - bool* isTemporarilyUnoptimizable) -{ - MOZ_ASSERT(globalLexical->isGlobal()); - RootedId id(cx, NameToId(name)); - - // There must not be a shadowing binding on the global lexical scope. - if (globalLexical->lookup(cx, id)) - return true; - - RootedGlobalObject global(cx, &globalLexical->global()); - - // The property must be found, and it must be found as a normal data property. - RootedShape shape(cx); - RootedNativeObject current(cx, global); - while (true) { - shape = current->lookup(cx, id); - if (shape) - break; - JSObject* proto = current->staticPrototype(); - if (!proto || !proto->is()) - return true; - current = &proto->as(); - } - - // Instantiate this global property, for use during Ion compilation. - if (IsIonEnabled(cx)) - EnsureTrackPropertyTypes(cx, current, id); - - // Try to add a getter stub. We don't handle scripted getters yet; if this - // changes we need to make sure IonBuilder::getPropTryCommonGetter (which - // requires a Baseline stub) handles non-outerized this objects correctly. - bool isScripted; - if (IsCacheableGetPropCall(cx, global, current, shape, &isScripted, isTemporarilyUnoptimizable) && - !isScripted) - { - ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); - RootedFunction getter(cx, &shape->getterObject()->as()); - - // The CallNativeGlobal stub needs to generate 3 shape checks: - // - // 1. The global lexical scope shape check. - // 2. The global object shape check. - // 3. The holder shape check. - // - // 1 is done as the receiver check, as for GETNAME the global lexical scope is in the - // receiver position. 2 is done as a manual check that other GetProp stubs don't do. 3 is - // done as the holder check per normal. - // - // In the case the holder is the global object, check 2 is redundant but is not yet - // optimized away. - JitSpew(JitSpew_BaselineIC, " Generating GetName(GlobalName/NativeGetter) stub"); - if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallNativeGlobal, current, - globalLexical, getter)) - { - *attached = true; - return true; - } - ICGetPropCallNativeCompiler compiler(cx, ICStub::GetProp_CallNativeGlobal, - ICStubCompiler::Engine::Baseline, - monitorStub, globalLexical, current, - getter, script->pcToOffset(pc), - /* inputDefinitelyObject = */ true); - - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - - stub->addNewStub(newStub); - *attached = true; - } - return true; -} - static bool DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_, HandleObject envChain, MutableHandleValue res) @@ -2502,7 +2371,6 @@ DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_ RootedPropertyName name(cx, script->getName(pc)); bool attached = false; - bool isTemporarilyUnoptimizable = false; // Attach new stub. if (stub->numOptimizedStubs() >= ICGetName_Fallback::MAX_OPTIMIZED_STUBS) { @@ -2510,12 +2378,16 @@ DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_ attached = true; } - if (!attached && IsGlobalOp(JSOp(*pc)) && !script->hasNonSyntacticScope()) { - if (!TryAttachGlobalNameAccessorStub(cx, script, pc, stub, - envChain.as(), - name, &attached, &isTemporarilyUnoptimizable)) - { - return false; + if (!attached && !JitOptions.disableCacheIR) { + ICStubEngine engine = ICStubEngine::Baseline; + GetNameIRGenerator gen(cx, pc, script, envChain, name); + if (gen.tryAttachStub()) { + ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), + engine, info.outerScript(cx), stub); + if (newStub) { + JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub"); + attached = true; + } } } @@ -2538,23 +2410,8 @@ DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_ // Add a type monitor stub for the resulting value. if (!stub->addMonitorStubForValue(cx, &info, res)) return false; - if (attached) - return true; - if (!JitOptions.disableCacheIR) { - ICStubEngine engine = ICStubEngine::Baseline; - GetNameIRGenerator gen(cx, pc, script, envChain, name); - if (gen.tryAttachStub()) { - ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), - engine, info.outerScript(cx), stub); - if (newStub) { - JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub"); - attached = true; - } - } - } - - if (!attached && !isTemporarilyUnoptimizable) + if (!attached) stub->noteUnoptimizableAccess(); return true; } @@ -2803,6 +2660,54 @@ TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, IC return true; } +// Try to update existing SetProp setter call stubs for the given holder in +// place with a new shape and setter. +static bool +UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub, + ICStub::Kind kind, + NativeObject* holder, + JSObject* receiver, + JSFunction* setter) +{ + MOZ_ASSERT(kind == ICStub::SetProp_CallScripted || + kind == ICStub::SetProp_CallNative); + MOZ_ASSERT(holder); + MOZ_ASSERT(receiver); + + bool isOwnSetter = (holder == receiver); + bool foundMatchingStub = false; + ReceiverGuard receiverGuard(receiver); + for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) { + if (iter->kind() == kind) { + ICSetPropCallSetter* setPropStub = static_cast(*iter); + if (setPropStub->holder() == holder && setPropStub->isOwnSetter() == isOwnSetter) { + // If this is an own setter, update the receiver guard as well, + // since that's the shape we'll be guarding on. Furthermore, + // isOwnSetter() relies on holderShape_ and receiverGuard_ being + // the same shape. + if (isOwnSetter) + setPropStub->receiverGuard().update(receiverGuard); + + MOZ_ASSERT(setPropStub->holderShape() != holder->lastProperty() || + !setPropStub->receiverGuard().matches(receiverGuard), + "Why didn't we end up using this stub?"); + + // We want to update the holder shape to match the new one no + // matter what, even if the receiver shape is different. + setPropStub->holderShape() = holder->lastProperty(); + + // Make sure to update the setter, since a shape change might + // have changed which setter we want to use. + setPropStub->setter() = setter; + if (setPropStub->receiverGuard().matches(receiverGuard)) + foundMatchingStub = true; + } + } + } + + return foundMatchingStub; +} + // Attach an optimized property set stub for a SETPROP/SETGNAME/SETNAME op on // an accessor property. static bool diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp index 2d7fa53fe709..967718bda107 100644 --- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -11,6 +11,8 @@ #include "jit/BaselineIC.h" #include "jit/CacheIRCompiler.h" +#include "jsscriptinlines.h" + #include "vm/EnvironmentObject-inl.h" #include "vm/ObjectGroup-inl.h" @@ -670,22 +672,6 @@ BaselineInspector::templateCallObject() return &res->as(); } -static Shape* -GlobalShapeForGetPropFunction(ICStub* stub) -{ - if (stub->isGetProp_CallNativeGlobal()) { - ICGetProp_CallNativeGlobal* nstub = stub->toGetProp_CallNativeGlobal(); - if (nstub->isOwnGetter()) - return nullptr; - - Shape* shape = nstub->globalShape(); - MOZ_ASSERT(shape->getObjectClass()->flags & JSCLASS_IS_GLOBAL); - return shape; - } - - return nullptr; -} - static bool MatchCacheIRReceiverGuard(CacheIRReader& reader, ICCacheIR_Monitored* stub, ObjOperandId objId, ReceiverGuard* receiver) @@ -734,12 +720,89 @@ MatchCacheIRReceiverGuard(CacheIRReader& reader, ICCacheIR_Monitored* stub, ObjO return true; } +static bool +AddCacheIRGlobalGetter(ICCacheIR_Monitored* stub, bool innerized, + JSObject** holder_, Shape** holderShape_, + JSFunction** commonGetter, Shape** globalShape_, bool* isOwnProperty, + BaselineInspector::ReceiverVector& receivers, + BaselineInspector::ObjectGroupVector& convertUnboxedGroups, + JSScript* script) +{ + // We are matching on the IR generated by tryAttachGlobalNameGetter: + // + // GuardShape objId + // globalId = LoadEnclosingEnvironment objId + // GuardShape globalId + // > + // + // CallNativeGetterResult globalId + + CacheIRReader reader(stub->stubInfo()); + + ObjOperandId objId = ObjOperandId(0); + if (!reader.matchOp(CacheOp::GuardShape, objId)) + return false; + Shape* globalLexicalShape = stub->stubInfo()->getStubField(stub, reader.stubOffset()); + + if (!reader.matchOp(CacheOp::LoadEnclosingEnvironment, objId)) + return false; + ObjOperandId globalId = reader.objOperandId(); + + if (!reader.matchOp(CacheOp::GuardShape, globalId)) + return false; + Shape* globalShape = stub->stubInfo()->getStubField(stub, reader.stubOffset()); + MOZ_ASSERT(globalShape->getObjectClass()->flags & JSCLASS_IS_GLOBAL); + + JSObject* holder = &script->global(); + Shape* holderShape = globalShape; + if (reader.matchOp(CacheOp::LoadObject)) { + ObjOperandId holderId = reader.objOperandId(); + holder = stub->stubInfo()->getStubField(stub, reader.stubOffset()).get(); + + if (!reader.matchOp(CacheOp::GuardShape, holderId)) + return false; + holderShape = stub->stubInfo()->getStubField(stub, reader.stubOffset()); + } + + // This guard will always fail, try the next stub. + if (holder->as().lastProperty() != holderShape) + return true; + + if (!reader.matchOp(CacheOp::CallNativeGetterResult, globalId)) + return false; + size_t offset = reader.stubOffset(); + JSFunction* getter = + &stub->stubInfo()->getStubField(stub, offset)->as(); + + ReceiverGuard receiver; + receiver.shape = globalLexicalShape; + if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) + return false; + + if (!*commonGetter) { + *holder_ = holder; + *holderShape_ = holderShape; + *commonGetter = getter; + *globalShape_ = globalShape; + + // This is always false, because the getters never live on the globalLexical. + *isOwnProperty = false; + } else if (*isOwnProperty || holderShape != *holderShape_ || globalShape != *globalShape_) { + return false; + } else { + MOZ_ASSERT(*commonGetter == getter); + } + + return true; +} + static bool AddCacheIRGetPropFunction(ICCacheIR_Monitored* stub, bool innerized, JSObject** holder, Shape** holderShape, JSFunction** commonGetter, Shape** globalShape, bool* isOwnProperty, BaselineInspector::ReceiverVector& receivers, - BaselineInspector::ObjectGroupVector& convertUnboxedGroups) + BaselineInspector::ObjectGroupVector& convertUnboxedGroups, + JSScript* script) { // We match either an own getter: // @@ -768,8 +831,11 @@ AddCacheIRGetPropFunction(ICCacheIR_Monitored* stub, bool innerized, CacheIRReader reader(stub->stubInfo()); ObjOperandId objId = ObjOperandId(0); - if (!reader.matchOp(CacheOp::GuardIsObject, objId)) - return false; + if (!reader.matchOp(CacheOp::GuardIsObject, objId)) { + return AddCacheIRGlobalGetter(stub, innerized, holder, holderShape, commonGetter, + globalShape, isOwnProperty, receivers, convertUnboxedGroups, + script); + } if (innerized) { if (!reader.matchOp(CacheOp::GuardClass, objId) || @@ -880,31 +946,11 @@ BaselineInspector::commonGetPropFunction(jsbytecode* pc, bool innerized, const ICEntry& entry = icEntryFromPC(pc); for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) { - if (stub->isGetProp_CallNativeGlobal()) { - ICGetPropCallGetter* nstub = static_cast(stub); - bool isOwn = nstub->isOwnGetter(); - if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups)) - return false; - - if (!*commonGetter) { - *holder = isOwn ? nullptr : nstub->holder().get(); - *holderShape = nstub->holderShape(); - *commonGetter = nstub->getter(); - *globalShape = GlobalShapeForGetPropFunction(nstub); - *isOwnProperty = isOwn; - } else if (nstub->holderShape() != *holderShape || - GlobalShapeForGetPropFunction(nstub) != *globalShape || - isOwn != *isOwnProperty) - { - return false; - } else { - MOZ_ASSERT(*commonGetter == nstub->getter()); - } - } else if (stub->isCacheIR_Monitored()) { + if (stub->isCacheIR_Monitored()) { if (!AddCacheIRGetPropFunction(stub->toCacheIR_Monitored(), innerized, holder, holderShape, commonGetter, globalShape, isOwnProperty, receivers, - convertUnboxedGroups)) + convertUnboxedGroups, script)) { return false; } diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index 1e518732540a..45c9dace8620 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -30,12 +30,14 @@ IRGenerator::IRGenerator(JSContext* cx, jsbytecode* pc, CacheKind cacheKind) GetPropIRGenerator::GetPropIRGenerator(JSContext* cx, jsbytecode* pc, CacheKind cacheKind, ICStubEngine engine, bool* isTemporarilyUnoptimizable, - HandleValue val, HandleValue idVal) + HandleValue val, HandleValue idVal, + CanAttachGetter canAttachGetter) : IRGenerator(cx, pc, cacheKind), val_(val), idVal_(idVal), engine_(engine), isTemporarilyUnoptimizable_(isTemporarilyUnoptimizable), + canAttachGetter_(canAttachGetter), preliminaryObjectAction_(PreliminaryObjectAction::None) {} @@ -223,7 +225,8 @@ enum NativeGetPropCacheability { static NativeGetPropCacheability CanAttachNativeGetProp(JSContext* cx, HandleObject obj, HandleId id, MutableHandleNativeObject holder, MutableHandleShape shape, - jsbytecode* pc, ICStubEngine engine, bool* isTemporarilyUnoptimizable) + jsbytecode* pc, ICStubEngine engine, CanAttachGetter canAttachGetter, + bool* isTemporarilyUnoptimizable) { MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SYMBOL(id)); @@ -253,6 +256,9 @@ CanAttachNativeGetProp(JSContext* cx, HandleObject obj, HandleId id, if (IsCacheableNoProperty(cx, obj, holder, shape, id, pc)) return CanAttachReadSlot; + if (canAttachGetter == CanAttachGetter::No) + return CanAttachNone; + if (IsCacheableGetPropCallScripted(obj, holder, shape, isTemporarilyUnoptimizable)) { // See bug 1226816. if (engine != ICStubEngine::IonSharedIC) @@ -425,7 +431,8 @@ GetPropIRGenerator::tryAttachNative(HandleObject obj, ObjOperandId objId, Handle RootedNativeObject holder(cx_); NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, obj, id, &holder, &shape, pc_, - engine_, isTemporarilyUnoptimizable_); + engine_, canAttachGetter_, + isTemporarilyUnoptimizable_); MOZ_ASSERT_IF(idempotent(), type == CanAttachNone || (type == CanAttachReadSlot && holder)); switch (type) { @@ -476,7 +483,8 @@ GetPropIRGenerator::tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, H RootedShape shape(cx_); RootedNativeObject holder(cx_); NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, windowObj, id, &holder, &shape, pc_, - engine_, isTemporarilyUnoptimizable_); + engine_, canAttachGetter_, + isTemporarilyUnoptimizable_); if (type != CanAttachCallGetter || !IsCacheableGetPropCallNative(windowObj, holder, shape)) { @@ -584,7 +592,7 @@ GetPropIRGenerator::tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId o RootedShape shape(cx_); NativeGetPropCacheability canCache = CanAttachNativeGetProp(cx_, checkObj, id, &holder, &shape, - pc_, engine_, + pc_, engine_, canAttachGetter_, isTemporarilyUnoptimizable_); MOZ_ASSERT_IF(idempotent(), canCache == CanAttachNone || (canCache == CanAttachReadSlot && holder)); @@ -1153,12 +1161,44 @@ GetNameIRGenerator::tryAttachStub() if (tryAttachGlobalNameValue(envId, id)) return true; + if (tryAttachGlobalNameGetter(envId, id)) + return true; if (tryAttachEnvironmentName(envId, id)) return true; return false; } +bool +CanAttachGlobalName(JSContext* cx, Handle globalLexical, HandleId id, + MutableHandleNativeObject holder, MutableHandleShape shape) +{ + // The property must be found, and it must be found as a normal data property. + RootedNativeObject current(cx, globalLexical); + while (true) { + shape.set(current->lookup(cx, id)); + if (shape) + break; + + if (current == globalLexical) { + current = &globalLexical->global(); + } else { + // In the browser the global prototype chain should be immutable. + if (!current->staticPrototypeIsImmutable()) + return false; + + JSObject* proto = current->staticPrototype(); + if (!proto || !proto->is()) + return false; + + current = &proto->as(); + } + } + + holder.set(current); + return true; +} + bool GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId, HandleId id) { @@ -1168,44 +1208,31 @@ GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId, HandleId id) Handle globalLexical = env_.as(); MOZ_ASSERT(globalLexical->isGlobal()); - // The property must be found, and it must be found as a normal data property. - RootedShape shape(cx_); - RootedNativeObject current(cx_, globalLexical); - while (true) { - shape = current->lookup(cx_, id); - if (shape) - break; - if (current == globalLexical) { - current = &globalLexical->global(); - } else { - // In the browser the global prototype chain should be immutable. - if (!current->staticPrototypeIsImmutable()) - return false; - JSObject* proto = current->staticPrototype(); - if (!proto || !proto->is()) - return false; - current = &proto->as(); - } - } + RootedNativeObject holder(cx_); + RootedShape shape(cx_); + if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &shape)) + return false; + + // The property must be found, and it must be found as a normal data property. if (!shape->hasDefaultGetter() || !shape->hasSlot()) return false; // Instantiate this global property, for use during Ion compilation. if (IsIonEnabled(cx_)) - EnsureTrackPropertyTypes(cx_, current, id); + EnsureTrackPropertyTypes(cx_, holder, id); - if (current == globalLexical) { + if (holder == globalLexical) { // There is no need to guard on the shape. Lexical bindings are // non-configurable, and this stub cannot be shared across globals. - size_t dynamicSlotOffset = current->dynamicSlotIndex(shape->slot()) * sizeof(Value); + size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value); writer.loadDynamicSlotResult(objId, dynamicSlotOffset); } else { - // Check the prototype chain from the global to the current + // Check the prototype chain from the global to the holder // prototype. Ignore the global lexical scope as it doesn't figure // into the prototype chain. We guard on the global lexical // scope's shape independently. - if (!IsCacheableGetPropReadSlotForIonOrCacheIR(&globalLexical->global(), current, shape)) + if (!IsCacheableGetPropReadSlotForIonOrCacheIR(&globalLexical->global(), holder, shape)) return false; // Shape guard for global lexical. @@ -1216,19 +1243,59 @@ GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId, HandleId id) writer.guardShape(globalId, globalLexical->global().lastProperty()); ObjOperandId holderId = globalId; - if (current != &globalLexical->global()) { + if (holder != &globalLexical->global()) { // Shape guard holder. - holderId = writer.loadObject(current); - writer.guardShape(holderId, current->lastProperty()); + holderId = writer.loadObject(holder); + writer.guardShape(holderId, holder->lastProperty()); } - EmitLoadSlotResult(writer, holderId, current, shape); + EmitLoadSlotResult(writer, holderId, holder, shape); } writer.typeMonitorResult(); return true; } +bool +GetNameIRGenerator::tryAttachGlobalNameGetter(ObjOperandId objId, HandleId id) +{ + if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope()) + return false; + + Handle globalLexical = env_.as(); + MOZ_ASSERT(globalLexical->isGlobal()); + + RootedNativeObject holder(cx_); + RootedShape shape(cx_); + if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &shape)) + return false; + + if (holder == globalLexical) + return false; + + if (!IsCacheableGetPropCallNative(&globalLexical->global(), holder, shape)) + return false; + + if (IsIonEnabled(cx_)) + EnsureTrackPropertyTypes(cx_, holder, id); + + // Shape guard for global lexical. + writer.guardShape(objId, globalLexical->lastProperty()); + + // Guard on the shape of the GlobalObject. + ObjOperandId globalId = writer.loadEnclosingEnvironment(objId); + writer.guardShape(globalId, globalLexical->global().lastProperty()); + + if (holder != &globalLexical->global()) { + // Shape guard holder. + ObjOperandId holderId = writer.loadObject(holder); + writer.guardShape(holderId, holder->lastProperty()); + } + + EmitCallGetterResultNoGuards(writer, &globalLexical->global(), holder, shape, globalId); + return true; +} + bool GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId, HandleId id) { diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h index e57d3ccbff5f..8522d01d9a84 100644 --- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -720,6 +720,8 @@ class MOZ_RAII IRGenerator CacheKind cacheKind() const { return cacheKind_; } }; +enum class CanAttachGetter { Yes, No }; + // GetPropIRGenerator generates CacheIR for a GetProp IC. class MOZ_RAII GetPropIRGenerator : public IRGenerator { @@ -727,6 +729,7 @@ class MOZ_RAII GetPropIRGenerator : public IRGenerator HandleValue idVal_; ICStubEngine engine_; bool* isTemporarilyUnoptimizable_; + CanAttachGetter canAttachGetter_; enum class PreliminaryObjectAction { None, Unlink, NotePreliminary }; PreliminaryObjectAction preliminaryObjectAction_; @@ -777,7 +780,8 @@ class MOZ_RAII GetPropIRGenerator : public IRGenerator public: GetPropIRGenerator(JSContext* cx, jsbytecode* pc, CacheKind cacheKind, ICStubEngine engine, - bool* isTemporarilyUnoptimizable, HandleValue val, HandleValue idVal); + bool* isTemporarilyUnoptimizable, HandleValue val, HandleValue idVal, + CanAttachGetter canAttachGetter); bool tryAttachStub(); bool tryAttachIdempotentStub(); @@ -798,6 +802,7 @@ class MOZ_RAII GetNameIRGenerator : public IRGenerator HandlePropertyName name_; bool tryAttachGlobalNameValue(ObjOperandId objId, HandleId id); + bool tryAttachGlobalNameGetter(ObjOperandId objId, HandleId id); bool tryAttachEnvironmentName(ObjOperandId objId, HandleId id); public: diff --git a/js/src/jit/IonIC.cpp b/js/src/jit/IonIC.cpp index 5b35a4dbc05f..2e411020e9cc 100644 --- a/js/src/jit/IonIC.cpp +++ b/js/src/jit/IonIC.cpp @@ -127,12 +127,18 @@ IonGetPropertyIC::update(JSContext* cx, HandleScript outerScript, IonGetProperty bool attached = false; if (!JitOptions.disableCacheIR && !ic->disabled()) { if (ic->canAttachStub()) { + // IonBuilder calls PropertyReadNeedsTypeBarrier to determine if it + // needs a type barrier. Unfortunately, PropertyReadNeedsTypeBarrier + // does not account for getters, so we should only attach a getter + // stub if we inserted a type barrier. + CanAttachGetter canAttachGetter = + ic->monitoredResult() ? CanAttachGetter::Yes : CanAttachGetter::No; jsbytecode* pc = ic->idempotent() ? nullptr : ic->pc(); RootedValue objVal(cx, ObjectValue(*obj)); bool isTemporarilyUnoptimizable; GetPropIRGenerator gen(cx, pc, ic->kind(), ICStubEngine::IonIC, &isTemporarilyUnoptimizable, - objVal, idVal); + objVal, idVal, canAttachGetter); if (ic->idempotent() ? gen.tryAttachIdempotentStub() : gen.tryAttachStub()) { attached = ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), outerScript); diff --git a/js/src/jit/JitAllocPolicy.h b/js/src/jit/JitAllocPolicy.h index bf162929063f..ab4f8693591c 100644 --- a/js/src/jit/JitAllocPolicy.h +++ b/js/src/jit/JitAllocPolicy.h @@ -173,7 +173,7 @@ struct TempObject "Placement new argument type must inherit from TempObject"); MOZ_ASSERT(pos); return pos; - } + } }; template diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp index eb5a6c1c2242..fca633662216 100644 --- a/js/src/jit/JitOptions.cpp +++ b/js/src/jit/JitOptions.cpp @@ -237,6 +237,16 @@ DefaultJitOptions::DefaultJitOptions() // included in the bounds check. SET_DEFAULT(wasmFoldOffsets, true); + // Until which wasm bytecode size should we accumulate functions, in order + // to compile efficiently on helper threads (see also bug 1320374). + SET_DEFAULT(wasmBatchThreshold, 10000); + + // In order to have different batching thresholds for Ion and the wasm + // baseline, and since a same batch can contain both Ion and baseline + // compiled functions, we make Ion functions weight more by using a scaling + // factor. + SET_DEFAULT(wasmBatchIonScaleFactor, 9); + // Determines whether we suppress using signal handlers // for interrupting jit-ed code. This is used only for testing. SET_DEFAULT(ionInterruptWithoutSignals, false); diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h index 076980b4e55b..769b42f59759 100644 --- a/js/src/jit/JitOptions.h +++ b/js/src/jit/JitOptions.h @@ -87,6 +87,8 @@ struct DefaultJitOptions uint32_t branchPruningBlockSpanFactor; uint32_t branchPruningEffectfulInstFactor; uint32_t branchPruningThreshold; + uint32_t wasmBatchThreshold; + uint32_t wasmBatchIonScaleFactor; mozilla::Maybe forcedDefaultIonWarmUpThreshold; mozilla::Maybe forcedDefaultIonSmallFunctionWarmUpThreshold; mozilla::Maybe forcedRegisterAllocator; diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp index 1155709c7683..b6bade1cb00a 100644 --- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -175,7 +175,6 @@ ICStub::NonCacheIRStubMakesGCCalls(Kind kind) case Call_ScriptedFunCall: case Call_StringSplit: case WarmUpCounter_Fallback: - case GetProp_CallNativeGlobal: case GetProp_Generic: case SetProp_CallScripted: case SetProp_CallNative: @@ -352,15 +351,6 @@ ICStub::trace(JSTracer* trc) TraceEdge(trc, &constantStub->value(), "baseline-getintrinsic-constant-value"); break; } - case ICStub::GetProp_CallNativeGlobal: { - ICGetProp_CallNativeGlobal* callStub = toGetProp_CallNativeGlobal(); - callStub->receiverGuard().trace(trc); - TraceEdge(trc, &callStub->holder(), "baseline-getpropcallnativeglobal-stub-holder"); - TraceEdge(trc, &callStub->holderShape(), "baseline-getpropcallnativeglobal-stub-holdershape"); - TraceEdge(trc, &callStub->globalShape(), "baseline-getpropcallnativeglobal-stub-globalshape"); - TraceEdge(trc, &callStub->getter(), "baseline-getpropcallnativeglobal-stub-getter"); - break; - } case ICStub::SetProp_Native: { ICSetProp_Native* propStub = toSetProp_Native(); TraceEdge(trc, &propStub->shape(), "baseline-setpropnative-stub-shape"); @@ -2108,68 +2098,6 @@ IsCacheableGetPropCall(JSContext* cx, JSObject* obj, JSObject* holder, Shape* sh return true; } -// Try to update all existing GetProp/GetName getter call stubs that match the -// given holder in place with a new shape and getter. fallbackStub can be -// either an ICGetProp_Fallback or an ICGetName_Fallback. -// -// If 'getter' is an own property, holder == receiver must be true. -bool -UpdateExistingGetPropCallStubs(ICFallbackStub* fallbackStub, - ICStub::Kind kind, - HandleNativeObject holder, - HandleObject receiver, - HandleFunction getter) -{ - MOZ_ASSERT(kind == ICStub::GetProp_CallNativeGlobal); - MOZ_ASSERT(fallbackStub->isGetName_Fallback() || - fallbackStub->isGetProp_Fallback()); - MOZ_ASSERT(holder); - MOZ_ASSERT(receiver); - - bool isOwnGetter = (holder == receiver); - bool foundMatchingStub = false; - ReceiverGuard receiverGuard(receiver); - for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) { - if (iter->kind() == kind) { - ICGetPropCallGetter* getPropStub = static_cast(*iter); - if (getPropStub->holder() == holder && getPropStub->isOwnGetter() == isOwnGetter) { - // If this is an own getter, update the receiver guard as well, - // since that's the shape we'll be guarding on. Furthermore, - // isOwnGetter() relies on holderShape_ and receiverGuard_ being - // the same shape. - if (isOwnGetter) - getPropStub->receiverGuard().update(receiverGuard); - - MOZ_ASSERT(getPropStub->holderShape() != holder->lastProperty() || - !getPropStub->receiverGuard().matches(receiverGuard) || - getPropStub->toGetProp_CallNativeGlobal()->globalShape() != - receiver->as().global().lastProperty(), - "Why didn't we end up using this stub?"); - - // We want to update the holder shape to match the new one no - // matter what, even if the receiver shape is different. - getPropStub->holderShape() = holder->lastProperty(); - - // Make sure to update the getter, since a shape change might - // have changed which getter we want to use. - getPropStub->getter() = getter; - - if (getPropStub->isGetProp_CallNativeGlobal()) { - ICGetProp_CallNativeGlobal* globalStub = - getPropStub->toGetProp_CallNativeGlobal(); - globalStub->globalShape() = - receiver->as().global().lastProperty(); - } - - if (getPropStub->receiverGuard().matches(receiverGuard)) - foundMatchingStub = true; - } - } - } - - return foundMatchingStub; -} - bool CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, jsid id, JSObject** lastProto, size_t* protoChainDepthOut) @@ -2296,7 +2224,7 @@ DoGetPropFallback(JSContext* cx, void* payload, ICGetProp_Fallback* stub_, if (!attached && !JitOptions.disableCacheIR) { RootedValue idVal(cx, StringValue(name)); GetPropIRGenerator gen(cx, pc, CacheKind::GetProp, engine, &isTemporarilyUnoptimizable, - val, idVal); + val, idVal, CanAttachGetter::Yes); if (gen.tryAttachStub()) { ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), engine, info.outerScript(cx), stub); @@ -2430,18 +2358,6 @@ GuardReceiverObject(MacroAssembler& masm, ReceiverGuard guard, } } -static void -GuardGlobalObject(MacroAssembler& masm, HandleObject holder, Register globalLexicalReg, - Register holderReg, Register scratch, size_t globalShapeOffset, Label* failure) -{ - if (holder->is()) - return; - masm.extractObject(Address(globalLexicalReg, EnvironmentObject::offsetOfEnclosingEnvironment()), - holderReg); - masm.loadPtr(Address(ICStubReg, globalShapeOffset), scratch); - masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, failure); -} - bool GetProtoShapes(JSObject* obj, size_t protoChainDepth, MutableHandle shapes) { @@ -2457,135 +2373,6 @@ GetProtoShapes(JSObject* obj, size_t protoChainDepth, MutableHandle return true; } -// -// VM function to help call native getters. -// - -bool -DoCallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj, - MutableHandleValue result) -{ - MOZ_ASSERT(callee->isNative()); - JSNative natfun = callee->native(); - - JS::AutoValueArray<2> vp(cx); - vp[0].setObject(*callee.get()); - vp[1].setObject(*obj.get()); - - if (!natfun(cx, 0, vp.begin())) - return false; - - result.set(vp[0]); - return true; -} - -typedef bool (*DoCallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, MutableHandleValue); -static const VMFunction DoCallNativeGetterInfo = - FunctionInfo(DoCallNativeGetter, "DoCallNativeGetter"); - -bool -ICGetPropCallNativeCompiler::generateStubCode(MacroAssembler& masm) -{ - Label failure; - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); - Register objReg = InvalidReg; - - if (inputDefinitelyObject_) { - objReg = R0.scratchReg(); - } else { - // Guard input is an object and unbox. - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - objReg = masm.extractObject(R0, ExtractTemp0); - } - - Register scratch = regs.takeAnyExcluding(ICTailCallReg); - - // Shape guard. - GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch, - ICGetPropCallGetter::offsetOfReceiverGuard(), &failure); - - if (receiver_ != holder_) { - Register holderReg = regs.takeAny(); - - // If we are generating a non-lexical GETGNAME stub, we must also - // guard on the shape of the GlobalObject. - if (kind == ICStub::GetProp_CallNativeGlobal) { - MOZ_ASSERT(receiver_->is() && - receiver_->as().isGlobal()); - GuardGlobalObject(masm, holder_, objReg, holderReg, scratch, - ICGetProp_CallNativeGlobal::offsetOfGlobalShape(), &failure); - } - - masm.loadPtr(Address(ICStubReg, ICGetPropCallGetter::offsetOfHolder()), holderReg); - masm.loadPtr(Address(ICStubReg, ICGetPropCallGetter::offsetOfHolderShape()), scratch); - masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failure); - regs.add(holderReg); - } - - // Box and push obj onto baseline frame stack for decompiler - if (engine_ == Engine::Baseline) { - if (inputDefinitelyObject_) - masm.tagValue(JSVAL_TYPE_OBJECT, objReg, R0); - EmitStowICValues(masm, 1); - if (inputDefinitelyObject_) - objReg = masm.extractObject(R0, ExtractTemp0); - } - - // Push a stub frame so that we can perform a non-tail call. - enterStubFrame(masm, scratch); - - // Load callee function. - Register callee = regs.takeAny(); - masm.loadPtr(Address(ICStubReg, ICGetPropCallGetter::offsetOfGetter()), callee); - - // If we're calling a getter on the global, inline the logic for the - // 'this' hook on the global lexical scope and manually push the global. - if (kind == ICStub::GetProp_CallNativeGlobal) - masm.extractObject(Address(objReg, EnvironmentObject::offsetOfEnclosingEnvironment()), - objReg); - - // Push args for vm call. - masm.Push(objReg); - masm.Push(callee); - - regs.add(R0); - - if (!callVM(DoCallNativeGetterInfo, masm)) - return false; - leaveStubFrame(masm); - - if (engine_ == Engine::Baseline) - EmitUnstowICValues(masm, 1, /* discard = */true); - - // Enter type monitor IC to type-check result. - EmitEnterTypeMonitorIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -ICStub* -ICGetPropCallNativeCompiler::getStub(ICStubSpace* space) -{ - ReceiverGuard guard(receiver_); - Shape* holderShape = holder_->as().lastProperty(); - - switch (kind) { - case ICStub::GetProp_CallNativeGlobal: { - Shape* globalShape = receiver_->as().global().lastProperty(); - return newStub(space, getStubCode(), firstMonitorStub_, - guard, holder_, holderShape, globalShape, - getter_, pcOffset_); - } - - default: - MOZ_CRASH("Bad stub kind"); - } -} - /* static */ ICGetProp_Generic* ICGetProp_Generic::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, ICGetProp_Generic& other) @@ -2678,31 +2465,6 @@ BaselineScript::noteAccessedGetter(uint32_t pcOffset) stub->toGetProp_Fallback()->noteAccessedGetter(); } -ICGetPropCallGetter::ICGetPropCallGetter(Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, - ReceiverGuard receiverGuard, JSObject* holder, - Shape* holderShape, JSFunction* getter, - uint32_t pcOffset) - : ICMonitoredStub(kind, stubCode, firstMonitorStub), - receiverGuard_(receiverGuard), - holder_(holder), - holderShape_(holderShape), - getter_(getter), - pcOffset_(pcOffset) -{ - MOZ_ASSERT(kind == ICStub::GetProp_CallNativeGlobal); -} - -/* static */ ICGetProp_CallNativeGlobal* -ICGetProp_CallNativeGlobal::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetProp_CallNativeGlobal& other) -{ - return New(cx, space, other.jitCode(), firstMonitorStub, - other.receiverGuard(), other.holder_, - other.holderShape_, other.globalShape_, - other.getter_, other.pcOffset_); -} - -// // TypeMonitor_Fallback // diff --git a/js/src/jit/SharedIC.h b/js/src/jit/SharedIC.h index 184ca2bb95d4..099bde8134a0 100644 --- a/js/src/jit/SharedIC.h +++ b/js/src/jit/SharedIC.h @@ -2256,10 +2256,6 @@ GetProtoShapes(JSObject* obj, size_t protoChainDepth, MutableHandle void CheckForTypedObjectWithDetachedStorage(JSContext* cx, MacroAssembler& masm, Label* failure); -MOZ_MUST_USE bool -DoCallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj, - MutableHandleValue result); - void LoadTypedThingData(MacroAssembler& masm, TypedThingLayout layout, Register obj, Register result); @@ -2384,158 +2380,6 @@ ReferenceTypeFromSimpleTypeDescrKey(uint32_t key) return ReferenceTypeDescr::Type(key >> 1); } -class ICGetPropCallGetter : public ICMonitoredStub -{ - friend class ICStubSpace; - - protected: - // Shape/group of receiver object. Used for both own and proto getters. - // In the GetPropCallDOMProxyNative case, the receiver guard enforces - // the proxy handler, because Shape implies Class. - HeapReceiverGuard receiverGuard_; - - // Holder and holder shape. For own getters, guarding on receiverGuard_ is - // sufficient, although Ion may use holder_ and holderShape_ even for own - // getters. In this case holderShape_ == receiverGuard_.shape_ (isOwnGetter - // below relies on this). - GCPtrObject holder_; - - GCPtrShape holderShape_; - - // Function to call. - GCPtrFunction getter_; - - // PC offset of call - uint32_t pcOffset_; - - ICGetPropCallGetter(Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, - ReceiverGuard receiverGuard, JSObject* holder, - Shape* holderShape, JSFunction* getter, uint32_t pcOffset); - - public: - GCPtrObject& holder() { - return holder_; - } - GCPtrShape& holderShape() { - return holderShape_; - } - GCPtrFunction& getter() { - return getter_; - } - HeapReceiverGuard& receiverGuard() { - return receiverGuard_; - } - - bool isOwnGetter() const { - MOZ_ASSERT(holder_->isNative()); - MOZ_ASSERT(holderShape_); - return receiverGuard_.shape() == holderShape_; - } - - static size_t offsetOfHolder() { - return offsetof(ICGetPropCallGetter, holder_); - } - static size_t offsetOfHolderShape() { - return offsetof(ICGetPropCallGetter, holderShape_); - } - static size_t offsetOfGetter() { - return offsetof(ICGetPropCallGetter, getter_); - } - static size_t offsetOfPCOffset() { - return offsetof(ICGetPropCallGetter, pcOffset_); - } - static size_t offsetOfReceiverGuard() { - return offsetof(ICGetPropCallGetter, receiverGuard_); - } - - class Compiler : public ICStubCompiler { - protected: - ICStub* firstMonitorStub_; - RootedObject receiver_; - RootedObject holder_; - RootedFunction getter_; - uint32_t pcOffset_; - - virtual int32_t getKey() const { - // ICGetPropCallNativeCompiler::getKey adds more bits to our - // return value, so be careful when making changes here. - return static_cast(engine_) | - (static_cast(kind) << 1) | - (HeapReceiverGuard::keyBits(receiver_) << 17) | - (static_cast(receiver_ != holder_) << 19); - } - - public: - Compiler(JSContext* cx, ICStub::Kind kind, Engine engine, ICStub* firstMonitorStub, - HandleObject receiver, HandleObject holder, HandleFunction getter, - uint32_t pcOffset) - : ICStubCompiler(cx, kind, engine), - firstMonitorStub_(firstMonitorStub), - receiver_(cx, receiver), - holder_(cx, holder), - getter_(cx, getter), - pcOffset_(pcOffset) - { - MOZ_ASSERT(kind == ICStub::GetProp_CallNativeGlobal); - } - }; -}; - -// Stub for calling a native getter on the GlobalObject. -class ICGetProp_CallNativeGlobal : public ICGetPropCallGetter -{ - friend class ICStubSpace; - - protected: - GCPtrShape globalShape_; - - ICGetProp_CallNativeGlobal(JitCode* stubCode, ICStub* firstMonitorStub, - ReceiverGuard receiverGuard, - JSObject* holder, Shape* holderShape, Shape* globalShape, - JSFunction* getter, uint32_t pcOffset) - : ICGetPropCallGetter(GetProp_CallNativeGlobal, stubCode, firstMonitorStub, - receiverGuard, holder, holderShape, getter, pcOffset), - globalShape_(globalShape) - { } - - public: - static ICGetProp_CallNativeGlobal* Clone(JSContext* cx, ICStubSpace* space, - ICStub* firstMonitorStub, - ICGetProp_CallNativeGlobal& other); - - GCPtrShape& globalShape() { - return globalShape_; - } - static size_t offsetOfGlobalShape() { - return offsetof(ICGetProp_CallNativeGlobal, globalShape_); - } -}; - -class ICGetPropCallNativeCompiler : public ICGetPropCallGetter::Compiler -{ - bool inputDefinitelyObject_; - protected: - MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm); - - virtual int32_t getKey() const { - int32_t baseKey = ICGetPropCallGetter::Compiler::getKey(); - MOZ_ASSERT((baseKey >> 21) == 0); - return baseKey | (static_cast(inputDefinitelyObject_) << 21); - } - - public: - ICGetPropCallNativeCompiler(JSContext* cx, ICStub::Kind kind, ICStubCompiler::Engine engine, - ICStub* firstMonitorStub, HandleObject receiver, - HandleObject holder, HandleFunction getter, uint32_t pcOffset, - bool inputDefinitelyObject = false) - : ICGetPropCallGetter::Compiler(cx, kind, engine, firstMonitorStub, receiver, holder, - getter, pcOffset), - inputDefinitelyObject_(inputDefinitelyObject) - {} - - ICStub* getStub(ICStubSpace* space); -}; - // JSOP_NEWARRAY // JSOP_NEWINIT diff --git a/js/src/jit/SharedICList.h b/js/src/jit/SharedICList.h index 6bf41a0e3fb1..6a4b86eff9bd 100644 --- a/js/src/jit/SharedICList.h +++ b/js/src/jit/SharedICList.h @@ -35,7 +35,6 @@ namespace jit { _(Compare_Int32WithBoolean) \ \ _(GetProp_Fallback) \ - _(GetProp_CallNativeGlobal) \ _(GetProp_Generic) \ \ _(CacheIR_Monitored) \ diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index bb04a3b7c936..9ac0cb0b658d 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -1372,6 +1372,24 @@ ProxyGetPropertyByValue(JSContext* cx, HandleObject proxy, HandleValue idVal, return Proxy::get(cx, proxy, receiver, id, vp); } +bool +CallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj, + MutableHandleValue result) +{ + MOZ_ASSERT(callee->isNative()); + JSNative natfun = callee->native(); + + JS::AutoValueArray<2> vp(cx); + vp[0].setObject(*callee.get()); + vp[1].setObject(*obj.get()); + + if (!natfun(cx, 0, vp.begin())) + return false; + + result.set(vp[0]); + return true; +} + bool EqualStringsHelper(JSString* str1, JSString* str2) { diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index a1f386905902..3fe1d71eac4b 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -818,6 +818,11 @@ MOZ_MUST_USE bool ProxyGetPropertyByValue(JSContext* cx, HandleObject proxy, HandleValue idVal, MutableHandleValue vp); +MOZ_MUST_USE bool +CallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj, + MutableHandleValue result); + + MOZ_MUST_USE bool EqualStringsHelper(JSString* str1, JSString* str2); diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index 372b63322274..a5b3c691bebe 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -8436,9 +8436,11 @@ StoreAsmJSModuleInCache(AsmJSParser& parser, Module& module, ExclusiveContext* c size_t bytecodeSize, compiledSize; module.serializedSize(&bytecodeSize, &compiledSize); + MOZ_RELEASE_ASSERT(bytecodeSize == 0); + MOZ_RELEASE_ASSERT(compiledSize <= UINT32_MAX); - size_t serializedSize = 2 * sizeof(uint32_t) + - bytecodeSize + compiledSize + + size_t serializedSize = sizeof(uint32_t) + + compiledSize + moduleChars.serializedSize(); JS::OpenAsmJSCacheEntryForWriteOp open = cx->asmJSCacheOps().openEntryForWrite; @@ -8461,16 +8463,10 @@ StoreAsmJSModuleInCache(AsmJSParser& parser, Module& module, ExclusiveContext* c // between any two builds (regardless of platform, architecture, ...). // (The Module::assumptionsMatch() guard everything in the Module and // afterwards.) - MOZ_RELEASE_ASSERT(bytecodeSize <= UINT32_MAX); - MOZ_RELEASE_ASSERT(compiledSize <= UINT32_MAX); - cursor = WriteScalar(cursor, bytecodeSize); cursor = WriteScalar(cursor, compiledSize); - uint8_t* compiledBegin = cursor; - uint8_t* bytecodeBegin = compiledBegin + compiledSize;; - - module.serialize(bytecodeBegin, bytecodeSize, compiledBegin, compiledSize); - cursor = bytecodeBegin + bytecodeSize; + module.serialize(/* bytecodeBegin = */ nullptr, /* bytecodeSize = */ 0, cursor, compiledSize); + cursor += compiledSize; cursor = moduleChars.serialize(cursor); @@ -8501,33 +8497,29 @@ LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, bool* loaded size_t remain = entry.serializedSize; const uint8_t* cursor = entry.memory; - uint32_t bytecodeSize, compiledSize; - (cursor = ReadScalarChecked(cursor, &remain, &bytecodeSize)) && - (cursor = ReadScalarChecked(cursor, &remain, &compiledSize)); + uint32_t compiledSize; + cursor = ReadScalarChecked(cursor, &remain, &compiledSize); if (!cursor) return true; - const uint8_t* compiledBegin = cursor; - const uint8_t* bytecodeBegin = compiledBegin + compiledSize; - Assumptions assumptions; if (!assumptions.initBuildIdFromContext(cx)) return false; - if (!Module::assumptionsMatch(assumptions, compiledBegin, remain)) + if (!Module::assumptionsMatch(assumptions, cursor, remain)) return true; MutableAsmJSMetadata asmJSMetadata = cx->new_(); if (!asmJSMetadata) return false; - *module = Module::deserialize(bytecodeBegin, bytecodeSize, compiledBegin, compiledSize, - asmJSMetadata.get()); + *module = Module::deserialize(/* bytecodeBegin = */ nullptr, /* bytecodeSize = */ 0, + cursor, compiledSize, asmJSMetadata.get()); if (!*module) { ReportOutOfMemory(cx); return false; } - cursor = bytecodeBegin + bytecodeSize; + cursor += compiledSize; // Due to the hash comparison made by openEntryForRead, this should succeed // with high probability. diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp index dbd7a3737592..2aa9508174e7 100644 --- a/js/src/wasm/WasmBaselineCompile.cpp +++ b/js/src/wasm/WasmBaselineCompile.cpp @@ -512,7 +512,7 @@ class BaseCompiler Assembler::Condition latentIntCmp_; // Comparison operator, if latentOp_ == Compare, int types Assembler::DoubleCondition latentDoubleCmp_;// Comparison operator, if latentOp_ == Compare, float types - FuncCompileResults& compileResults_; + FuncOffsets offsets_; MacroAssembler& masm; // No '_' suffix - too tedious... AllocatableGeneralRegisterSet availGPR_; @@ -567,11 +567,12 @@ class BaseCompiler Decoder& decoder, const FuncBytes& func, const ValTypeVector& locals, - FuncCompileResults& compileResults); + TempAllocator* alloc, + MacroAssembler* masm); MOZ_MUST_USE bool init(); - void finish(); + FuncOffsets finish(); MOZ_MUST_USE bool emitFunction(); @@ -2053,7 +2054,7 @@ class BaseCompiler JitSpew(JitSpew_Codegen, "# Emitting wasm baseline code"); SigIdDesc sigId = env_.funcSigs[func_.index()]->id; - GenerateFunctionPrologue(masm, localSize_, sigId, &compileResults_.offsets()); + GenerateFunctionPrologue(masm, localSize_, sigId, &offsets_); MOZ_ASSERT(masm.framePushed() == uint32_t(localSize_)); @@ -2148,7 +2149,7 @@ class BaseCompiler // Restore the TLS register in case it was overwritten by the function. loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer)); - GenerateFunctionEpilogue(masm, localSize_, &compileResults_.offsets()); + GenerateFunctionEpilogue(masm, localSize_, &offsets_); #if defined(JS_ION_PERF) // FIXME - profiling code missing. Bug 1286948. @@ -2162,7 +2163,7 @@ class BaseCompiler masm.wasmEmitTrapOutOfLineCode(); - compileResults_.offsets().end = masm.currentOffset(); + offsets_.end = masm.currentOffset(); // A frame greater than 256KB is implausible, probably an attack, // so fail the compilation. @@ -7540,12 +7541,13 @@ BaseCompiler::BaseCompiler(const ModuleEnvironment& env, Decoder& decoder, const FuncBytes& func, const ValTypeVector& locals, - FuncCompileResults& compileResults) + TempAllocator* alloc, + MacroAssembler* masm) : env_(env), iter_(decoder, func.lineOrBytecode()), func_(func), lastReadCallSite_(0), - alloc_(compileResults.alloc()), + alloc_(*alloc), locals_(locals), localSize_(0), varLow_(0), @@ -7558,8 +7560,7 @@ BaseCompiler::BaseCompiler(const ModuleEnvironment& env, latentType_(ValType::I32), latentIntCmp_(Assembler::Equal), latentDoubleCmp_(Assembler::DoubleEqual), - compileResults_(compileResults), - masm(compileResults_.masm()), + masm(*masm), availGPR_(GeneralRegisterSet::All()), availFPU_(FloatRegisterSet::All()), #ifdef DEBUG @@ -7702,13 +7703,15 @@ BaseCompiler::init() return true; } -void +FuncOffsets BaseCompiler::finish() { MOZ_ASSERT(done(), "all bytes must be consumed"); MOZ_ASSERT(func_.callSiteLineNums().length() == lastReadCallSite_); masm.flushBuffer(); + + return offsets_; } static LiveRegisterSet @@ -7756,12 +7759,11 @@ js::wasm::BaselineCanCompile(const FunctionGenerator* fg) } bool -js::wasm::BaselineCompileFunction(CompileTask* task) +js::wasm::BaselineCompileFunction(CompileTask* task, FuncCompileUnit* unit) { - MOZ_ASSERT(task->mode() == CompileTask::CompileMode::Baseline); + MOZ_ASSERT(unit->mode() == CompileMode::Baseline); - const FuncBytes& func = task->func(); - FuncCompileResults& results = task->results(); + const FuncBytes& func = unit->func(); Decoder d(func.bytes()); @@ -7775,19 +7777,18 @@ js::wasm::BaselineCompileFunction(CompileTask* task) // The MacroAssembler will sometimes access the jitContext. - JitContext jitContext(&results.alloc()); + JitContext jitContext(&task->alloc()); // One-pass baseline compilation. - BaseCompiler f(task->env(), d, func, locals, results); + BaseCompiler f(task->env(), d, func, locals, &task->alloc(), &task->masm()); if (!f.init()) return false; if (!f.emitFunction()) return false; - f.finish(); - + unit->finish(f.finish()); return true; } diff --git a/js/src/wasm/WasmBaselineCompile.h b/js/src/wasm/WasmBaselineCompile.h index 3c7a0d2e8364..3fc43ba9606a 100644 --- a/js/src/wasm/WasmBaselineCompile.h +++ b/js/src/wasm/WasmBaselineCompile.h @@ -24,6 +24,7 @@ namespace wasm { class FunctionGenerator; class CompileTask; +class FuncCompileUnit; // Return true if BaselineCompileFunction can generate code for the // function held in the FunctionGenerator. If false is returned a @@ -39,7 +40,7 @@ BaselineCanCompile(const FunctionGenerator* fg); // Generate adequate code quickly. bool -BaselineCompileFunction(CompileTask* task); +BaselineCompileFunction(CompileTask* task, FuncCompileUnit* unit); } // namespace wasm } // namespace js diff --git a/js/src/wasm/WasmCompile.h b/js/src/wasm/WasmCompile.h index d5a19da97a08..87f2b16c675f 100644 --- a/js/src/wasm/WasmCompile.h +++ b/js/src/wasm/WasmCompile.h @@ -19,7 +19,6 @@ #ifndef wasm_compile_h #define wasm_compile_h -#include "wasm/WasmJS.h" #include "wasm/WasmModule.h" namespace js { @@ -40,7 +39,7 @@ struct CompileArgs { Assumptions assumptions; ScriptedCaller scriptedCaller; - MOZ_INIT_OUTSIDE_CTOR bool alwaysBaseline; + bool alwaysBaseline; CompileArgs(Assumptions&& assumptions, ScriptedCaller&& scriptedCaller) : assumptions(Move(assumptions)), diff --git a/js/src/wasm/WasmGenerator.cpp b/js/src/wasm/WasmGenerator.cpp index dcda4ab4519f..a89f9aa4a29e 100644 --- a/js/src/wasm/WasmGenerator.cpp +++ b/js/src/wasm/WasmGenerator.cpp @@ -55,6 +55,8 @@ ModuleGenerator::ModuleGenerator() startOfUnpatchedCallsites_(0), parallel_(false), outstanding_(0), + currentTask_(nullptr), + batchedBytecode_(0), activeFuncDef_(nullptr), startedFuncDefs_(false), finishedFuncDefs_(false), @@ -96,6 +98,8 @@ ModuleGenerator::~ModuleGenerator() } else { MOZ_ASSERT(!outstanding_); } + MOZ_ASSERT_IF(finishedFuncDefs_, !batchedBytecode_); + MOZ_ASSERT_IF(finishedFuncDefs_, !currentTask_); } bool @@ -391,42 +395,45 @@ ModuleGenerator::patchFarJumps(const TrapExitOffsetArray& trapExits) bool ModuleGenerator::finishTask(CompileTask* task) { - const FuncBytes& func = task->func(); - FuncCompileResults& results = task->results(); - masm_.haltingAlign(CodeAlignment); // Before merging in the new function's code, if calls in a prior function // body might go out of range, insert far jumps to extend the range. - if ((masm_.size() - startOfUnpatchedCallsites_) + results.masm().size() > JumpRange()) { + if ((masm_.size() - startOfUnpatchedCallsites_) + task->masm().size() > JumpRange()) { startOfUnpatchedCallsites_ = masm_.size(); if (!patchCallSites()) return false; } - // Offset the recorded FuncOffsets by the offset of the function in the - // whole module's code segment. uint32_t offsetInWhole = masm_.size(); - results.offsets().offsetBy(offsetInWhole); + for (const FuncCompileUnit& unit : task->units()) { + const FuncBytes& func = unit.func(); - // Add the CodeRange for this function. - uint32_t funcCodeRangeIndex = metadata_->codeRanges.length(); - if (!metadata_->codeRanges.emplaceBack(func.index(), func.lineOrBytecode(), results.offsets())) - return false; + // Offset the recorded FuncOffsets by the offset of the function in the + // whole module's code segment. + FuncOffsets offsets = unit.offsets(); + offsets.offsetBy(offsetInWhole); - MOZ_ASSERT(!funcIsCompiled(func.index())); - funcToCodeRange_[func.index()] = funcCodeRangeIndex; + // Add the CodeRange for this function. + uint32_t funcCodeRangeIndex = metadata_->codeRanges.length(); + if (!metadata_->codeRanges.emplaceBack(func.index(), func.lineOrBytecode(), offsets)) + return false; + + MOZ_ASSERT(!funcIsCompiled(func.index())); + funcToCodeRange_[func.index()] = funcCodeRangeIndex; + } // Merge the compiled results into the whole-module masm. mozilla::DebugOnly sizeBefore = masm_.size(); - if (!masm_.asmMergeWith(results.masm())) + if (!masm_.asmMergeWith(task->masm())) + return false; + MOZ_ASSERT(masm_.size() == offsetInWhole + task->masm().size()); + + if (!task->reset(&freeFuncBytes_)) return false; - MOZ_ASSERT(masm_.size() == offsetInWhole + results.masm().size()); - UniqueBytes recycled; - task->reset(&recycled); freeTasks_.infallibleAppend(task); - return freeBytes_.emplaceBack(Move(recycled)); + return true; } bool @@ -864,15 +871,6 @@ ModuleGenerator::startFuncDefs() for (size_t i = 0; i < numTasks; i++) freeTasks_.infallibleAppend(&tasks_[i]); - if (!freeBytes_.reserve(numTasks)) - return false; - for (size_t i = 0; i < numTasks; i++) { - auto bytes = js::MakeUnique(); - if (!bytes) - return false; - freeBytes_.infallibleAppend(Move(bytes)); - } - startedFuncDefs_ = true; MOZ_ASSERT(!finishedFuncDefs_); return true; @@ -885,58 +883,81 @@ ModuleGenerator::startFuncDef(uint32_t lineOrBytecode, FunctionGenerator* fg) MOZ_ASSERT(!activeFuncDef_); MOZ_ASSERT(!finishedFuncDefs_); - if (!freeBytes_.empty()) { - fg->bytes_ = Move(freeBytes_.back()); - freeBytes_.popBack(); + if (!freeFuncBytes_.empty()) { + fg->funcBytes_ = Move(freeFuncBytes_.back()); + freeFuncBytes_.popBack(); } else { - fg->bytes_ = js::MakeUnique(); - if (!fg->bytes_) + fg->funcBytes_ = js::MakeUnique(); + if (!fg->funcBytes_) return false; } - fg->lineOrBytecode_ = lineOrBytecode; + if (!currentTask_) { + if (freeTasks_.empty() && !finishOutstandingTask()) + return false; + currentTask_ = freeTasks_.popCopy(); + } + + fg->funcBytes_->setLineOrBytecode(lineOrBytecode); fg->m_ = this; activeFuncDef_ = fg; return true; } +bool +ModuleGenerator::launchBatchCompile() +{ + MOZ_ASSERT(currentTask_); + + size_t numBatchedFuncs = currentTask_->units().length(); + MOZ_ASSERT(numBatchedFuncs); + + if (parallel_) { + if (!StartOffThreadWasmCompile(currentTask_)) + return false; + outstanding_++; + } else { + if (!CompileFunction(currentTask_)) + return false; + if (!finishTask(currentTask_)) + return false; + } + + currentTask_ = nullptr; + batchedBytecode_ = 0; + + numFinishedFuncDefs_ += numBatchedFuncs; + return true; +} + bool ModuleGenerator::finishFuncDef(uint32_t funcIndex, FunctionGenerator* fg) { MOZ_ASSERT(activeFuncDef_ == fg); - auto func = js::MakeUnique(Move(fg->bytes_), - funcIndex, - funcSig(funcIndex), - fg->lineOrBytecode_, - Move(fg->callSiteLineNums_)); - if (!func) - return false; + UniqueFuncBytes func = Move(fg->funcBytes_); + + func->setFunc(funcIndex, &funcSig(funcIndex)); auto mode = alwaysBaseline_ && BaselineCanCompile(fg) - ? CompileTask::CompileMode::Baseline - : CompileTask::CompileMode::Ion; + ? CompileMode::Baseline + : CompileMode::Ion; - if (freeTasks_.empty() && !finishOutstandingTask()) + CheckedInt newBatched = func->bytes().length(); + if (mode == CompileMode::Ion) + newBatched *= JitOptions.wasmBatchIonScaleFactor; + newBatched += batchedBytecode_; + + if (!currentTask_->units().emplaceBack(Move(func), mode)) return false; - CompileTask* task = freeTasks_.popCopy(); - task->init(Move(func), mode); - - if (parallel_) { - if (!StartOffThreadWasmCompile(task)) - return false; - outstanding_++; - } else { - if (!CompileFunction(task)) - return false; - if (!finishTask(task)) - return false; - } + if (newBatched.isValid() && newBatched.value() < JitOptions.wasmBatchThreshold) + batchedBytecode_ = newBatched.value(); + else if (!launchBatchCompile()) + return false; fg->m_ = nullptr; activeFuncDef_ = nullptr; - numFinishedFuncDefs_++; return true; } @@ -947,6 +968,9 @@ ModuleGenerator::finishFuncDefs() MOZ_ASSERT(!activeFuncDef_); MOZ_ASSERT(!finishedFuncDefs_); + if (currentTask_ && !launchBatchCompile()) + return false; + while (outstanding_ > 0) { if (!finishOutstandingTask()) return false; @@ -1146,14 +1170,18 @@ wasm::CompileFunction(CompileTask* task) TraceLoggerThread* logger = TraceLoggerForCurrentThread(); AutoTraceLog logCompile(logger, TraceLogger_WasmCompilation); - switch (task->mode()) { - case wasm::CompileTask::CompileMode::Ion: - return wasm::IonCompileFunction(task); - case wasm::CompileTask::CompileMode::Baseline: - return wasm::BaselineCompileFunction(task); - case wasm::CompileTask::CompileMode::None: - break; + for (FuncCompileUnit& unit : task->units()) { + switch (unit.mode()) { + case CompileMode::Ion: + if (!IonCompileFunction(task, &unit)) + return false; + break; + case CompileMode::Baseline: + if (!BaselineCompileFunction(task, &unit)) + return false; + break; + } } - MOZ_CRASH("Uninitialized task"); + return true; } diff --git a/js/src/wasm/WasmGenerator.h b/js/src/wasm/WasmGenerator.h index 9e0fab45f1cf..daa98a005434 100644 --- a/js/src/wasm/WasmGenerator.h +++ b/js/src/wasm/WasmGenerator.h @@ -36,124 +36,158 @@ struct CompileArgs; class FunctionGenerator; -typedef Vector UniqueBytesVector; - // The FuncBytes class represents a single, concurrently-compilable function. // A FuncBytes object is composed of the wasm function body bytes along with the // ambient metadata describing the function necessary to compile it. class FuncBytes { - UniqueBytes bytes_; + Bytes bytes_; uint32_t index_; - const SigWithId& sig_; + const SigWithId* sig_; uint32_t lineOrBytecode_; Uint32Vector callSiteLineNums_; public: - FuncBytes(UniqueBytes bytes, - uint32_t index, - const SigWithId& sig, - uint32_t lineOrBytecode, - Uint32Vector&& callSiteLineNums) - : bytes_(Move(bytes)), - index_(index), - sig_(sig), - lineOrBytecode_(lineOrBytecode), - callSiteLineNums_(Move(callSiteLineNums)) + FuncBytes() + : index_(UINT32_MAX), + sig_(nullptr), + lineOrBytecode_(UINT32_MAX) {} - Bytes& bytes() { return *bytes_; } - const Bytes& bytes() const { return *bytes_; } - UniqueBytes recycle() { return Move(bytes_); } + Bytes& bytes() { + return bytes_; + } + MOZ_MUST_USE bool addCallSiteLineNum(uint32_t lineno) { + return callSiteLineNums_.append(lineno); + } + void setLineOrBytecode(uint32_t lineOrBytecode) { + MOZ_ASSERT(lineOrBytecode_ == UINT32_MAX); + lineOrBytecode_ = lineOrBytecode; + } + void setFunc(uint32_t index, const SigWithId* sig) { + MOZ_ASSERT(index_ == UINT32_MAX); + MOZ_ASSERT(sig_ == nullptr); + index_ = index; + sig_ = sig; + } + void reset() { + bytes_.clear(); + index_ = UINT32_MAX; + sig_ = nullptr; + lineOrBytecode_ = UINT32_MAX; + callSiteLineNums_.clear(); + } + + const Bytes& bytes() const { return bytes_; } uint32_t index() const { return index_; } - const SigWithId& sig() const { return sig_; } + const SigWithId& sig() const { return *sig_; } uint32_t lineOrBytecode() const { return lineOrBytecode_; } const Uint32Vector& callSiteLineNums() const { return callSiteLineNums_; } }; typedef UniquePtr UniqueFuncBytes; +typedef Vector UniqueFuncBytesVector; -// The FuncCompileResults class contains the results of compiling a single -// function body, ready to be merged into the whole-module MacroAssembler. - -class FuncCompileResults +enum class CompileMode { - jit::TempAllocator alloc_; - jit::MacroAssembler masm_; - FuncOffsets offsets_; - - FuncCompileResults(const FuncCompileResults&) = delete; - FuncCompileResults& operator=(const FuncCompileResults&) = delete; - - public: - explicit FuncCompileResults(LifoAlloc& lifo) - : alloc_(&lifo), - masm_(jit::MacroAssembler::WasmToken(), alloc_) - {} - - jit::TempAllocator& alloc() { return alloc_; } - jit::MacroAssembler& masm() { return masm_; } - FuncOffsets& offsets() { return offsets_; } + Baseline, + Ion }; -// A CompileTask represents the task of compiling a single function body. An -// CompileTask is filled with the wasm code to be compiled on the main -// validation thread, sent off to a compilation helper thread which creates -// the FuncCompileResults, and finally sent back to the validation thread. To -// save time allocating and freeing memory, CompileTasks are reset() and -// reused. +// FuncCompileUnit contains all the data necessary to produce and store the +// results of a single function's compilation. + +class FuncCompileUnit +{ + UniqueFuncBytes func_; + CompileMode mode_; + FuncOffsets offsets_; + DebugOnly finished_; + + public: + FuncCompileUnit(UniqueFuncBytes func, CompileMode mode) + : func_(Move(func)), + mode_(mode), + finished_(false) + {} + + const FuncBytes& func() const { return *func_; } + CompileMode mode() const { return mode_; } + FuncOffsets offsets() const { MOZ_ASSERT(finished_); return offsets_; } + + void finish(FuncOffsets offsets) { + MOZ_ASSERT(!finished_); + offsets_ = offsets; + finished_ = true; + } + + UniqueFuncBytes recycle() { + MOZ_ASSERT(finished_); + func_->reset(); + return Move(func_); + } +}; + +typedef Vector FuncCompileUnitVector; + +// A CompileTask represents the task of compiling a batch of functions. It is +// filled with a certain number of function's bodies that are sent off to a +// compilation helper thread, which fills in the resulting code offsets, and +// finally sent back to the validation thread. To save time allocating and +// freeing memory, CompileTasks are reset() and reused. class CompileTask { - public: - enum class CompileMode { None, Baseline, Ion }; - - private: - const ModuleEnvironment& env_; - LifoAlloc lifo_; - UniqueFuncBytes func_; - CompileMode mode_; - Maybe results_; + const ModuleEnvironment& env_; + LifoAlloc lifo_; + Maybe alloc_; + Maybe masm_; + FuncCompileUnitVector units_; CompileTask(const CompileTask&) = delete; CompileTask& operator=(const CompileTask&) = delete; + void init() { + alloc_.emplace(&lifo_); + masm_.emplace(jit::MacroAssembler::WasmToken(), *alloc_); + } + public: CompileTask(const ModuleEnvironment& env, size_t defaultChunkSize) - : env_(env), lifo_(defaultChunkSize), func_(nullptr), mode_(CompileMode::None) - {} + : env_(env), + lifo_(defaultChunkSize) + { + init(); + } LifoAlloc& lifo() { return lifo_; } + jit::TempAllocator& alloc() { + return *alloc_; + } const ModuleEnvironment& env() const { return env_; } - void init(UniqueFuncBytes func, CompileMode mode) { - MOZ_ASSERT(!func_); - func_ = Move(func); - results_.emplace(lifo_); - mode_ = mode; + jit::MacroAssembler& masm() { + return *masm_; } - CompileMode mode() const { - return mode_; + FuncCompileUnitVector& units() { + return units_; } - const FuncBytes& func() const { - MOZ_ASSERT(func_); - return *func_; - } - FuncCompileResults& results() { - return *results_; - } - void reset(UniqueBytes* recycled) { - if (func_) { - *recycled = Move(func_->recycle()); - (*recycled)->clear(); + bool reset(UniqueFuncBytesVector* freeFuncBytes) { + for (FuncCompileUnit& unit : units_) { + if (!freeFuncBytes->emplaceBack(Move(unit.recycle()))) + return false; } - func_.reset(nullptr); - results_.reset(); + + units_.clear(); + masm_.reset(); + alloc_.reset(); lifo_.releaseAll(); - mode_ = CompileMode::None; + + init(); + return true; } }; @@ -196,7 +230,9 @@ class MOZ_STACK_CLASS ModuleGenerator uint32_t outstanding_; CompileTaskVector tasks_; CompileTaskPtrVector freeTasks_; - UniqueBytesVector freeBytes_; + UniqueFuncBytesVector freeFuncBytes_; + CompileTask* currentTask_; + uint32_t batchedBytecode_; // Assertions DebugOnly activeFuncDef_; @@ -206,6 +242,7 @@ class MOZ_STACK_CLASS ModuleGenerator bool funcIsCompiled(uint32_t funcIndex) const; const CodeRange& funcCodeRange(uint32_t funcIndex) const; + uint32_t numFuncImports() const; MOZ_MUST_USE bool patchCallSites(TrapExitOffsetArray* maybeTrapExits = nullptr); MOZ_MUST_USE bool patchFarJumps(const TrapExitOffsetArray& trapExits); MOZ_MUST_USE bool finishTask(CompileTask* task); @@ -217,12 +254,11 @@ class MOZ_STACK_CLASS ModuleGenerator MOZ_MUST_USE bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff); MOZ_MUST_USE bool allocateGlobal(GlobalDesc* global); + MOZ_MUST_USE bool launchBatchCompile(); + MOZ_MUST_USE bool initAsmJS(Metadata* asmJSMetadata); MOZ_MUST_USE bool initWasm(); - // Functions declarations: - uint32_t numFuncImports() const; - public: explicit ModuleGenerator(); ~ModuleGenerator(); @@ -275,10 +311,11 @@ class MOZ_STACK_CLASS ModuleGenerator }; // A FunctionGenerator encapsulates the generation of a single function body. -// ModuleGenerator::startFunc must be called after construction and before doing -// anything else. After the body is complete, ModuleGenerator::finishFunc must -// be called before the FunctionGenerator is destroyed and the next function is -// started. +// ModuleGenerator::startFuncDef must be called after construction and before +// doing anything else. +// +// After the body is complete, ModuleGenerator::finishFuncDef must be called +// before the FunctionGenerator is destroyed and the next function is started. class MOZ_STACK_CLASS FunctionGenerator { @@ -288,16 +325,11 @@ class MOZ_STACK_CLASS FunctionGenerator bool usesSimd_; bool usesAtomics_; - // Data created during function generation, then handed over to the - // FuncBytes in ModuleGenerator::finishFunc(). - UniqueBytes bytes_; - Uint32Vector callSiteLineNums_; - - uint32_t lineOrBytecode_; + UniqueFuncBytes funcBytes_; public: FunctionGenerator() - : m_(nullptr), usesSimd_(false), usesAtomics_(false), bytes_(nullptr), lineOrBytecode_(0) + : m_(nullptr), usesSimd_(false), usesAtomics_(false), funcBytes_(nullptr) {} bool usesSimd() const { @@ -315,10 +347,10 @@ class MOZ_STACK_CLASS FunctionGenerator } Bytes& bytes() { - return *bytes_; + return funcBytes_->bytes(); } MOZ_MUST_USE bool addCallSiteLineNum(uint32_t lineno) { - return callSiteLineNums_.append(lineno); + return funcBytes_->addCallSiteLineNum(lineno); } }; diff --git a/js/src/wasm/WasmIonCompile.cpp b/js/src/wasm/WasmIonCompile.cpp index 565196d9d19f..b1ffb0c9e52a 100644 --- a/js/src/wasm/WasmIonCompile.cpp +++ b/js/src/wasm/WasmIonCompile.cpp @@ -168,8 +168,6 @@ class FunctionCompiler uint32_t blockDepth_; ControlFlowPatchsVector blockPatches_; - FuncCompileResults& compileResults_; - // TLS pointer argument to the current function. MWasmParameter* tlsPointer_; @@ -178,8 +176,7 @@ class FunctionCompiler Decoder& decoder, const FuncBytes& func, const ValTypeVector& locals, - MIRGenerator& mirGen, - FuncCompileResults& compileResults) + MIRGenerator& mirGen) : env_(env), iter_(decoder, func.lineOrBytecode()), func_(func), @@ -194,14 +191,12 @@ class FunctionCompiler maxStackArgBytes_(0), loopDepth_(0), blockDepth_(0), - compileResults_(compileResults), tlsPointer_(nullptr) {} const ModuleEnvironment& env() const { return env_; } IonOpIter& iter() { return iter_; } TempAllocator& alloc() const { return alloc_; } - MacroAssembler& masm() const { return compileResults_.masm(); } const Sig& sig() const { return func_.sig(); } TrapOffset trapOffset() const { @@ -3697,12 +3692,12 @@ EmitExpr(FunctionCompiler& f) } bool -wasm::IonCompileFunction(CompileTask* task) +wasm::IonCompileFunction(CompileTask* task, FuncCompileUnit* unit) { - MOZ_ASSERT(task->mode() == CompileTask::CompileMode::Ion); + MOZ_ASSERT(unit->mode() == CompileMode::Ion); - const FuncBytes& func = task->func(); - FuncCompileResults& results = task->results(); + const FuncBytes& func = unit->func(); + const ModuleEnvironment& env = task->env(); Decoder d(func.bytes()); @@ -3711,18 +3706,18 @@ wasm::IonCompileFunction(CompileTask* task) ValTypeVector locals; if (!locals.appendAll(func.sig().args())) return false; - if (!DecodeLocalEntries(d, task->env().kind, &locals)) + if (!DecodeLocalEntries(d, env.kind, &locals)) return false; // Set up for Ion compilation. - JitContext jitContext(&results.alloc()); + JitContext jitContext(&task->alloc()); const JitCompileOptions options; - MIRGraph graph(&results.alloc()); + MIRGraph graph(&task->alloc()); CompileInfo compileInfo(locals.length()); - MIRGenerator mir(nullptr, options, &results.alloc(), &graph, &compileInfo, + MIRGenerator mir(nullptr, options, &task->alloc(), &graph, &compileInfo, IonOptimizations.get(OptimizationLevel::Wasm)); - mir.initMinWasmHeapLength(task->env().minMemoryLength); + mir.initMinWasmHeapLength(env.minMemoryLength); // Capture the prologue's trap site before decoding the function. @@ -3730,7 +3725,7 @@ wasm::IonCompileFunction(CompileTask* task) // Build MIR graph { - FunctionCompiler f(task->env(), d, func, locals, mir, results); + FunctionCompiler f(env, d, func, locals, mir); if (!f.init()) return false; @@ -3770,11 +3765,15 @@ wasm::IonCompileFunction(CompileTask* task) if (!lir) return false; - SigIdDesc sigId = task->env().funcSigs[func.index()]->id; + SigIdDesc sigId = env.funcSigs[func.index()]->id; - CodeGenerator codegen(&mir, lir, &results.masm()); - if (!codegen.generateWasm(sigId, prologueTrapOffset, &results.offsets())) + CodeGenerator codegen(&mir, lir, &task->masm()); + + FuncOffsets offsets; + if (!codegen.generateWasm(sigId, prologueTrapOffset, &offsets)) return false; + + unit->finish(offsets); } return true; diff --git a/js/src/wasm/WasmIonCompile.h b/js/src/wasm/WasmIonCompile.h index e8636a90f90f..a228dbde871d 100644 --- a/js/src/wasm/WasmIonCompile.h +++ b/js/src/wasm/WasmIonCompile.h @@ -25,10 +25,11 @@ namespace js { namespace wasm { class CompileTask; +class FuncCompileUnit; // Generates very fast code at the expense of compilation time. MOZ_MUST_USE bool -IonCompileFunction(CompileTask* task); +IonCompileFunction(CompileTask* task, FuncCompileUnit* unit); } // namespace wasm } // namespace js diff --git a/js/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp index 69b60f5f80b4..5a530860f79d 100644 --- a/js/src/wasm/WasmModule.cpp +++ b/js/src/wasm/WasmModule.cpp @@ -141,7 +141,7 @@ LinkData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const Module::serializedSize(size_t* maybeBytecodeSize, size_t* maybeCompiledSize) const { if (maybeBytecodeSize) - *maybeBytecodeSize = SerializedPodVectorSize(bytecode_->bytes); + *maybeBytecodeSize = bytecode_->bytes.length(); if (maybeCompiledSize) { *maybeCompiledSize = assumptions_.serializedSize() + @@ -164,9 +164,13 @@ Module::serialize(uint8_t* maybeBytecodeBegin, size_t maybeBytecodeSize, if (maybeBytecodeBegin) { // Bytecode deserialization is not guarded by Assumptions and thus must not - // change incompatibly between builds. + // change incompatibly between builds. Thus, for simplicity, the format + // of the bytecode file is simply a .wasm file (thus, backwards + // compatibility is ensured by backwards compatibility of the wasm + // binary format). - uint8_t* bytecodeEnd = SerializePodVector(maybeBytecodeBegin, bytecode_->bytes); + const Bytes& bytes = bytecode_->bytes; + uint8_t* bytecodeEnd = WriteBytes(maybeBytecodeBegin, bytes.begin(), bytes.length()); MOZ_RELEASE_ASSERT(bytecodeEnd == maybeBytecodeBegin + maybeBytecodeSize); } @@ -204,14 +208,10 @@ Module::deserialize(const uint8_t* bytecodeBegin, size_t bytecodeSize, Metadata* maybeMetadata) { MutableBytes bytecode = js_new(); - if (!bytecode) + if (!bytecode || !bytecode->bytes.initLengthUninitialized(bytecodeSize)) return nullptr; - const uint8_t* bytecodeEnd = DeserializePodVector(bytecodeBegin, &bytecode->bytes); - if (!bytecodeEnd) - return nullptr; - - MOZ_RELEASE_ASSERT(bytecodeEnd == bytecodeBegin + bytecodeSize); + memcpy(bytecode->bytes.begin(), bytecodeBegin, bytecodeSize); Assumptions assumptions; const uint8_t* cursor = assumptions.deserialize(compiledBegin, compiledSize); @@ -344,15 +344,15 @@ wasm::DeserializeModule(PRFileDesc* bytecodeFile, PRFileDesc* maybeCompiledFile, compiledMapping.get(), compiledInfo.size); } + // Since the compiled file's assumptions don't match, we must recompile from + // bytecode. The bytecode file format is simply that of a .wasm (see + // Module::serialize). + MutableBytes bytecode = js_new(); - if (!bytecode) + if (!bytecode || !bytecode->bytes.initLengthUninitialized(bytecodeInfo.size)) return nullptr; - const uint8_t* bytecodeEnd = DeserializePodVector(bytecodeMapping.get(), &bytecode->bytes); - if (!bytecodeEnd) - return nullptr; - - MOZ_RELEASE_ASSERT(bytecodeEnd == bytecodeMapping.get() + bytecodeInfo.size); + memcpy(bytecode->bytes.begin(), bytecodeMapping.get(), bytecodeInfo.size); ScriptedCaller scriptedCaller; scriptedCaller.filename = Move(filename); diff --git a/js/src/wasm/WasmSerialize.h b/js/src/wasm/WasmSerialize.h index 71c67d5abc1b..79d759b27cb5 100644 --- a/js/src/wasm/WasmSerialize.h +++ b/js/src/wasm/WasmSerialize.h @@ -137,7 +137,7 @@ static inline uint8_t* SerializePodVector(uint8_t* cursor, const mozilla::Vector& vec) { // This binary format must not change without taking into consideration the - // constraints in Assumptions::serialize and Module::serialize. + // constraints in Assumptions::serialize. cursor = WriteScalar(cursor, vec.length()); cursor = WriteBytes(cursor, vec.begin(), vec.length() * sizeof(T)); diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index 796eca4098dd..ccdf9ebd972d 100644 --- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -53,6 +53,7 @@ #include "mozilla/dom/ShadowRoot.h" #include "mozilla/dom/URL.h" #include "mozilla/AsyncEventDispatcher.h" +#include "mozilla/ServoBindings.h" #include "mozilla/StyleSheet.h" #include "mozilla/StyleSheetInlines.h" #include "mozilla/ConsoleReportCollector.h" @@ -1405,29 +1406,27 @@ Loader::InsertSheetInDoc(StyleSheet* aSheet, nsresult Loader::InsertChildSheet(StyleSheet* aSheet, StyleSheet* aParentSheet, - ImportRule* aParentRule) + ImportRule* aGeckoParentRule, + const RawServoImportRule* aServoParentRule) { LOG(("css::Loader::InsertChildSheet")); - NS_PRECONDITION(aSheet, "Nothing to insert"); - NS_PRECONDITION(aParentSheet, "Need a parent to insert into"); - NS_PRECONDITION(aParentSheet, "How did we get imported?"); - - // XXXheycam The InsertChildSheet API doesn't work with ServoStyleSheets, - // since they won't have Gecko ImportRules in them. - if (aSheet->IsServo()) { - return NS_ERROR_FAILURE; + MOZ_ASSERT(aSheet, "Nothing to insert"); + MOZ_ASSERT(aParentSheet, "Need a parent to insert into"); + MOZ_ASSERT_IF(aSheet->IsGecko(), aGeckoParentRule && !aServoParentRule); + MOZ_ASSERT_IF(aSheet->IsServo(), aServoParentRule && !aGeckoParentRule); + if (aSheet->IsGecko()) { + // child sheets should always start out enabled, even if they got + // cloned off of top-level sheets which were disabled + aSheet->AsGecko()->SetEnabled(true); + aGeckoParentRule->SetSheet(aSheet->AsGecko()); // This sets the ownerRule on the sheet + } else { + RefPtr sheet = + Servo_ImportRule_GetSheet(aServoParentRule).Consume(); + aSheet->AsServo()->SetSheetForImport(sheet); } - - // child sheets should always start out enabled, even if they got - // cloned off of top-level sheets which were disabled - aSheet->AsGecko()->SetEnabled(true); - aParentSheet->AppendStyleSheet(aSheet); - aParentRule->SetSheet(aSheet->AsGecko()); // This sets the ownerRule on the sheet LOG((" Inserting into parent sheet")); - // LOG((" Inserting into parent sheet at position %d", insertionPoint)); - return NS_OK; } @@ -1775,7 +1774,8 @@ Loader::ParseSheet(const nsAString& aInput, aLoadData->mLineNumber); } else { rv = - aLoadData->mSheet->AsServo()->ParseSheet(aInput, sheetURI, baseURI, + aLoadData->mSheet->AsServo()->ParseSheet(this, + aInput, sheetURI, baseURI, aLoadData->mSheet->Principal(), aLoadData->mLineNumber); } @@ -1991,7 +1991,7 @@ Loader::LoadInlineStyle(nsIContent* aElement, bool* aIsAlternate) { LOG(("css::Loader::LoadInlineStyle")); - NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?"); + MOZ_ASSERT(mParsingDatas.IsEmpty(), "We're in the middle of a parse?"); *aCompleted = true; @@ -2197,13 +2197,19 @@ nsresult Loader::LoadChildSheet(StyleSheet* aParentSheet, nsIURI* aURL, nsMediaList* aMedia, - ImportRule* aParentRule, + ImportRule* aGeckoParentRule, + const RawServoImportRule* aServoParentRule, LoaderReusableStyleSheets* aReusableSheets) { LOG(("css::Loader::LoadChildSheet")); NS_PRECONDITION(aURL, "Must have a URI to load"); NS_PRECONDITION(aParentSheet, "Must have a parent sheet"); + // Servo doesn't support reusable sheets. + MOZ_ASSERT_IF(aReusableSheets, aParentSheet->IsGecko()); + MOZ_ASSERT_IF(aParentSheet->IsGecko(), aGeckoParentRule && !aServoParentRule); + MOZ_ASSERT_IF(aParentSheet->IsServo(), aServoParentRule && !aGeckoParentRule); + if (!mEnabled) { LOG_WARN((" Not enabled")); return NS_ERROR_NOT_AVAILABLE; @@ -2215,7 +2221,11 @@ Loader::LoadChildSheet(StyleSheet* aParentSheet, // check for an owning document: if none, don't bother walking up the parent // sheets - if (aParentSheet->GetOwningDocument()) { + // + // FIXME(emilio): Figure out whether this walk up is necessary (try seems + // green without it), and fix the parenting of stylesheets in the servo case + // if that's the case. + if (aParentSheet->GetOwningDocument() && aParentSheet->IsGecko()) { StyleSheet* topSheet = aParentSheet; while (StyleSheet* parent = topSheet->GetParentSheet()) { topSheet = parent; @@ -2266,7 +2276,7 @@ Loader::LoadChildSheet(StyleSheet* aParentSheet, StyleSheetState state; if (aReusableSheets && aReusableSheets->FindReusableStyleSheet(aURL, reusableSheet)) { sheet = reusableSheet; - aParentRule->SetSheet(reusableSheet); + aGeckoParentRule->SetSheet(reusableSheet); state = eSheetComplete; } else { bool isAlternate; @@ -2283,7 +2293,8 @@ Loader::LoadChildSheet(StyleSheet* aParentSheet, PrepareSheet(sheet, empty, empty, aMedia, nullptr, isAlternate); } - rv = InsertChildSheet(sheet, aParentSheet, aParentRule); + rv = InsertChildSheet(sheet, aParentSheet, aGeckoParentRule, + aServoParentRule); NS_ENSURE_SUCCESS(rv, rv); if (state == eSheetComplete) { diff --git a/layout/style/Loader.h b/layout/style/Loader.h index 209783a80422..a67498d67f65 100644 --- a/layout/style/Loader.h +++ b/layout/style/Loader.h @@ -285,15 +285,19 @@ public: * @param aParentSheet the parent of this child sheet * @param aURL the URL of the child sheet * @param aMedia the already-parsed media list for the child sheet - * @param aRule the @import rule importing this child. This is used to - * properly order the child sheet list of aParentSheet. + * @param aGeckoParentRule the @import rule importing this child, when using + * Gecko's style system. This is used to properly + * order the child sheet list of aParentSheet. + * @param aServoParentRule the @import rule importing this child, when using + * Servo's style system. * @param aSavedSheets any saved style sheets which could be reused * for this load */ nsresult LoadChildSheet(StyleSheet* aParentSheet, nsIURI* aURL, nsMediaList* aMedia, - ImportRule* aRule, + ImportRule* aGeckoParentRule, + const RawServoImportRule* aServoParentRule, LoaderReusableStyleSheets* aSavedSheets); /** @@ -484,7 +488,8 @@ private: nsresult InsertChildSheet(StyleSheet* aSheet, StyleSheet* aParentSheet, - ImportRule* aParentRule); + ImportRule* aGeckoParentRule, + const RawServoImportRule* aServoParentRule); nsresult InternalLoadNonDocumentSheet(nsIURI* aURL, bool aIsPreload, diff --git a/layout/style/ServoArcTypeList.h b/layout/style/ServoArcTypeList.h index ead8d80ef71e..8ebead199656 100644 --- a/layout/style/ServoArcTypeList.h +++ b/layout/style/ServoArcTypeList.h @@ -11,3 +11,4 @@ SERVO_ARC_TYPE(StyleSheet, RawServoStyleSheet) SERVO_ARC_TYPE(ComputedValues, ServoComputedValues) SERVO_ARC_TYPE(DeclarationBlock, RawServoDeclarationBlock) SERVO_ARC_TYPE(StyleRule, RawServoStyleRule) +SERVO_ARC_TYPE(ImportRule, RawServoImportRule) diff --git a/layout/style/ServoBindingList.h b/layout/style/ServoBindingList.h index 09280f6d1675..b7265df4deb7 100644 --- a/layout/style/ServoBindingList.h +++ b/layout/style/ServoBindingList.h @@ -26,12 +26,26 @@ SERVO_BINDING_FUNC(Servo_Element_ShouldTraverse, bool, RawGeckoElementBorrowed n SERVO_BINDING_FUNC(Servo_StyleSheet_Empty, RawServoStyleSheetStrong, mozilla::css::SheetParsingMode parsing_mode) SERVO_BINDING_FUNC(Servo_StyleSheet_FromUTF8Bytes, RawServoStyleSheetStrong, + mozilla::css::Loader* loader, + mozilla::ServoStyleSheet* gecko_stylesheet, const nsACString* data, mozilla::css::SheetParsingMode parsing_mode, const nsACString* base_url, ThreadSafeURIHolder* base, ThreadSafeURIHolder* referrer, ThreadSafePrincipalHolder* principal) +SERVO_BINDING_FUNC(Servo_ImportRule_GetSheet, + RawServoStyleSheetStrong, + const RawServoImportRuleBorrowed import_rule) +SERVO_BINDING_FUNC(Servo_StyleSheet_ClearAndUpdate, + void, + RawServoStyleSheetBorrowed stylesheet, + mozilla::css::Loader* loader, + mozilla::ServoStyleSheet* gecko_stylesheet, + const nsACString* data, + ThreadSafeURIHolder* base, + ThreadSafeURIHolder* referrer, + ThreadSafePrincipalHolder* principal) SERVO_BINDING_FUNC(Servo_StyleSheet_HasRules, bool, RawServoStyleSheetBorrowed sheet) SERVO_BINDING_FUNC(Servo_StyleSheet_GetRules, ServoCssRulesStrong, diff --git a/layout/style/ServoBindings.cpp b/layout/style/ServoBindings.cpp index 86299c8adfb2..352388a14afe 100644 --- a/layout/style/ServoBindings.cpp +++ b/layout/style/ServoBindings.cpp @@ -10,6 +10,7 @@ #include "StyleStructContext.h" #include "gfxFontFamilyList.h" #include "nsAttrValueInlines.h" +#include "nsCSSParser.h" #include "nsCSSRuleProcessor.h" #include "nsContentUtils.h" #include "nsDOMTokenList.h" @@ -20,6 +21,7 @@ #include "nsINode.h" #include "nsIPrincipal.h" #include "nsNameSpaceManager.h" +#include "nsNetUtil.h" #include "nsRuleNode.h" #include "nsString.h" #include "nsStyleStruct.h" @@ -1027,6 +1029,40 @@ Gecko_CSSValue_GetArrayItem(nsCSSValueBorrowedMut aCSSValue, int32_t aIndex) return &aCSSValue->GetArrayValue()->Item(aIndex); } +void +Gecko_LoadStyleSheet(css::Loader* aLoader, + ServoStyleSheet* aParent, + RawServoImportRuleBorrowed aImportRule, + const uint8_t* aURLString, + uint32_t aURLStringLength, + const uint8_t* aMediaString, + uint32_t aMediaStringLength) +{ + MOZ_ASSERT(aLoader, "Should've catched this before"); + MOZ_ASSERT(aParent, "Only used for @import, so parent should exist!"); + MOZ_ASSERT(aURLString, "Invalid URLs shouldn't be loaded!"); + RefPtr media = new nsMediaList(); + if (aMediaStringLength) { + MOZ_ASSERT(aMediaString); + // TODO(emilio, bug 1325878): This is not great, though this is going away + // soon anyway, when we can have a Servo-backed nsMediaList. + nsDependentCSubstring medium(reinterpret_cast(aMediaString), + aMediaStringLength); + nsCSSParser mediumParser(aLoader); + mediumParser.ParseMediaList( + NS_ConvertUTF8toUTF16(medium), nullptr, 0, media); + } + + nsDependentCSubstring urlSpec(reinterpret_cast(aURLString), + aURLStringLength); + + // Servo's loader guarantees that the URL is valid. + nsCOMPtr uri; + MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), urlSpec)); + + aLoader->LoadChildSheet(aParent, uri, media, nullptr, aImportRule, nullptr); +} + NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsCSSValueSharedList, CSSValueSharedList); #define STYLE_STRUCT(name, checkdata_cb) \ diff --git a/layout/style/ServoBindings.h b/layout/style/ServoBindings.h index 3aa73933d847..37cccdff2783 100644 --- a/layout/style/ServoBindings.h +++ b/layout/style/ServoBindings.h @@ -29,6 +29,7 @@ class nsIPrincipal; class nsIURI; struct nsFont; namespace mozilla { + class ServoStyleSheet; class FontFamilyList; enum FontFamilyType : uint32_t; } @@ -95,6 +96,13 @@ RawGeckoElementBorrowedOrNull Gecko_GetLastChildElement(RawGeckoElementBorrowed RawGeckoElementBorrowedOrNull Gecko_GetPrevSiblingElement(RawGeckoElementBorrowed element); RawGeckoElementBorrowedOrNull Gecko_GetNextSiblingElement(RawGeckoElementBorrowed element); RawGeckoElementBorrowedOrNull Gecko_GetDocumentElement(RawGeckoDocumentBorrowed document); +void Gecko_LoadStyleSheet(mozilla::css::Loader* loader, + mozilla::ServoStyleSheet* parent, + RawServoImportRuleBorrowed import_rule, + const uint8_t* url_bytes, + uint32_t url_length, + const uint8_t* media_bytes, + uint32_t media_length); // By default, Servo walks the DOM by traversing the siblings of the DOM-view // first child. This generally works, but misses anonymous children, which we diff --git a/layout/style/ServoStyleSheet.cpp b/layout/style/ServoStyleSheet.cpp index 0a689b9b2739..e6b72d7233f1 100644 --- a/layout/style/ServoStyleSheet.cpp +++ b/layout/style/ServoStyleSheet.cpp @@ -62,21 +62,17 @@ ServoStyleSheet::GetParentSheet() const void ServoStyleSheet::AppendStyleSheet(ServoStyleSheet* aSheet) { - // XXXheycam: When we implement support for child sheets, we'll have - // to fix SetOwningDocument to propagate the owning document down - // to the children. - MOZ_CRASH("stylo: not implemented"); + aSheet->mDocument = mDocument; } nsresult -ServoStyleSheet::ParseSheet(const nsAString& aInput, +ServoStyleSheet::ParseSheet(css::Loader* aLoader, + const nsAString& aInput, nsIURI* aSheetURI, nsIURI* aBaseURI, nsIPrincipal* aSheetPrincipal, uint32_t aLineNumber) { - DropSheet(); - RefPtr base = new ThreadSafeURIHolder(aBaseURI); RefPtr referrer = new ThreadSafeURIHolder(aSheetURI); RefPtr principal = @@ -87,8 +83,15 @@ ServoStyleSheet::ParseSheet(const nsAString& aInput, NS_ENSURE_SUCCESS(rv, rv); NS_ConvertUTF16toUTF8 input(aInput); - mSheet = Servo_StyleSheet_FromUTF8Bytes(&input, mParsingMode, &baseString, - base, referrer, principal).Consume(); + if (!mSheet) { + mSheet = + Servo_StyleSheet_FromUTF8Bytes(aLoader, this, &input, mParsingMode, + &baseString, base, referrer, + principal).Consume(); + } else { + Servo_StyleSheet_ClearAndUpdate(mSheet, aLoader, this, &input, base, + referrer, principal); + } return NS_OK; } diff --git a/layout/style/ServoStyleSheet.h b/layout/style/ServoStyleSheet.h index c86768b9dd0d..9b324355debc 100644 --- a/layout/style/ServoStyleSheet.h +++ b/layout/style/ServoStyleSheet.h @@ -18,6 +18,10 @@ namespace mozilla { class ServoCSSRuleList; +namespace css { +class Loader; +} + /** * CSS style sheet object that is a wrapper for a Servo Stylesheet. */ @@ -36,7 +40,8 @@ public: ServoStyleSheet* GetParentSheet() const; void AppendStyleSheet(ServoStyleSheet* aSheet); - MOZ_MUST_USE nsresult ParseSheet(const nsAString& aInput, + MOZ_MUST_USE nsresult ParseSheet(css::Loader* aLoader, + const nsAString& aInput, nsIURI* aSheetURI, nsIURI* aBaseURI, nsIPrincipal* aSheetPrincipal, @@ -56,6 +61,10 @@ public: #endif RawServoStyleSheet* RawSheet() const { return mSheet; } + void SetSheetForImport(RawServoStyleSheet* aSheet) { + MOZ_ASSERT(!mSheet); + mSheet = aSheet; + } // WebIDL StyleSheet API nsMediaList* Media() final; diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 8167dae58717..b91bdd35f5c0 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -3796,8 +3796,8 @@ CSSParserImpl::ProcessImport(const nsString& aURLSpec, uint32_t aColumnNumber) { RefPtr rule = new css::ImportRule(aMedia, aURLSpec, - aLineNumber, - aColumnNumber); + aLineNumber, + aColumnNumber); (*aAppendFunc)(rule, aData); // Diagnose bad URIs even if we don't have a child loader. @@ -3815,7 +3815,9 @@ CSSParserImpl::ProcessImport(const nsString& aURLSpec, } if (mChildLoader) { - mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule, mReusableSheets); + mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule, + /* aServoParentRule = */ nullptr, + mReusableSheets); } } diff --git a/layout/style/nsLayoutStylesheetCache.cpp b/layout/style/nsLayoutStylesheetCache.cpp index b259b1f121a9..99df44761756 100644 --- a/layout/style/nsLayoutStylesheetCache.cpp +++ b/layout/style/nsLayoutStylesheetCache.cpp @@ -971,7 +971,10 @@ nsLayoutStylesheetCache::BuildPreferenceSheet(RefPtr* aSheet, if (sheet->IsGecko()) { sheet->AsGecko()->ReparseSheet(sheetText); } else { - nsresult rv = sheet->AsServo()->ParseSheet(sheetText, uri, uri, nullptr, 0); + ServoStyleSheet* servoSheet = sheet->AsServo(); + // NB: The pref sheet never has @import rules. + nsresult rv = + servoSheet->ParseSheet(nullptr, sheetText, uri, uri, nullptr, 0); // Parsing the about:PreferenceStyleSheet URI can only fail on OOM. If we // are OOM before we parsed any documents we might as well abort. MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index 07d37e3cdcf8..514c45d37a4e 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -1027,7 +1027,20 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams) nsLayoutUtils::RectToGfxRect(aParams.borderArea, frame->PresContext()->AppUnitsPerDevPixel()); context.Rectangle(drawingRect, true); - context.SetColor(Color(0.0, 1.0, 0.0, 1.0)); + Color overlayColor(0.0f, 0.0f, 0.0f, 0.8f); + if (maskUsage.shouldGenerateMaskLayer) { + overlayColor.r = 1.0f; // red represents css positioned mask. + } + if (maskUsage.shouldApplyClipPath || + maskUsage.shouldGenerateClipMaskLayer) { + overlayColor.g = 1.0f; // green represents clip-path:. + } + if (maskUsage.shouldApplyBasicShape) { + overlayColor.b = 1.0f; // blue represents + // clip-path:||. + } + + context.SetColor(overlayColor); context.Fill(); } diff --git a/security/sandbox/mac/Sandbox.mm b/security/sandbox/mac/Sandbox.mm index d7f8e5cb162a..40048935e4c3 100644 --- a/security/sandbox/mac/Sandbox.mm +++ b/security/sandbox/mac/Sandbox.mm @@ -135,7 +135,6 @@ static const char pluginSandboxRules[] = "(allow file-read*\n" " (regex #\"^/etc$\")\n" " (regex #\"^/dev/u?random$\")\n" - " (regex #\"^/(private/)?var($|/)\")\n" " (literal \"/usr/share/icu/icudt51l.dat\")\n" " (regex #\"^/System/Library/Displays/Overrides/*\")\n" " (regex #\"^/System/Library/CoreServices/CoreTypes.bundle/*\")\n" diff --git a/taskcluster/ci/toolchain/linux.yml b/taskcluster/ci/toolchain/linux.yml index 9eeadf3ef31d..9169a290a524 100644 --- a/taskcluster/ci/toolchain/linux.yml +++ b/taskcluster/ci/toolchain/linux.yml @@ -23,6 +23,28 @@ linux64-clang/opt: - 'taskcluster/scripts/misc/build-clang-linux.sh' - 'taskcluster/taskgraph/transforms/job/toolchain.py' +linux64-clang-tidy/opt: + description: "Clang-tidy build" + treeherder: + kind: build + platform: linux64/opt + symbol: Cc(Clang-Tidy) + tier: 1 + run: + using: toolchain-script + script: build-clang-tidy-linux.sh + worker-type: aws-provisioner-v1/gecko-{level}-b-linux + worker: + implementation: docker-worker + docker-image: {in-tree: desktop-build} + max-run-time: 36000 + when: + files-changed: + - 'build/clang-plugin/**' + - 'build/build-clang/**' + - 'taskcluster/scripts/misc/build-clang-tidy-linux.sh' + - 'taskcluster/taskgraph/transforms/job/toolchain.py' + linux64-gcc/opt: description: "GCC toolchain build" treeherder: diff --git a/taskcluster/scripts/misc/build-clang-tidy-linux.sh b/taskcluster/scripts/misc/build-clang-tidy-linux.sh new file mode 100755 index 000000000000..8e6a1803fcbf --- /dev/null +++ b/taskcluster/scripts/misc/build-clang-tidy-linux.sh @@ -0,0 +1,30 @@ +#!/bin/bash +set -x -e -v + +# This script is for building clang for Linux. + +WORKSPACE=$HOME/workspace +HOME_DIR=$WORKSPACE/build +UPLOAD_DIR=$WORKSPACE/artifacts + +# Fetch our toolchain from tooltool +cd $HOME_DIR +wget -O tooltool.py https://raw.githubusercontent.com/mozilla/build-tooltool/master/tooltool.py +chmod +x tooltool.py +: TOOLTOOL_CACHE ${TOOLTOOL_CACHE:=/home/worker/tooltool-cache} +export TOOLTOOL_CACHE +cd src +$HOME_DIR/tooltool.py -m browser/config/tooltool-manifests/linux64/releng.manifest fetch + +# gets a bit too verbose here +set +x + +cd build/build-clang +# |mach python| sets up a virtualenv for us! +../../mach python ./build-clang.py -c clang-tidy-linux64.json + +set -x + +# Put a tarball in the artifacts dir +mkdir -p $UPLOAD_DIR +cp clang.tar.* $UPLOAD_DIR diff --git a/testing/specialpowers/content/SpecialPowersObserverAPI.js b/testing/specialpowers/content/SpecialPowersObserverAPI.js index d3137a42d45c..9d2f194a19f7 100644 --- a/testing/specialpowers/content/SpecialPowersObserverAPI.js +++ b/testing/specialpowers/content/SpecialPowersObserverAPI.js @@ -544,17 +544,13 @@ SpecialPowersObserverAPI.prototype = { } case "SPStartupExtension": { - let {ExtensionData, Management} = Components.utils.import("resource://gre/modules/Extension.jsm", {}); + let {ExtensionData} = Components.utils.import("resource://gre/modules/Extension.jsm", {}); let id = aMessage.data.id; let extension = this._extensions.get(id); - let startupListener = (msg, ext) => { - if (ext == extension) { - this._sendReply(aMessage, "SPExtensionMessage", {id, type: "extensionSetId", args: [extension.id]}); - Management.off("startup", startupListener); - } - }; - Management.on("startup", startupListener); + extension.on("startup", () => { + this._sendReply(aMessage, "SPExtensionMessage", {id, type: "extensionSetId", args: [extension.id]}); + }); // Make sure the extension passes the packaging checks when // they're run on a bare archive rather than a running instance, @@ -579,7 +575,6 @@ SpecialPowersObserverAPI.prototype = { this._sendReply(aMessage, "SPExtensionMessage", {id, type: "extensionStarted", args: []}); }).catch(e => { dump(`Extension startup failed: ${e}\n${e.stack}`); - Management.off("startup", startupListener); this._sendReply(aMessage, "SPExtensionMessage", {id, type: "extensionFailed", args: []}); }); return undefined; diff --git a/toolkit/modules/addons/WebRequest.jsm b/toolkit/modules/addons/WebRequest.jsm index a84a990461bf..c720dae5da73 100644 --- a/toolkit/modules/addons/WebRequest.jsm +++ b/toolkit/modules/addons/WebRequest.jsm @@ -813,14 +813,14 @@ HttpObserverManager = { // Check whether we've already added a listener to this channel, // so we don't wind up chaining multiple listeners. let channelData = getData(channel); - if (!channelData.listener && channel instanceof Ci.nsITraceableChannel) { + if (!channelData.hasListener && channel instanceof Ci.nsITraceableChannel) { let responseStatus = channel.responseStatus; // skip redirections, https://bugzilla.mozilla.org/show_bug.cgi?id=728901#c8 if (responseStatus < 300 || responseStatus >= 400) { let listener = new StartStopListener(this, loadContext); let orig = channel.setNewListener(listener); listener.orig = orig; - channelData.listener = listener; + channelData.hasListener = true; } } } diff --git a/tools/profiler/moz.build b/tools/profiler/moz.build index 3c446b0d46d5..b12eacbc6cc5 100644 --- a/tools/profiler/moz.build +++ b/tools/profiler/moz.build @@ -150,3 +150,6 @@ if CONFIG['GNU_CXX']: '-Wno-error=shadow', '-Wno-ignored-qualifiers', # due to use of breakpad headers ] + +with Files('**'): + BUG_COMPONENT = ('Core', 'Gecko Profiler') diff --git a/tools/profiler/tests/test_saved_stacks.js b/tools/profiler/tests/test_saved_stacks.js deleted file mode 100644 index c8852768cfd8..000000000000 --- a/tools/profiler/tests/test_saved_stacks.js +++ /dev/null @@ -1,69 +0,0 @@ -// Test that we get `js::SavedStacks::saveCurrentStack` frames. - -function run_test() { - let p = Cc["@mozilla.org/tools/profiler;1"]; - // Just skip the test if the profiler component isn't present. - if (!p) - return; - p = p.getService(Ci.nsIProfiler); - if (!p) - return; - - const { saveStack } = Cu.getJSTestingFunctions(); - - const ms = 5; - p.StartProfiler(100, ms, ["js"], 1); - - let then = Date.now(); - while (Date.now() - then < 30000) { - function a() { - saveStack(); - saveStack(); - saveStack(); - saveStack(); - saveStack(); - saveStack(); - saveStack(); - saveStack(); - saveStack(); - saveStack(); - saveStack(); - saveStack(); - saveStack(); - } - - a(); - a(); - a(); - a(); - a(); - - function b() { - a(); - } - - b(); - b(); - b(); - b(); - b(); - } - - var profile = p.getProfileData().threads[0]; - - do_check_neq(profile.samples.data.length, 0); - - let found = false; - for (let sample of profile.samples.data) { - const stack = getInflatedStackLocations(profile, sample); - for (let frame of stack) { - if (frame.indexOf("js::SavedStacks::saveCurrentStack") >= 0) { - found = true; - break; - } - } - } - do_check_true(found); - - p.StopProfiler(); -} diff --git a/tools/profiler/tests/xpcshell.ini b/tools/profiler/tests/xpcshell.ini index 2726ae3d354f..997a7c142ad0 100644 --- a/tools/profiler/tests/xpcshell.ini +++ b/tools/profiler/tests/xpcshell.ini @@ -15,5 +15,4 @@ skip-if = true skip-if = !debug [test_enterjit_osr_enabling.js] skip-if = !debug -[test_asm.js] -[test_saved_stacks.js] +[test_asm.js] \ No newline at end of file