mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 14:22:01 +00:00
Merge inbound to central, a=merge
MozReview-Commit-ID: KIm8XUkfyZW
This commit is contained in:
commit
6e269458be
@ -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},
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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';
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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';
|
||||
|
@ -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';
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
// Small subset of the webL10n API by Fabien Cazenave for pdf.js extension.
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
18
build/build-clang/clang-tidy-linux64.json
Normal file
18
build/build-clang/clang-tidy-linux64.json
Normal file
@ -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++"
|
||||
}
|
@ -163,6 +163,8 @@ WebGLContext::InitWebGL2(FailureReason* const out_failReason)
|
||||
mDefaultTransformFeedback = new WebGLTransformFeedback(this, 0);
|
||||
mBoundTransformFeedback = mDefaultTransformFeedback;
|
||||
|
||||
gl->fGenTransformFeedbacks(1, &mEmptyTFO);
|
||||
|
||||
////
|
||||
|
||||
if (!gl->IsGLES()) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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<WebGLBuffer>* 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<WebGLElementArrayCache> mCache;
|
||||
size_t mTFBindCount;
|
||||
size_t mNonTFBindCount;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -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
|
||||
|
||||
|
@ -1654,8 +1654,6 @@ protected:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidateForNonTransformFeedback(const char* funcName, WebGLBuffer* buffer);
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
bool ValidateNonNull(const char* funcName, const dom::Nullable<T>& maybe) {
|
||||
@ -1882,17 +1880,9 @@ protected:
|
||||
|
||||
////////////////////////////////////
|
||||
|
||||
private:
|
||||
mutable bool mBuffersForUB_Dirty;
|
||||
mutable std::set<const WebGLBuffer*> 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
|
||||
|
@ -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<WebGLBuffer>& bindPoint) {
|
||||
const auto fnClearIfBuffer = [&](GLenum target, WebGLRefPtr<WebGLBuffer>& 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();
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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<WebGLVertexArray>
|
||||
|
@ -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:
|
||||
|
@ -87,6 +87,7 @@ struct LinkedProgramInfo final
|
||||
//////
|
||||
|
||||
WebGLProgram* const prog;
|
||||
const GLenum transformFeedbackBufferMode;
|
||||
|
||||
std::vector<AttribInfo> attribs;
|
||||
std::vector<UniformInfo*> uniforms; // Owns its contents.
|
||||
|
@ -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<JSObject*> givenProto)
|
||||
{
|
||||
|
@ -17,6 +17,7 @@ class WebGLTransformFeedback final
|
||||
, public WebGLRefCountedObject<WebGLTransformFeedback>
|
||||
, public LinkedListElement<WebGLTransformFeedback>
|
||||
{
|
||||
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<const WebGLBuffer*> mBuffersForTF;
|
||||
|
||||
public:
|
||||
WebGLTransformFeedback(WebGLContext* webgl, GLuint tf);
|
||||
private:
|
||||
@ -52,10 +50,9 @@ public:
|
||||
WebGLContext* GetParentObject() const { return mContext; }
|
||||
virtual JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) 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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
Binary file not shown.
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
|
26
intl/icu-patches/bug-1325858-close-key.diff
Normal file
26
intl/icu-patches/bug-1325858-close-key.diff
Normal file
@ -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
|
@ -216,6 +216,8 @@ static LONG getTZKeyName(char* tzKeyName, int32_t length) {
|
||||
&cbData);
|
||||
}
|
||||
|
||||
RegCloseKey(hkey);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
7
js/src/jit-test/tests/ion/bug1188586.js
Normal file
7
js/src/jit-test/tests/ion/bug1188586.js
Normal file
@ -0,0 +1,7 @@
|
||||
(function(y) {
|
||||
for (var k = 0; k < 9; ++k) {
|
||||
try {
|
||||
y ** y == a;
|
||||
} catch (e) {}
|
||||
}
|
||||
})();
|
4
js/src/jit-test/tests/ion/bug1326150.js
Normal file
4
js/src/jit-test/tests/ion/bug1326150.js
Normal file
@ -0,0 +1,4 @@
|
||||
this.x = [];
|
||||
Function.apply(null, this.x);
|
||||
Object.defineProperty(this, "x", {get: valueOf});
|
||||
assertEq(evaluate("this.x;"), this);
|
@ -415,9 +415,9 @@ BaselineCacheIRCompiler::emitCallScriptedGetterResult()
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*DoCallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, MutableHandleValue);
|
||||
static const VMFunction DoCallNativeGetterInfo =
|
||||
FunctionInfo<DoCallNativeGetterFn>(DoCallNativeGetter, "DoCallNativeGetter");
|
||||
typedef bool (*CallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, MutableHandleValue);
|
||||
static const VMFunction CallNativeGetterInfo =
|
||||
FunctionInfo<CallNativeGetterFn>(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);
|
||||
|
@ -698,7 +698,6 @@ RecompileBaselineScriptForDebugMode(JSContext* cx, JSScript* script,
|
||||
_(Call_ScriptedApplyArray) \
|
||||
_(Call_ScriptedApplyArguments) \
|
||||
_(Call_ScriptedFunCall) \
|
||||
_(GetProp_CallNativeGlobal) \
|
||||
_(GetProp_Generic) \
|
||||
_(SetProp_CallScripted) \
|
||||
_(SetProp_CallNative)
|
||||
|
@ -435,10 +435,6 @@ ICTypeUpdate_ObjectGroup::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*DoCallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, MutableHandleValue);
|
||||
static const VMFunction DoCallNativeGetterInfo =
|
||||
FunctionInfo<DoCallNativeGetterFn>(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<ICSetPropCallSetter*>(*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<LexicalEnvironmentObject*> 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<NativeObject>())
|
||||
return true;
|
||||
current = &proto->as<NativeObject>();
|
||||
}
|
||||
|
||||
// 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<JSFunction>());
|
||||
|
||||
// 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<LexicalEnvironmentObject>(),
|
||||
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<ICSetPropCallSetter*>(*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
|
||||
|
@ -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<CallObject>();
|
||||
}
|
||||
|
||||
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
|
||||
// <holderId = LoadObject <holder>>
|
||||
// <GuardShape holderId>
|
||||
// CallNativeGetterResult globalId
|
||||
|
||||
CacheIRReader reader(stub->stubInfo());
|
||||
|
||||
ObjOperandId objId = ObjOperandId(0);
|
||||
if (!reader.matchOp(CacheOp::GuardShape, objId))
|
||||
return false;
|
||||
Shape* globalLexicalShape = stub->stubInfo()->getStubField<Shape*>(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<Shape*>(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<JSObject*>(stub, reader.stubOffset()).get();
|
||||
|
||||
if (!reader.matchOp(CacheOp::GuardShape, holderId))
|
||||
return false;
|
||||
holderShape = stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
|
||||
}
|
||||
|
||||
// This guard will always fail, try the next stub.
|
||||
if (holder->as<NativeObject>().lastProperty() != holderShape)
|
||||
return true;
|
||||
|
||||
if (!reader.matchOp(CacheOp::CallNativeGetterResult, globalId))
|
||||
return false;
|
||||
size_t offset = reader.stubOffset();
|
||||
JSFunction* getter =
|
||||
&stub->stubInfo()->getStubField<JSObject*>(stub, offset)->as<JSFunction>();
|
||||
|
||||
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<ICGetPropCallGetter*>(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;
|
||||
}
|
||||
|
@ -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<LexicalEnvironmentObject*> 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<NativeObject>())
|
||||
return false;
|
||||
|
||||
current = &proto->as<NativeObject>();
|
||||
}
|
||||
}
|
||||
|
||||
holder.set(current);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId, HandleId id)
|
||||
{
|
||||
@ -1168,44 +1208,31 @@ GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId, HandleId id)
|
||||
Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
|
||||
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<NativeObject>())
|
||||
return false;
|
||||
current = &proto->as<NativeObject>();
|
||||
}
|
||||
}
|
||||
|
||||
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<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
|
||||
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)
|
||||
{
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -173,7 +173,7 @@ struct TempObject
|
||||
"Placement new argument type must inherit from TempObject");
|
||||
MOZ_ASSERT(pos);
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
@ -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);
|
||||
|
@ -87,6 +87,8 @@ struct DefaultJitOptions
|
||||
uint32_t branchPruningBlockSpanFactor;
|
||||
uint32_t branchPruningEffectfulInstFactor;
|
||||
uint32_t branchPruningThreshold;
|
||||
uint32_t wasmBatchThreshold;
|
||||
uint32_t wasmBatchIonScaleFactor;
|
||||
mozilla::Maybe<uint32_t> forcedDefaultIonWarmUpThreshold;
|
||||
mozilla::Maybe<uint32_t> forcedDefaultIonSmallFunctionWarmUpThreshold;
|
||||
mozilla::Maybe<IonRegisterAllocator> forcedRegisterAllocator;
|
||||
|
@ -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<ICGetPropCallGetter*>(*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<LexicalEnvironmentObject>().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<LexicalEnvironmentObject>().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<GlobalObject>())
|
||||
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<ShapeVector> shapes)
|
||||
{
|
||||
@ -2457,135 +2373,6 @@ GetProtoShapes(JSObject* obj, size_t protoChainDepth, MutableHandle<ShapeVector>
|
||||
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<DoCallNativeGetterFn>(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<LexicalEnvironmentObject>() &&
|
||||
receiver_->as<LexicalEnvironmentObject>().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<NativeObject>().lastProperty();
|
||||
|
||||
switch (kind) {
|
||||
case ICStub::GetProp_CallNativeGlobal: {
|
||||
Shape* globalShape = receiver_->as<LexicalEnvironmentObject>().global().lastProperty();
|
||||
return newStub<ICGetProp_CallNativeGlobal>(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<ICGetProp_CallNativeGlobal>(cx, space, other.jitCode(), firstMonitorStub,
|
||||
other.receiverGuard(), other.holder_,
|
||||
other.holderShape_, other.globalShape_,
|
||||
other.getter_, other.pcOffset_);
|
||||
}
|
||||
|
||||
//
|
||||
// TypeMonitor_Fallback
|
||||
//
|
||||
|
||||
|
@ -2256,10 +2256,6 @@ GetProtoShapes(JSObject* obj, size_t protoChainDepth, MutableHandle<ShapeVector>
|
||||
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<int32_t>(engine_) |
|
||||
(static_cast<int32_t>(kind) << 1) |
|
||||
(HeapReceiverGuard::keyBits(receiver_) << 17) |
|
||||
(static_cast<int32_t>(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<int32_t>(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
|
||||
|
||||
|
@ -35,7 +35,6 @@ namespace jit {
|
||||
_(Compare_Int32WithBoolean) \
|
||||
\
|
||||
_(GetProp_Fallback) \
|
||||
_(GetProp_CallNativeGlobal) \
|
||||
_(GetProp_Generic) \
|
||||
\
|
||||
_(CacheIR_Monitored) \
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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<uint32_t>(cursor, bytecodeSize);
|
||||
cursor = WriteScalar<uint32_t>(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<uint32_t>(cursor, &remain, &bytecodeSize)) &&
|
||||
(cursor = ReadScalarChecked<uint32_t>(cursor, &remain, &compiledSize));
|
||||
uint32_t compiledSize;
|
||||
cursor = ReadScalarChecked<uint32_t>(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_<AsmJSMetadata>();
|
||||
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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)),
|
||||
|
@ -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<size_t> 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<Bytes>();
|
||||
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<Bytes>();
|
||||
if (!fg->bytes_)
|
||||
fg->funcBytes_ = js::MakeUnique<FuncBytes>();
|
||||
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<FuncBytes>(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<uint32_t> 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;
|
||||
}
|
||||
|
@ -36,124 +36,158 @@ struct CompileArgs;
|
||||
|
||||
class FunctionGenerator;
|
||||
|
||||
typedef Vector<UniqueBytes, 0, SystemAllocPolicy> 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<FuncBytes> UniqueFuncBytes;
|
||||
typedef Vector<UniqueFuncBytes, 8, SystemAllocPolicy> 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<bool> 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<FuncCompileUnit, 8, SystemAllocPolicy> 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<FuncCompileResults> results_;
|
||||
const ModuleEnvironment& env_;
|
||||
LifoAlloc lifo_;
|
||||
Maybe<jit::TempAllocator> alloc_;
|
||||
Maybe<jit::MacroAssembler> 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<FunctionGenerator*> 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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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<ShareableBytes>();
|
||||
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<ShareableBytes>();
|
||||
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);
|
||||
|
@ -137,7 +137,7 @@ static inline uint8_t*
|
||||
SerializePodVector(uint8_t* cursor, const mozilla::Vector<T, N, SystemAllocPolicy>& 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<uint32_t>(cursor, vec.length());
|
||||
cursor = WriteBytes(cursor, vec.begin(), vec.length() * sizeof(T));
|
||||
|
@ -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<RawServoStyleSheet> 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) {
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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<nsMediaList> 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<const char*>(aMediaString),
|
||||
aMediaStringLength);
|
||||
nsCSSParser mediumParser(aLoader);
|
||||
mediumParser.ParseMediaList(
|
||||
NS_ConvertUTF8toUTF16(medium), nullptr, 0, media);
|
||||
}
|
||||
|
||||
nsDependentCSubstring urlSpec(reinterpret_cast<const char*>(aURLString),
|
||||
aURLStringLength);
|
||||
|
||||
// Servo's loader guarantees that the URL is valid.
|
||||
nsCOMPtr<nsIURI> 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) \
|
||||
|
@ -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
|
||||
|
@ -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<ThreadSafeURIHolder> base = new ThreadSafeURIHolder(aBaseURI);
|
||||
RefPtr<ThreadSafeURIHolder> referrer = new ThreadSafeURIHolder(aSheetURI);
|
||||
RefPtr<ThreadSafePrincipalHolder> 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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -3796,8 +3796,8 @@ CSSParserImpl::ProcessImport(const nsString& aURLSpec,
|
||||
uint32_t aColumnNumber)
|
||||
{
|
||||
RefPtr<css::ImportRule> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -971,7 +971,10 @@ nsLayoutStylesheetCache::BuildPreferenceSheet(RefPtr<StyleSheet>* 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));
|
||||
|
@ -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:<clip-source>.
|
||||
}
|
||||
if (maskUsage.shouldApplyBasicShape) {
|
||||
overlayColor.b = 1.0f; // blue represents
|
||||
// clip-path:<basic-shape>||<geometry-box>.
|
||||
}
|
||||
|
||||
context.SetColor(overlayColor);
|
||||
context.Fill();
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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:
|
||||
|
30
taskcluster/scripts/misc/build-clang-tidy-linux.sh
Executable file
30
taskcluster/scripts/misc/build-clang-tidy-linux.sh
Executable file
@ -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
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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')
|
||||
|
@ -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();
|
||||
}
|
@ -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]
|
Loading…
Reference in New Issue
Block a user