зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
f0d003cecd
|
@ -564,17 +564,11 @@ this.EventManager.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
onProgressChange: function onProgressChange() {},
|
||||
|
||||
onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
|
||||
let docAcc = Utils.AccService.getAccessibleFor(aWebProgress.DOMWindow.document);
|
||||
this.present(Presentation.tabStateChanged(docAcc, 'newdoc'));
|
||||
},
|
||||
|
||||
onStatusChange: function onStatusChange() {},
|
||||
|
||||
onSecurityChange: function onSecurityChange() {},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference,
|
||||
Ci.nsISupports,
|
||||
|
|
|
@ -109,7 +109,9 @@ var wrapper = {
|
|||
this.iframe.QueryInterface(Ci.nsIFrameLoaderOwner);
|
||||
let docShell = this.iframe.frameLoader.docShell;
|
||||
docShell.QueryInterface(Ci.nsIWebProgress);
|
||||
docShell.addProgressListener(this.iframeListener, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
|
||||
docShell.addProgressListener(this.iframeListener,
|
||||
Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT |
|
||||
Ci.nsIWebProgress.NOTIFY_LOCATION);
|
||||
iframe.addEventListener("load", this);
|
||||
|
||||
// Ideally we'd just merge urlParams with new URL(url).searchParams, but our
|
||||
|
@ -166,10 +168,6 @@ var wrapper = {
|
|||
setErrorPage("networkError");
|
||||
}
|
||||
},
|
||||
|
||||
onProgressChange() {},
|
||||
onStatusChange() {},
|
||||
onSecurityChange() {},
|
||||
},
|
||||
|
||||
handleEvent(evt) {
|
||||
|
|
|
@ -165,10 +165,6 @@ const SocialErrorListener = {
|
|||
this.setErrorPage();
|
||||
}
|
||||
},
|
||||
|
||||
onProgressChange() {},
|
||||
onStatusChange() {},
|
||||
onSecurityChange() {},
|
||||
};
|
||||
|
||||
SocialErrorListener.init();
|
||||
|
|
|
@ -712,16 +712,32 @@ function test_tabNavigate() {
|
|||
|
||||
function test_urlBar() {
|
||||
return Task.spawn(function* () {
|
||||
let notificationPromise = waitForNotification("addon-install-origin-blocked");
|
||||
let progressPromise = waitForProgressNotification();
|
||||
let dialogPromise = waitForInstallDialog();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab("about:blank");
|
||||
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
gURLBar.value = TESTROOT + "amosigned.xpi";
|
||||
gURLBar.focus();
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
|
||||
yield progressPromise;
|
||||
let installDialog = yield dialogPromise;
|
||||
|
||||
let notificationPromise = waitForNotification("addon-install-restart");
|
||||
acceptInstallDialog(installDialog);
|
||||
let panel = yield notificationPromise;
|
||||
|
||||
let notification = panel.childNodes[0];
|
||||
ok(!notification.hasAttribute("buttonlabel"), "Button to allow install should be hidden.");
|
||||
is(notification.button.label, "Restart Now", "Should have seen the right button");
|
||||
is(notification.getAttribute("label"),
|
||||
"XPI Test will be installed after you restart " + gApp + ".",
|
||||
"Should have seen the right message");
|
||||
|
||||
let installs = yield getInstalls();
|
||||
is(installs.length, 1, "Should be one pending install");
|
||||
installs[0].cancel();
|
||||
|
||||
yield removeTab();
|
||||
});
|
||||
},
|
||||
|
|
|
@ -429,9 +429,4 @@ ProgressListener.prototype = {
|
|||
this.callbacks.onStopRequest();
|
||||
}
|
||||
},
|
||||
|
||||
onLocationChange() {},
|
||||
onProgressChange() {},
|
||||
onStatusChange() {},
|
||||
onSecurityChange() {},
|
||||
};
|
||||
|
|
|
@ -243,12 +243,6 @@ FrameTreeInternal.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
// Unused nsIWebProgressListener methods.
|
||||
onLocationChange() {},
|
||||
onProgressChange() {},
|
||||
onSecurityChange() {},
|
||||
onStatusChange() {},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference])
|
||||
};
|
||||
|
|
|
@ -107,12 +107,6 @@ TranslationContentHandler.prototype = {
|
|||
});
|
||||
},
|
||||
|
||||
// Unused methods.
|
||||
onProgressChange() {},
|
||||
onLocationChange() {},
|
||||
onStatusChange() {},
|
||||
onSecurityChange() {},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
|
|
|
@ -183,6 +183,10 @@ BoxModel.prototype = {
|
|||
* Hides the box-model highlighter on the currently selected element.
|
||||
*/
|
||||
onHideBoxModelHighlighter() {
|
||||
if (!this.inspector) {
|
||||
return;
|
||||
}
|
||||
|
||||
let toolbox = this.inspector.toolbox;
|
||||
toolbox.highlighterUtils.unhighlight();
|
||||
},
|
||||
|
@ -305,6 +309,10 @@ BoxModel.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!this.inspector) {
|
||||
return;
|
||||
}
|
||||
|
||||
let node = this.inspector.selection.nodeFront;
|
||||
this.inspector.pageStyle.getLayout(node, {
|
||||
autoMargins: true,
|
||||
|
@ -324,6 +332,10 @@ BoxModel.prototype = {
|
|||
* Options passed to the highlighter actor.
|
||||
*/
|
||||
onShowBoxModelHighlighter(options = {}) {
|
||||
if (!this.inspector) {
|
||||
return;
|
||||
}
|
||||
|
||||
let toolbox = this.inspector.toolbox;
|
||||
let nodeFront = this.inspector.selection.nodeFront;
|
||||
|
||||
|
|
|
@ -30,6 +30,14 @@ module.exports = createClass({
|
|||
|
||||
mixins: [ addons.PureRenderMixin ],
|
||||
|
||||
onKeyDown(event) {
|
||||
let { target } = event;
|
||||
|
||||
if (target == this.boxModelContainer) {
|
||||
this.boxModelMain.onKeyDown(event);
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
let {
|
||||
boxModel,
|
||||
|
@ -45,10 +53,19 @@ module.exports = createClass({
|
|||
return dom.div(
|
||||
{
|
||||
className: "boxmodel-container",
|
||||
tabIndex: 0,
|
||||
ref: div => {
|
||||
this.boxModelContainer = div;
|
||||
},
|
||||
onKeyDown: this.onKeyDown,
|
||||
},
|
||||
BoxModelMain({
|
||||
boxModel,
|
||||
boxModelContainer: this.boxModelContainer,
|
||||
setSelectedNode,
|
||||
ref: boxModelMain => {
|
||||
this.boxModelMain = boxModelMain;
|
||||
},
|
||||
onHideBoxModelHighlighter,
|
||||
onShowBoxModelEditor,
|
||||
onShowBoxModelHighlighter,
|
||||
|
|
|
@ -17,6 +17,8 @@ module.exports = createClass({
|
|||
propTypes: {
|
||||
box: PropTypes.string.isRequired,
|
||||
direction: PropTypes.string,
|
||||
focusable: PropTypes.bool.isRequired,
|
||||
level: PropTypes.string,
|
||||
property: PropTypes.string.isRequired,
|
||||
textContent: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||
onShowBoxModelEditor: PropTypes.func.isRequired,
|
||||
|
@ -28,7 +30,7 @@ module.exports = createClass({
|
|||
let { property, onShowBoxModelEditor } = this.props;
|
||||
|
||||
editableItem({
|
||||
element: this.refs.span,
|
||||
element: this.boxModelEditable,
|
||||
}, (element, event) => {
|
||||
onShowBoxModelEditor(element, event, property);
|
||||
});
|
||||
|
@ -38,6 +40,8 @@ module.exports = createClass({
|
|||
let {
|
||||
box,
|
||||
direction,
|
||||
focusable,
|
||||
level,
|
||||
property,
|
||||
textContent,
|
||||
} = this.props;
|
||||
|
@ -57,8 +61,11 @@ module.exports = createClass({
|
|||
{
|
||||
className: "boxmodel-editable",
|
||||
"data-box": box,
|
||||
tabIndex: box === level && focusable ? 0 : -1,
|
||||
title: property,
|
||||
ref: "span",
|
||||
ref: span => {
|
||||
this.boxModelEditable = span;
|
||||
},
|
||||
},
|
||||
textContent
|
||||
)
|
||||
|
|
|
@ -6,10 +6,13 @@
|
|||
|
||||
const { addons, createClass, createFactory, DOM: dom, PropTypes } =
|
||||
require("devtools/client/shared/vendor/react");
|
||||
const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
|
||||
const { KeyCodes } = require("devtools/client/shared/keycodes");
|
||||
|
||||
const { LocalizationHelper } = require("devtools/shared/l10n");
|
||||
|
||||
const BoxModelEditable = createFactory(require("./BoxModelEditable"));
|
||||
|
||||
// Reps
|
||||
const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
|
||||
const Rep = createFactory(REPS.Rep);
|
||||
|
@ -28,6 +31,7 @@ module.exports = createClass({
|
|||
|
||||
propTypes: {
|
||||
boxModel: PropTypes.shape(Types.boxModel).isRequired,
|
||||
boxModelContainer: PropTypes.object,
|
||||
setSelectedNode: PropTypes.func.isRequired,
|
||||
onHideBoxModelHighlighter: PropTypes.func.isRequired,
|
||||
onShowBoxModelEditor: PropTypes.func.isRequired,
|
||||
|
@ -37,11 +41,90 @@ module.exports = createClass({
|
|||
|
||||
mixins: [ addons.PureRenderMixin ],
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
activeDescendant: null,
|
||||
focusable: false,
|
||||
};
|
||||
},
|
||||
|
||||
componentDidUpdate() {
|
||||
let displayPosition = this.getDisplayPosition();
|
||||
let isContentBox = this.getContextBox();
|
||||
|
||||
this.layouts = {
|
||||
"position": new Map([
|
||||
[KeyCodes.DOM_VK_ESCAPE, this.positionLayout],
|
||||
[KeyCodes.DOM_VK_DOWN, this.marginLayout],
|
||||
[KeyCodes.DOM_VK_RETURN, this.positionEditable],
|
||||
[KeyCodes.DOM_VK_UP, null],
|
||||
["click", this.positionLayout]
|
||||
]),
|
||||
"margin": new Map([
|
||||
[KeyCodes.DOM_VK_ESCAPE, this.marginLayout],
|
||||
[KeyCodes.DOM_VK_DOWN, this.borderLayout],
|
||||
[KeyCodes.DOM_VK_RETURN, this.marginEditable],
|
||||
[KeyCodes.DOM_VK_UP, displayPosition ? this.positionLayout : null],
|
||||
["click", this.marginLayout]
|
||||
]),
|
||||
"border": new Map([
|
||||
[KeyCodes.DOM_VK_ESCAPE, this.borderLayout],
|
||||
[KeyCodes.DOM_VK_DOWN, this.paddingLayout],
|
||||
[KeyCodes.DOM_VK_RETURN, this.borderEditable],
|
||||
[KeyCodes.DOM_VK_UP, this.marginLayout],
|
||||
["click", this.borderLayout]
|
||||
]),
|
||||
"padding": new Map([
|
||||
[KeyCodes.DOM_VK_ESCAPE, this.paddingLayout],
|
||||
[KeyCodes.DOM_VK_DOWN, isContentBox ? this.contentLayout : null],
|
||||
[KeyCodes.DOM_VK_RETURN, this.paddingEditable],
|
||||
[KeyCodes.DOM_VK_UP, this.borderLayout],
|
||||
["click", this.paddingLayout]
|
||||
]),
|
||||
"content": new Map([
|
||||
[KeyCodes.DOM_VK_ESCAPE, this.contentLayout],
|
||||
[KeyCodes.DOM_VK_DOWN, null],
|
||||
[KeyCodes.DOM_VK_RETURN, this.contentEditable],
|
||||
[KeyCodes.DOM_VK_UP, this.paddingLayout],
|
||||
["click", this.contentLayout]
|
||||
])
|
||||
};
|
||||
},
|
||||
|
||||
getAriaActiveDescendant() {
|
||||
let { activeDescendant } = this.state;
|
||||
|
||||
if (!activeDescendant) {
|
||||
let displayPosition = this.getDisplayPosition();
|
||||
let nextLayout = displayPosition ? this.positionLayout : this.marginLayout;
|
||||
activeDescendant = nextLayout.getAttribute("data-box");
|
||||
this.setAriaActive(nextLayout);
|
||||
}
|
||||
|
||||
return activeDescendant;
|
||||
},
|
||||
|
||||
getBorderOrPaddingValue(property) {
|
||||
let { layout } = this.props.boxModel;
|
||||
return layout[property] ? parseFloat(layout[property]) : "-";
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the layout box sizing is context box and false otherwise.
|
||||
*/
|
||||
getContextBox() {
|
||||
let { layout } = this.props.boxModel;
|
||||
return layout["box-sizing"] == "content-box";
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the position is displayed and false otherwise.
|
||||
*/
|
||||
getDisplayPosition() {
|
||||
let { layout } = this.props.boxModel;
|
||||
return layout.position && layout.position != "static";
|
||||
},
|
||||
|
||||
getHeightValue(property) {
|
||||
let { layout } = this.props.boxModel;
|
||||
|
||||
|
@ -117,13 +200,67 @@ module.exports = createClass({
|
|||
return value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Move the focus to the next/previous editable element of the current layout.
|
||||
*
|
||||
* @param {Element} target
|
||||
* Node to be observed
|
||||
* @param {Boolean} shiftKey
|
||||
* Determines if shiftKey was pressed
|
||||
* @param {String} level
|
||||
* Current active layout
|
||||
*/
|
||||
moveFocus: function ({ target, shiftKey }, level) {
|
||||
let editBoxes = [
|
||||
...findDOMNode(this).querySelectorAll(`[data-box="${level}"].boxmodel-editable`)
|
||||
];
|
||||
let editingMode = target.tagName === "input";
|
||||
// target.nextSibling is input field
|
||||
let position = editingMode ? editBoxes.indexOf(target.nextSibling)
|
||||
: editBoxes.indexOf(target);
|
||||
|
||||
if (position === editBoxes.length - 1 && !shiftKey) {
|
||||
position = 0;
|
||||
} else if (position === 0 && shiftKey) {
|
||||
position = editBoxes.length - 1;
|
||||
} else {
|
||||
shiftKey ? position-- : position++;
|
||||
}
|
||||
|
||||
let editBox = editBoxes[position];
|
||||
editBox.focus();
|
||||
|
||||
if (editingMode) {
|
||||
editBox.click();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Active aria-level set to current layout.
|
||||
*
|
||||
* @param {Element} nextLayout
|
||||
* Element of next layout that user has navigated to
|
||||
*/
|
||||
setAriaActive(nextLayout) {
|
||||
let { boxModelContainer } = this.props;
|
||||
|
||||
// We set this attribute for testing purposes.
|
||||
if (boxModelContainer) {
|
||||
boxModelContainer.setAttribute("activedescendant", nextLayout.className);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
activeDescendant: nextLayout.getAttribute("data-box"),
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* While waiting for a reps fix in https://github.com/devtools-html/reps/issues/92,
|
||||
* translate nodeFront to a grip-like object that can be used with an ElementNode rep.
|
||||
*
|
||||
* @params {NodeFront} nodeFront
|
||||
* The NodeFront for which we want to create a grip-like object.
|
||||
* @returns {Object} a grip-like object that can be used with Reps.
|
||||
* @param {NodeFront} nodeFront
|
||||
* The NodeFront for which we want to create a grip-like object.
|
||||
* @return {Object} a grip-like object that can be used with Reps.
|
||||
*/
|
||||
translateNodeFrontToGrip(nodeFront) {
|
||||
let {
|
||||
|
@ -180,6 +317,95 @@ module.exports = createClass({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle keyboard navigation and focus for box model layouts.
|
||||
*
|
||||
* Updates active layout on arrow key navigation
|
||||
* Focuses next layout's editboxes on enter key
|
||||
* Unfocuses current layout's editboxes when active layout changes
|
||||
* Controls tabbing between editBoxes
|
||||
*
|
||||
* @param {Event} event
|
||||
* The event triggered by a keypress on the box model
|
||||
*/
|
||||
onKeyDown(event) {
|
||||
let { target, keyCode } = event;
|
||||
let isEditable = target._editable || target.editor;
|
||||
|
||||
let level = this.getAriaActiveDescendant();
|
||||
let editingMode = target.tagName === "input";
|
||||
|
||||
switch (keyCode) {
|
||||
case KeyCodes.DOM_VK_RETURN:
|
||||
if (!isEditable) {
|
||||
this.setState({ focusable: true });
|
||||
let editableBox = this.layouts[level].get(keyCode);
|
||||
if (editableBox) {
|
||||
editableBox.boxModelEditable.focus();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case KeyCodes.DOM_VK_DOWN:
|
||||
case KeyCodes.DOM_VK_UP:
|
||||
if (!editingMode) {
|
||||
event.preventDefault();
|
||||
this.setState({ focusable: false });
|
||||
|
||||
let nextLayout = this.layouts[level].get(keyCode);
|
||||
this.setAriaActive(nextLayout);
|
||||
|
||||
if (target && target._editable) {
|
||||
target.blur();
|
||||
}
|
||||
|
||||
this.props.boxModelContainer.focus();
|
||||
}
|
||||
break;
|
||||
case KeyCodes.DOM_VK_TAB:
|
||||
if (isEditable) {
|
||||
event.preventDefault();
|
||||
this.moveFocus(event, level);
|
||||
}
|
||||
break;
|
||||
case KeyCodes.DOM_VK_ESCAPE:
|
||||
if (target._editable) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.setState({ focusable: false });
|
||||
this.props.boxModelContainer.focus();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update aria-active on mouse click.
|
||||
*
|
||||
* @param {Event} event
|
||||
* The event triggered by a mouse click on the box model
|
||||
*/
|
||||
onLevelClick(event) {
|
||||
let { target } = event;
|
||||
let displayPosition = this.getDisplayPosition();
|
||||
let isContentBox = this.getContextBox();
|
||||
|
||||
// Avoid switching the aria active descendant to the position or content layout
|
||||
// if those are not editable.
|
||||
if ((!displayPosition && target == this.positionLayout) ||
|
||||
(!isContentBox && target == this.contentLayout)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let nextLayout = this.layouts[target.getAttribute("data-box")].get("click");
|
||||
this.setAriaActive(nextLayout);
|
||||
|
||||
if (target && target._editable) {
|
||||
target.blur();
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
let {
|
||||
boxModel,
|
||||
|
@ -188,6 +414,7 @@ module.exports = createClass({
|
|||
} = this.props;
|
||||
let { layout, offsetParent } = boxModel;
|
||||
let { height, width, position } = layout;
|
||||
let { activeDescendant: level, focusable } = this.state;
|
||||
|
||||
let displayOffsetParent = offsetParent && layout.position === "absolute";
|
||||
|
||||
|
@ -201,7 +428,7 @@ module.exports = createClass({
|
|||
let paddingBottom = this.getBorderOrPaddingValue("padding-bottom");
|
||||
let paddingLeft = this.getBorderOrPaddingValue("padding-left");
|
||||
|
||||
let displayPosition = layout.position && layout.position != "static";
|
||||
let displayPosition = this.getDisplayPosition();
|
||||
let positionTop = this.getPositionValue("top");
|
||||
let positionRight = this.getPositionValue("right");
|
||||
let positionBottom = this.getPositionValue("bottom");
|
||||
|
@ -222,7 +449,12 @@ module.exports = createClass({
|
|||
},
|
||||
BoxModelEditable({
|
||||
box: "content",
|
||||
focusable,
|
||||
level,
|
||||
property: "width",
|
||||
ref: editable => {
|
||||
this.contentEditable = editable;
|
||||
},
|
||||
textContent: width,
|
||||
onShowBoxModelEditor
|
||||
}),
|
||||
|
@ -232,6 +464,8 @@ module.exports = createClass({
|
|||
),
|
||||
BoxModelEditable({
|
||||
box: "content",
|
||||
focusable,
|
||||
level,
|
||||
property: "height",
|
||||
textContent: height,
|
||||
onShowBoxModelEditor
|
||||
|
@ -253,6 +487,12 @@ module.exports = createClass({
|
|||
return dom.div(
|
||||
{
|
||||
className: "boxmodel-main",
|
||||
"data-box": "position",
|
||||
ref: div => {
|
||||
this.positionLayout = div;
|
||||
},
|
||||
onClick: this.onLevelClick,
|
||||
onKeyDown: this.onKeyDown,
|
||||
onMouseOver: this.onHighlightMouseOver,
|
||||
onMouseOut: this.props.onHideBoxModelHighlighter,
|
||||
},
|
||||
|
@ -301,6 +541,9 @@ module.exports = createClass({
|
|||
className: "boxmodel-margins",
|
||||
"data-box": "margin",
|
||||
title: BOXMODEL_L10N.getStr("boxmodel.margin"),
|
||||
ref: div => {
|
||||
this.marginLayout = div;
|
||||
},
|
||||
},
|
||||
dom.span(
|
||||
{
|
||||
|
@ -315,6 +558,9 @@ module.exports = createClass({
|
|||
className: "boxmodel-borders",
|
||||
"data-box": "border",
|
||||
title: BOXMODEL_L10N.getStr("boxmodel.border"),
|
||||
ref: div => {
|
||||
this.borderLayout = div;
|
||||
},
|
||||
},
|
||||
dom.span(
|
||||
{
|
||||
|
@ -329,11 +575,17 @@ module.exports = createClass({
|
|||
className: "boxmodel-paddings",
|
||||
"data-box": "padding",
|
||||
title: BOXMODEL_L10N.getStr("boxmodel.padding"),
|
||||
ref: div => {
|
||||
this.paddingLayout = div;
|
||||
},
|
||||
},
|
||||
dom.div({
|
||||
className: "boxmodel-contents",
|
||||
"data-box": "content",
|
||||
title: BOXMODEL_L10N.getStr("boxmodel.content"),
|
||||
ref: div => {
|
||||
this.contentLayout = div;
|
||||
},
|
||||
})
|
||||
)
|
||||
)
|
||||
|
@ -343,7 +595,12 @@ module.exports = createClass({
|
|||
BoxModelEditable({
|
||||
box: "position",
|
||||
direction: "top",
|
||||
focusable,
|
||||
level,
|
||||
property: "position-top",
|
||||
ref: editable => {
|
||||
this.positionEditable = editable;
|
||||
},
|
||||
textContent: positionTop,
|
||||
onShowBoxModelEditor,
|
||||
})
|
||||
|
@ -353,6 +610,8 @@ module.exports = createClass({
|
|||
BoxModelEditable({
|
||||
box: "position",
|
||||
direction: "right",
|
||||
focusable,
|
||||
level,
|
||||
property: "position-right",
|
||||
textContent: positionRight,
|
||||
onShowBoxModelEditor,
|
||||
|
@ -363,6 +622,8 @@ module.exports = createClass({
|
|||
BoxModelEditable({
|
||||
box: "position",
|
||||
direction: "bottom",
|
||||
focusable,
|
||||
level,
|
||||
property: "position-bottom",
|
||||
textContent: positionBottom,
|
||||
onShowBoxModelEditor,
|
||||
|
@ -373,6 +634,8 @@ module.exports = createClass({
|
|||
BoxModelEditable({
|
||||
box: "position",
|
||||
direction: "left",
|
||||
focusable,
|
||||
level,
|
||||
property: "position-left",
|
||||
textContent: positionLeft,
|
||||
onShowBoxModelEditor,
|
||||
|
@ -382,13 +645,20 @@ module.exports = createClass({
|
|||
BoxModelEditable({
|
||||
box: "margin",
|
||||
direction: "top",
|
||||
focusable,
|
||||
level,
|
||||
property: "margin-top",
|
||||
ref: editable => {
|
||||
this.marginEditable = editable;
|
||||
},
|
||||
textContent: marginTop,
|
||||
onShowBoxModelEditor,
|
||||
}),
|
||||
BoxModelEditable({
|
||||
box: "margin",
|
||||
direction: "right",
|
||||
focusable,
|
||||
level,
|
||||
property: "margin-right",
|
||||
textContent: marginRight,
|
||||
onShowBoxModelEditor,
|
||||
|
@ -396,6 +666,8 @@ module.exports = createClass({
|
|||
BoxModelEditable({
|
||||
box: "margin",
|
||||
direction: "bottom",
|
||||
focusable,
|
||||
level,
|
||||
property: "margin-bottom",
|
||||
textContent: marginBottom,
|
||||
onShowBoxModelEditor,
|
||||
|
@ -403,6 +675,8 @@ module.exports = createClass({
|
|||
BoxModelEditable({
|
||||
box: "margin",
|
||||
direction: "left",
|
||||
focusable,
|
||||
level,
|
||||
property: "margin-left",
|
||||
textContent: marginLeft,
|
||||
onShowBoxModelEditor,
|
||||
|
@ -410,13 +684,20 @@ module.exports = createClass({
|
|||
BoxModelEditable({
|
||||
box: "border",
|
||||
direction: "top",
|
||||
focusable,
|
||||
level,
|
||||
property: "border-top-width",
|
||||
ref: editable => {
|
||||
this.borderEditable = editable;
|
||||
},
|
||||
textContent: borderTop,
|
||||
onShowBoxModelEditor,
|
||||
}),
|
||||
BoxModelEditable({
|
||||
box: "border",
|
||||
direction: "right",
|
||||
focusable,
|
||||
level,
|
||||
property: "border-right-width",
|
||||
textContent: borderRight,
|
||||
onShowBoxModelEditor,
|
||||
|
@ -424,6 +705,8 @@ module.exports = createClass({
|
|||
BoxModelEditable({
|
||||
box: "border",
|
||||
direction: "bottom",
|
||||
focusable,
|
||||
level,
|
||||
property: "border-bottom-width",
|
||||
textContent: borderBottom,
|
||||
onShowBoxModelEditor,
|
||||
|
@ -431,6 +714,8 @@ module.exports = createClass({
|
|||
BoxModelEditable({
|
||||
box: "border",
|
||||
direction: "left",
|
||||
focusable,
|
||||
level,
|
||||
property: "border-left-width",
|
||||
textContent: borderLeft,
|
||||
onShowBoxModelEditor,
|
||||
|
@ -438,13 +723,20 @@ module.exports = createClass({
|
|||
BoxModelEditable({
|
||||
box: "padding",
|
||||
direction: "top",
|
||||
focusable,
|
||||
level,
|
||||
property: "padding-top",
|
||||
ref: editable => {
|
||||
this.paddingEditable = editable;
|
||||
},
|
||||
textContent: paddingTop,
|
||||
onShowBoxModelEditor,
|
||||
}),
|
||||
BoxModelEditable({
|
||||
box: "padding",
|
||||
direction: "right",
|
||||
focusable,
|
||||
level,
|
||||
property: "padding-right",
|
||||
textContent: paddingRight,
|
||||
onShowBoxModelEditor,
|
||||
|
@ -452,6 +744,8 @@ module.exports = createClass({
|
|||
BoxModelEditable({
|
||||
box: "padding",
|
||||
direction: "bottom",
|
||||
focusable,
|
||||
level,
|
||||
property: "padding-bottom",
|
||||
textContent: paddingBottom,
|
||||
onShowBoxModelEditor,
|
||||
|
@ -459,6 +753,8 @@ module.exports = createClass({
|
|||
BoxModelEditable({
|
||||
box: "padding",
|
||||
direction: "left",
|
||||
focusable,
|
||||
level,
|
||||
property: "padding-left",
|
||||
textContent: paddingLeft,
|
||||
onShowBoxModelEditor,
|
||||
|
|
|
@ -22,7 +22,6 @@ support-files =
|
|||
[browser_boxmodel_editablemodel_stylerules.js]
|
||||
[browser_boxmodel_guides.js]
|
||||
[browser_boxmodel_navigation.js]
|
||||
skip-if = true # Bug 1336198
|
||||
[browser_boxmodel_offsetparent.js]
|
||||
[browser_boxmodel_positions.js]
|
||||
[browser_boxmodel_properties.js]
|
||||
|
|
|
@ -28,49 +28,65 @@ add_task(function* () {
|
|||
|
||||
function* testInitialFocus(inspector, view) {
|
||||
info("Test that the focus is on margin layout.");
|
||||
let viewdoc = view.doc;
|
||||
let boxmodel = viewdoc.getElementById("boxmodel-wrapper");
|
||||
let viewdoc = view.document;
|
||||
let boxmodel = viewdoc.querySelector(".boxmodel-container");
|
||||
boxmodel.focus();
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
|
||||
is(boxmodel.getAttribute("aria-activedescendant"), "boxmodel-margins",
|
||||
"Should be set to the margin layout.");
|
||||
is(boxmodel.getAttribute("activedescendant"), "boxmodel-main",
|
||||
"Should be set to the position layout.");
|
||||
}
|
||||
|
||||
function* testChangingLevels(inspector, view) {
|
||||
info("Test that using arrow keys updates level.");
|
||||
let viewdoc = view.doc;
|
||||
let boxmodel = viewdoc.getElementById("boxmodel-wrapper");
|
||||
let viewdoc = view.document;
|
||||
let boxmodel = viewdoc.querySelector(".boxmodel-container");
|
||||
boxmodel.focus();
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
is(boxmodel.getAttribute("aria-activedescendant"), "boxmodel-borders",
|
||||
is(boxmodel.getAttribute("activedescendant"), "boxmodel-margins",
|
||||
"Should be set to the margin layout.");
|
||||
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
is(boxmodel.getAttribute("activedescendant"), "boxmodel-borders",
|
||||
"Should be set to the border layout.");
|
||||
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
is(boxmodel.getAttribute("aria-activedescendant"), "boxmodel-padding",
|
||||
is(boxmodel.getAttribute("activedescendant"), "boxmodel-paddings",
|
||||
"Should be set to the padding layout.");
|
||||
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
is(boxmodel.getAttribute("activedescendant"), "boxmodel-contents",
|
||||
"Should be set to the content layout.");
|
||||
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
is(boxmodel.getAttribute("activedescendant"), "boxmodel-paddings",
|
||||
"Should be set to the padding layout.");
|
||||
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
is(boxmodel.getAttribute("aria-activedescendant"), "boxmodel-borders",
|
||||
is(boxmodel.getAttribute("activedescendant"), "boxmodel-borders",
|
||||
"Should be set to the border layout.");
|
||||
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
is(boxmodel.getAttribute("aria-activedescendant"), "boxmodel-margins",
|
||||
is(boxmodel.getAttribute("activedescendant"), "boxmodel-margins",
|
||||
"Should be set to the margin layout.");
|
||||
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
is(boxmodel.getAttribute("activedescendant"), "boxmodel-main",
|
||||
"Should be set to the position layout.");
|
||||
}
|
||||
|
||||
function* testTabbingWrapAround(inspector, view) {
|
||||
info("Test that using arrow keys updates level.");
|
||||
let viewdoc = view.doc;
|
||||
let boxmodel = viewdoc.getElementById("boxmodel-wrapper");
|
||||
let viewdoc = view.document;
|
||||
let boxmodel = viewdoc.querySelector(".boxmodel-container");
|
||||
boxmodel.focus();
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
|
||||
let editLevel = boxmodel.getAttribute("aria-activedescendant");
|
||||
let dataLevel = viewdoc.getElementById(editLevel).getAttribute("data-box");
|
||||
let editLevel = boxmodel.getAttribute("activedescendant");
|
||||
let dataLevel = viewdoc.querySelector(`.${editLevel}`).getAttribute("data-box");
|
||||
let editBoxes = [...viewdoc.querySelectorAll(
|
||||
`[data-box="${dataLevel}"].boxmodel-editable`)];
|
||||
|
||||
|
@ -86,18 +102,19 @@ function* testTabbingWrapAround(inspector, view) {
|
|||
|
||||
function* testChangingLevelsByClicking(inspector, view) {
|
||||
info("Test that clicking on levels updates level.");
|
||||
let viewdoc = view.doc;
|
||||
let boxmodel = viewdoc.getElementById("boxmodel-wrapper");
|
||||
let viewdoc = view.document;
|
||||
let boxmodel = viewdoc.querySelector(".boxmodel-container");
|
||||
boxmodel.focus();
|
||||
|
||||
let marginLayout = viewdoc.getElementById("boxmodel-margins");
|
||||
let borderLayout = viewdoc.getElementById("boxmodel-borders");
|
||||
let paddingLayout = viewdoc.getElementById("boxmodel-padding");
|
||||
let layouts = [paddingLayout, borderLayout, marginLayout];
|
||||
let marginLayout = viewdoc.querySelector(".boxmodel-margins");
|
||||
let borderLayout = viewdoc.querySelector(".boxmodel-borders");
|
||||
let paddingLayout = viewdoc.querySelector(".boxmodel-paddings");
|
||||
let contentLayout = viewdoc.querySelector(".boxmodel-contents");
|
||||
let layouts = [contentLayout, paddingLayout, borderLayout, marginLayout];
|
||||
|
||||
layouts.forEach(layout => {
|
||||
layout.click();
|
||||
is(boxmodel.getAttribute("aria-activedescendant"), layout.id,
|
||||
is(boxmodel.getAttribute("activedescendant"), layout.className,
|
||||
"Should be set to" + layout.getAttribute("data-box") + "layout.");
|
||||
});
|
||||
}
|
||||
|
|
|
@ -135,7 +135,7 @@
|
|||
|
||||
<div id="computedview-container">
|
||||
<div id="computedview-container-focusable" tabindex="-1">
|
||||
<div id="boxmodel-wrapper" tabindex="0">
|
||||
<div id="boxmodel-wrapper">
|
||||
</div>
|
||||
|
||||
<div id="propertyContainer" class="theme-separator" tabindex="0" dir="ltr">
|
||||
|
|
|
@ -69,6 +69,10 @@ add_task(function* () {
|
|||
".boxmodel-margin.boxmodel-top > span");
|
||||
EventUtils.synthesizeMouseAtCenter(margin, {}, inspector.panelWin);
|
||||
yield checkTextBox(inspector.panelDoc.activeElement, toolbox);
|
||||
|
||||
// Move the mouse out of the box-model region to avoid triggering the box model
|
||||
// highlighter.
|
||||
EventUtils.synthesizeMouseAtCenter(tag, {}, inspector.panelWin);
|
||||
});
|
||||
|
||||
function* checkTextBox(textBox, {textBoxContextMenuPopup}) {
|
||||
|
|
|
@ -7,10 +7,6 @@
|
|||
*/
|
||||
|
||||
.boxmodel-container {
|
||||
/* The view will grow bigger as the window gets resized, until 400px */
|
||||
max-width: 400px;
|
||||
margin: 0px auto;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
|
@ -33,9 +29,11 @@
|
|||
position: relative;
|
||||
color: var(--theme-selection-color);
|
||||
/* Make sure there is some space between the window's edges and the regions */
|
||||
margin: 14px 14px 4px 14px;
|
||||
margin: 14px auto;
|
||||
width: calc(100% - 2 * 14px);
|
||||
min-width: 240px;
|
||||
/* The view will grow bigger as the window gets resized, until 400px */
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.boxmodel-box {
|
||||
|
|
|
@ -77,8 +77,7 @@ var CSSUsageActor = protocol.ActorClassWithSpec(cssUsageSpec, {
|
|||
this._onTabLoad = this._onTabLoad.bind(this);
|
||||
this._onChange = this._onChange.bind(this);
|
||||
|
||||
this._notifyOn = Ci.nsIWebProgress.NOTIFY_STATUS |
|
||||
Ci.nsIWebProgress.NOTIFY_STATE_ALL;
|
||||
this._notifyOn = Ci.nsIWebProgress.NOTIFY_STATE_ALL;
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
|
@ -121,10 +120,6 @@ var CSSUsageActor = protocol.ActorClassWithSpec(cssUsageSpec, {
|
|||
}
|
||||
},
|
||||
|
||||
onLocationChange: () => {},
|
||||
onProgressChange: () => {},
|
||||
onSecurityChange: () => {},
|
||||
onStatusChange: () => {},
|
||||
destroy: () => {}
|
||||
};
|
||||
|
||||
|
|
|
@ -600,7 +600,6 @@ HighlighterEnvironment.prototype = {
|
|||
};
|
||||
|
||||
this.webProgress.addProgressListener(this.listener,
|
||||
Ci.nsIWebProgress.NOTIFY_STATUS |
|
||||
Ci.nsIWebProgress.NOTIFY_STATE_WINDOW |
|
||||
Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
|
||||
},
|
||||
|
|
|
@ -1516,7 +1516,6 @@ DebuggerProgressListener.prototype = {
|
|||
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
webProgress.addProgressListener(this,
|
||||
Ci.nsIWebProgress.NOTIFY_STATUS |
|
||||
Ci.nsIWebProgress.NOTIFY_STATE_WINDOW |
|
||||
Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
|
||||
|
||||
|
|
|
@ -2019,11 +2019,6 @@ ConsoleProgressListener.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
onLocationChange: function () {},
|
||||
onStatusChange: function () {},
|
||||
onProgressChange: function () {},
|
||||
onSecurityChange: function () {},
|
||||
|
||||
/**
|
||||
* Destroy the ConsoleProgressListener.
|
||||
*/
|
||||
|
|
|
@ -206,8 +206,7 @@ ShadowRoot::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
|
|||
void
|
||||
ShadowRoot::AddToIdTable(Element* aElement, nsIAtom* aId)
|
||||
{
|
||||
nsIdentifierMapEntry *entry =
|
||||
mIdentifierMap.PutEntry(nsDependentAtomString(aId));
|
||||
nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
|
||||
if (entry) {
|
||||
entry->AddIdElement(aElement);
|
||||
}
|
||||
|
@ -216,8 +215,7 @@ ShadowRoot::AddToIdTable(Element* aElement, nsIAtom* aId)
|
|||
void
|
||||
ShadowRoot::RemoveFromIdTable(Element* aElement, nsIAtom* aId)
|
||||
{
|
||||
nsIdentifierMapEntry *entry =
|
||||
mIdentifierMap.GetEntry(nsDependentAtomString(aId));
|
||||
nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
|
||||
if (entry) {
|
||||
entry->RemoveIdElement(aElement);
|
||||
if (entry->IsEmpty()) {
|
||||
|
|
|
@ -1675,17 +1675,12 @@ nsAttrValue::LoadImage(nsIDocument* aDocument)
|
|||
{
|
||||
NS_ASSERTION(Type() == eURL, "wrong type");
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
nsString val;
|
||||
ToString(val);
|
||||
NS_ASSERTION(!val.IsEmpty(),
|
||||
"How did we end up with an empty string for eURL");
|
||||
}
|
||||
#endif
|
||||
|
||||
MiscContainer* cont = GetMiscContainer();
|
||||
mozilla::css::URLValue* url = cont->mValue.mURL;
|
||||
|
||||
NS_ASSERTION(!url->mString.IsEmpty(),
|
||||
"How did we end up with an empty string for eURL");
|
||||
|
||||
mozilla::css::ImageValue* image =
|
||||
new css::ImageValue(url->GetURI(), url->mString,
|
||||
do_AddRef(url->mExtraData), aDocument);
|
||||
|
|
|
@ -529,7 +529,7 @@ nsIdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty()
|
|||
size_t
|
||||
nsIdentifierMapEntry::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
return nsStringHashKey::SizeOfExcludingThis(aMallocSizeOf);
|
||||
return mKey.mString.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
|
||||
}
|
||||
|
||||
// Helper structs for the content->subdoc map
|
||||
|
@ -2848,8 +2848,7 @@ nsDocument::AddToNameTable(Element *aElement, nsIAtom* aName)
|
|||
"Only put elements that need to be exposed as document['name'] in "
|
||||
"the named table.");
|
||||
|
||||
nsIdentifierMapEntry *entry =
|
||||
mIdentifierMap.PutEntry(nsDependentAtomString(aName));
|
||||
nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aName);
|
||||
|
||||
// Null for out-of-memory
|
||||
if (entry) {
|
||||
|
@ -2868,8 +2867,7 @@ nsDocument::RemoveFromNameTable(Element *aElement, nsIAtom* aName)
|
|||
if (mIdentifierMap.Count() == 0)
|
||||
return;
|
||||
|
||||
nsIdentifierMapEntry *entry =
|
||||
mIdentifierMap.GetEntry(nsDependentAtomString(aName));
|
||||
nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aName);
|
||||
if (!entry) // Could be false if the element was anonymous, hence never added
|
||||
return;
|
||||
|
||||
|
@ -2883,8 +2881,7 @@ nsDocument::RemoveFromNameTable(Element *aElement, nsIAtom* aName)
|
|||
void
|
||||
nsDocument::AddToIdTable(Element *aElement, nsIAtom* aId)
|
||||
{
|
||||
nsIdentifierMapEntry *entry =
|
||||
mIdentifierMap.PutEntry(nsDependentAtomString(aId));
|
||||
nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
|
||||
|
||||
if (entry) { /* True except on OOM */
|
||||
if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
|
||||
|
@ -2906,8 +2903,7 @@ nsDocument::RemoveFromIdTable(Element *aElement, nsIAtom* aId)
|
|||
return;
|
||||
}
|
||||
|
||||
nsIdentifierMapEntry *entry =
|
||||
mIdentifierMap.GetEntry(nsDependentAtomString(aId));
|
||||
nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
|
||||
if (!entry) // Can be null for XML elements with changing ids.
|
||||
return;
|
||||
|
||||
|
@ -5123,7 +5119,7 @@ nsDocument::AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
|
|||
if (!CheckGetElementByIdArg(id))
|
||||
return nullptr;
|
||||
|
||||
nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(id);
|
||||
nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aID);
|
||||
NS_ENSURE_TRUE(entry, nullptr);
|
||||
|
||||
entry->AddContentChangeCallback(aObserver, aData, aForImage);
|
||||
|
@ -5139,7 +5135,7 @@ nsDocument::RemoveIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
|
|||
if (!CheckGetElementByIdArg(id))
|
||||
return;
|
||||
|
||||
nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(id);
|
||||
nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aID);
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -143,27 +143,91 @@ public:
|
|||
* Perhaps the document.all results should have their own hashtable
|
||||
* in nsHTMLDocument.
|
||||
*/
|
||||
class nsIdentifierMapEntry : public nsStringHashKey
|
||||
class nsIdentifierMapEntry : public PLDHashEntryHdr
|
||||
{
|
||||
public:
|
||||
struct AtomOrString
|
||||
{
|
||||
MOZ_IMPLICIT AtomOrString(nsIAtom* aAtom) : mAtom(aAtom) {}
|
||||
MOZ_IMPLICIT AtomOrString(const nsAString& aString) : mString(aString) {}
|
||||
AtomOrString(const AtomOrString& aOther)
|
||||
: mAtom(aOther.mAtom)
|
||||
, mString(aOther.mString)
|
||||
{
|
||||
}
|
||||
|
||||
AtomOrString(AtomOrString&& aOther)
|
||||
: mAtom(aOther.mAtom.forget())
|
||||
, mString(aOther.mString)
|
||||
{
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAtom> mAtom;
|
||||
const nsString mString;
|
||||
};
|
||||
|
||||
typedef const AtomOrString& KeyType;
|
||||
typedef const AtomOrString* KeyTypePointer;
|
||||
|
||||
typedef mozilla::dom::Element Element;
|
||||
typedef mozilla::net::ReferrerPolicy ReferrerPolicy;
|
||||
|
||||
explicit nsIdentifierMapEntry(const nsAString& aKey) :
|
||||
nsStringHashKey(&aKey), mNameContentList(nullptr)
|
||||
explicit nsIdentifierMapEntry(const AtomOrString& aKey)
|
||||
: mKey(aKey)
|
||||
{
|
||||
}
|
||||
explicit nsIdentifierMapEntry(const nsAString* aKey) :
|
||||
nsStringHashKey(aKey), mNameContentList(nullptr)
|
||||
explicit nsIdentifierMapEntry(const AtomOrString* aKey)
|
||||
: mKey(aKey ? *aKey : nullptr)
|
||||
{
|
||||
}
|
||||
nsIdentifierMapEntry(const nsIdentifierMapEntry& aOther) :
|
||||
nsStringHashKey(&aOther.GetKey())
|
||||
nsIdentifierMapEntry(nsIdentifierMapEntry&& aOther) :
|
||||
mKey(mozilla::Move(aOther.GetKey())),
|
||||
mIdContentList(mozilla::Move(aOther.mIdContentList)),
|
||||
mNameContentList(aOther.mNameContentList.forget()),
|
||||
mChangeCallbacks(aOther.mChangeCallbacks.forget()),
|
||||
mImageElement(aOther.mImageElement.forget())
|
||||
{
|
||||
NS_ERROR("Should never be called");
|
||||
}
|
||||
~nsIdentifierMapEntry();
|
||||
|
||||
KeyType GetKey() const { return mKey; }
|
||||
|
||||
nsString GetKeyAsString() const
|
||||
{
|
||||
if (mKey.mAtom) {
|
||||
return nsAtomString(mKey.mAtom);
|
||||
}
|
||||
|
||||
return mKey.mString;
|
||||
}
|
||||
|
||||
bool KeyEquals(const KeyTypePointer aOtherKey) const
|
||||
{
|
||||
if (mKey.mAtom) {
|
||||
if (aOtherKey->mAtom) {
|
||||
return mKey.mAtom == aOtherKey->mAtom;
|
||||
}
|
||||
|
||||
return mKey.mAtom->Equals(aOtherKey->mString);
|
||||
}
|
||||
|
||||
if (aOtherKey->mAtom) {
|
||||
return aOtherKey->mAtom->Equals(mKey.mString);
|
||||
}
|
||||
|
||||
return mKey.mString.Equals(aOtherKey->mString);
|
||||
}
|
||||
|
||||
static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
|
||||
|
||||
static PLDHashNumber HashKey(const KeyTypePointer aKey)
|
||||
{
|
||||
return aKey->mAtom ?
|
||||
aKey->mAtom->hash() : mozilla::HashString(aKey->mString);
|
||||
}
|
||||
|
||||
enum { ALLOW_MEMMOVE = false };
|
||||
|
||||
void AddNameElement(nsINode* aDocument, Element* aElement);
|
||||
void RemoveNameElement(Element* aElement);
|
||||
bool IsEmpty();
|
||||
|
@ -254,12 +318,16 @@ public:
|
|||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
||||
|
||||
private:
|
||||
nsIdentifierMapEntry(const nsIdentifierMapEntry& aOther) = delete;
|
||||
nsIdentifierMapEntry& operator=(const nsIdentifierMapEntry& aOther) = delete;
|
||||
|
||||
void FireChangeCallbacks(Element* aOldElement, Element* aNewElement,
|
||||
bool aImageOnly = false);
|
||||
|
||||
AtomOrString mKey;
|
||||
// empty if there are no elements with this ID.
|
||||
// The elements are stored as weak pointers.
|
||||
nsTArray<Element*> mIdContentList;
|
||||
AutoTArray<Element*, 1> mIdContentList;
|
||||
RefPtr<nsBaseContentList> mNameContentList;
|
||||
nsAutoPtr<nsTHashtable<ChangeCallbackEntry> > mChangeCallbacks;
|
||||
RefPtr<Element> mImageElement;
|
||||
|
|
|
@ -1714,10 +1714,6 @@ BrowserElementChild.prototype = {
|
|||
mixedContent: isMixedContent,
|
||||
});
|
||||
},
|
||||
|
||||
onStatusChange: function(webProgress, request, status, message) {},
|
||||
onProgressChange: function(webProgress, request, curSelfProgress,
|
||||
maxSelfProgress, curTotalProgress, maxTotalProgress) {},
|
||||
},
|
||||
|
||||
// Expose the message manager for WebApps and others.
|
||||
|
|
|
@ -962,14 +962,8 @@ nsGenericHTMLElement::ParseBackgroundAttribute(int32_t aNamespaceID,
|
|||
return false;
|
||||
}
|
||||
|
||||
nsString value(aValue);
|
||||
RefPtr<nsStringBuffer> buffer = nsCSSValue::BufferFromString(value);
|
||||
if (MOZ_UNLIKELY(!buffer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mozilla::css::URLValue *url =
|
||||
new mozilla::css::URLValue(uri, buffer, baseURI, doc->GetDocumentURI(),
|
||||
new mozilla::css::URLValue(uri, aValue, baseURI, doc->GetDocumentURI(),
|
||||
NodePrincipal());
|
||||
aResult.SetTo(url, &aValue);
|
||||
return true;
|
||||
|
|
|
@ -2312,7 +2312,7 @@ nsHTMLDocument::GetSupportedNames(nsTArray<nsString>& aNames)
|
|||
nsIdentifierMapEntry* entry = iter.Get();
|
||||
if (entry->HasNameElement() ||
|
||||
entry->HasIdElementExposedAsHTMLDocumentProperty()) {
|
||||
aNames.AppendElement(entry->GetKey());
|
||||
aNames.AppendElement(entry->GetKeyAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,17 @@
|
|||
|
||||
#include "ContentPrefs.h"
|
||||
|
||||
/************************************************************
|
||||
* DO NOT ADD PREFS TO THIS LIST WITHOUT DOM PEER REVIEW *
|
||||
************************************************************/
|
||||
/******************************************************************************
|
||||
*
|
||||
* DO NOT ADD PREFS TO THIS LIST WITHOUT DOM PEER REVIEW
|
||||
*
|
||||
* This is the list of preferences that are sent to the content process on
|
||||
* startup. Only prefs that are required immediately upon startup should be
|
||||
* listed here. The first IPC message received in the content process will
|
||||
* contain all the other prefs. Prefs should only be listed here if they must be
|
||||
* read before the first IPC message is received.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
const char* mozilla::dom::ContentPrefs::gInitPrefs[] = {
|
||||
"accessibility.monoaudio.enable",
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#ifndef mozilla_dom_ContentPrefs_h
|
||||
#define mozilla_dom_ContentPrefs_h
|
||||
|
||||
// See the comment in ContentPrefs.cpp for more information.
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
// Test that texImage2D on an animated image doesn't assert.
|
||||
|
||||
var gl;
|
||||
|
||||
function start() {
|
||||
canvas = document.getElementById("glcanvas");
|
||||
gl = null;
|
||||
|
||||
try {
|
||||
gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
|
||||
}
|
||||
catch(e) {
|
||||
}
|
||||
|
||||
if (!gl) {
|
||||
return;
|
||||
}
|
||||
|
||||
var texture = gl.createTexture();
|
||||
var image = new Image();
|
||||
image.onload = function() { handleTextureLoaded(image, texture); }
|
||||
image.src = "1249576-1.png"; // an animated png
|
||||
}
|
||||
|
||||
function handleTextureLoaded(image, texture) {
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body onload="start()">
|
||||
<canvas id="glcanvas" width="640" height="480">
|
||||
Your browser doesn't appear to support the <code><canvas></code> element.
|
||||
</canvas>
|
||||
</body>
|
||||
</html>
|
|
@ -24,6 +24,7 @@ load 1242093-1.html
|
|||
load 1242778-1.png
|
||||
load 1249576-1.png
|
||||
load 1253362-1.html
|
||||
load 1355898-1.html
|
||||
load colormap-range.gif
|
||||
HTTP load delayedframe.sjs # A 3-frame animated GIF with an inordinate delay between the second and third frame
|
||||
|
||||
|
|
|
@ -97,29 +97,26 @@ CheckIsValidConstructible(const Value& v);
|
|||
|
||||
class MOZ_STACK_CLASS IncludeUsedRval
|
||||
{
|
||||
protected:
|
||||
#ifdef JS_DEBUG
|
||||
mutable bool usedRval_;
|
||||
|
||||
public:
|
||||
bool usedRval() const { return usedRval_; }
|
||||
void setUsedRval() const { usedRval_ = true; }
|
||||
void clearUsedRval() const { usedRval_ = false; }
|
||||
void assertUnusedRval() const { MOZ_ASSERT(!usedRval_); }
|
||||
#else
|
||||
void setUsedRval() const {}
|
||||
void clearUsedRval() const {}
|
||||
void assertUnusedRval() const {}
|
||||
#endif
|
||||
};
|
||||
|
||||
class MOZ_STACK_CLASS NoUsedRval
|
||||
{
|
||||
protected:
|
||||
public:
|
||||
bool usedRval() const { return false; }
|
||||
void setUsedRval() const {}
|
||||
void clearUsedRval() const {}
|
||||
void assertUnusedRval() const {}
|
||||
};
|
||||
|
||||
template<class WantUsedRval>
|
||||
class MOZ_STACK_CLASS CallArgsBase : public WantUsedRval
|
||||
class MOZ_STACK_CLASS CallArgsBase
|
||||
{
|
||||
static_assert(mozilla::IsSame<WantUsedRval, IncludeUsedRval>::value ||
|
||||
mozilla::IsSame<WantUsedRval, NoUsedRval>::value,
|
||||
|
@ -133,6 +130,19 @@ class MOZ_STACK_CLASS CallArgsBase : public WantUsedRval
|
|||
// True if the caller does not use the return value.
|
||||
bool ignoresReturnValue_:1;
|
||||
|
||||
#ifdef JS_DEBUG
|
||||
WantUsedRval wantUsedRval_;
|
||||
bool usedRval() const { return wantUsedRval_.usedRval(); }
|
||||
void setUsedRval() const { wantUsedRval_.setUsedRval(); }
|
||||
void clearUsedRval() const { wantUsedRval_.clearUsedRval(); }
|
||||
void assertUnusedRval() const { wantUsedRval_.assertUnusedRval(); }
|
||||
#else
|
||||
bool usedRval() const { return false; }
|
||||
void setUsedRval() const {}
|
||||
void clearUsedRval() const {}
|
||||
void assertUnusedRval() const {}
|
||||
#endif
|
||||
|
||||
public:
|
||||
// CALLEE ACCESS
|
||||
|
||||
|
@ -160,7 +170,7 @@ class MOZ_STACK_CLASS CallArgsBase : public WantUsedRval
|
|||
return false;
|
||||
|
||||
#ifdef JS_DEBUG
|
||||
if (!this->usedRval_)
|
||||
if (!this->usedRval())
|
||||
CheckIsValidConstructible(calleev());
|
||||
#endif
|
||||
|
||||
|
|
|
@ -775,40 +775,45 @@ struct JSClass {
|
|||
void* reserved[3];
|
||||
};
|
||||
|
||||
#define JSCLASS_HAS_PRIVATE (1<<0) // objects have private slot
|
||||
#define JSCLASS_DELAY_METADATA_BUILDER (1<<1) // class's initialization code
|
||||
// will call
|
||||
// SetNewObjectMetadata itself
|
||||
#define JSCLASS_IS_WRAPPED_NATIVE (1<<2) // class is an XPCWrappedNative.
|
||||
// WeakMaps use this to override
|
||||
// the wrapper disposal
|
||||
// mechanism.
|
||||
#define JSCLASS_PRIVATE_IS_NSISUPPORTS (1<<3) // private is (nsISupports*)
|
||||
#define JSCLASS_IS_DOMJSCLASS (1<<4) // objects are DOM
|
||||
#define JSCLASS_HAS_XRAYED_CONSTRUCTOR (1<<5) // if wrapped by an xray
|
||||
// wrapper, the builtin
|
||||
// class's constructor won't
|
||||
// be unwrapped and invoked.
|
||||
// Instead, the constructor is
|
||||
// resolved in the caller's
|
||||
// compartment and invoked
|
||||
// with a wrapped newTarget.
|
||||
// The constructor has to
|
||||
// detect and handle this
|
||||
// situation.
|
||||
// See PromiseConstructor for
|
||||
// details.
|
||||
#define JSCLASS_EMULATES_UNDEFINED (1<<6) // objects of this class act
|
||||
// like the value undefined,
|
||||
// in some contexts
|
||||
#define JSCLASS_USERBIT1 (1<<7) // Reserved for embeddings.
|
||||
// Objects have private slot.
|
||||
static const uint32_t JSCLASS_HAS_PRIVATE = 1 << 0;
|
||||
|
||||
// Class's initialization code will call `SetNewObjectMetadata` itself.
|
||||
static const uint32_t JSCLASS_DELAY_METADATA_BUILDER = 1 << 1;
|
||||
|
||||
// Class is an XPCWrappedNative. WeakMaps use this to override the wrapper
|
||||
// disposal mechanism.
|
||||
static const uint32_t JSCLASS_IS_WRAPPED_NATIVE = 1 << 2;
|
||||
|
||||
// Private is `nsISupports*`.
|
||||
static const uint32_t JSCLASS_PRIVATE_IS_NSISUPPORTS = 1 << 3;
|
||||
|
||||
// Objects are DOM.
|
||||
static const uint32_t JSCLASS_IS_DOMJSCLASS = 1 << 4;
|
||||
|
||||
// If wrapped by an xray wrapper, the builtin class's constructor won't be
|
||||
// unwrapped and invoked. Instead, the constructor is resolved in the caller's
|
||||
// compartment and invoked with a wrapped newTarget. The constructor has to
|
||||
// detect and handle this situation. See PromiseConstructor for details.
|
||||
static const uint32_t JSCLASS_HAS_XRAYED_CONSTRUCTOR = 1 << 5;
|
||||
|
||||
// Objects of this class act like the value undefined, in some contexts.
|
||||
static const uint32_t JSCLASS_EMULATES_UNDEFINED = 1 << 6;
|
||||
|
||||
// Reserved for embeddings.
|
||||
static const uint32_t JSCLASS_USERBIT1 = 1 << 7;
|
||||
|
||||
// To reserve slots fetched and stored via JS_Get/SetReservedSlot, bitwise-or
|
||||
// JSCLASS_HAS_RESERVED_SLOTS(n) into the initializer for JSClass.flags, where
|
||||
// n is a constant in [1, 255]. Reserved slots are indexed from 0 to n-1.
|
||||
#define JSCLASS_RESERVED_SLOTS_SHIFT 8 // room for 8 flags below */
|
||||
#define JSCLASS_RESERVED_SLOTS_WIDTH 8 // and 16 above this field */
|
||||
#define JSCLASS_RESERVED_SLOTS_MASK JS_BITMASK(JSCLASS_RESERVED_SLOTS_WIDTH)
|
||||
// JSCLASS_HAS_RESERVED_SLOTS(n) into the initializer for JSClass.flags, where n
|
||||
// is a constant in [1, 255]. Reserved slots are indexed from 0 to n-1.
|
||||
|
||||
// Room for 8 flags below ...
|
||||
static const uintptr_t JSCLASS_RESERVED_SLOTS_SHIFT = 8;
|
||||
// ... and 16 above this field.
|
||||
static const uint32_t JSCLASS_RESERVED_SLOTS_WIDTH = 8;
|
||||
|
||||
static const uint32_t JSCLASS_RESERVED_SLOTS_MASK = JS_BITMASK(JSCLASS_RESERVED_SLOTS_WIDTH);
|
||||
|
||||
#define JSCLASS_HAS_RESERVED_SLOTS(n) (((n) & JSCLASS_RESERVED_SLOTS_MASK) \
|
||||
<< JSCLASS_RESERVED_SLOTS_SHIFT)
|
||||
#define JSCLASS_RESERVED_SLOTS(clasp) (((clasp)->flags \
|
||||
|
@ -818,21 +823,19 @@ struct JSClass {
|
|||
#define JSCLASS_HIGH_FLAGS_SHIFT (JSCLASS_RESERVED_SLOTS_SHIFT + \
|
||||
JSCLASS_RESERVED_SLOTS_WIDTH)
|
||||
|
||||
#define JSCLASS_IS_ANONYMOUS (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0))
|
||||
#define JSCLASS_IS_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1))
|
||||
#define JSCLASS_INTERNAL_FLAG2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2))
|
||||
#define JSCLASS_INTERNAL_FLAG3 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+3))
|
||||
|
||||
#define JSCLASS_IS_PROXY (1<<(JSCLASS_HIGH_FLAGS_SHIFT+4))
|
||||
|
||||
#define JSCLASS_SKIP_NURSERY_FINALIZE (1<<(JSCLASS_HIGH_FLAGS_SHIFT+5))
|
||||
static const uint32_t JSCLASS_IS_ANONYMOUS = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 0);
|
||||
static const uint32_t JSCLASS_IS_GLOBAL = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 1);
|
||||
static const uint32_t JSCLASS_INTERNAL_FLAG2 = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 2);
|
||||
static const uint32_t JSCLASS_INTERNAL_FLAG3 = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 3);
|
||||
static const uint32_t JSCLASS_IS_PROXY = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 4);
|
||||
static const uint32_t JSCLASS_SKIP_NURSERY_FINALIZE = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 5);
|
||||
|
||||
// Reserved for embeddings.
|
||||
#define JSCLASS_USERBIT2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+6))
|
||||
#define JSCLASS_USERBIT3 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+7))
|
||||
static const uint32_t JSCLASS_USERBIT2 = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 6);
|
||||
static const uint32_t JSCLASS_USERBIT3 = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 7);
|
||||
|
||||
#define JSCLASS_BACKGROUND_FINALIZE (1<<(JSCLASS_HIGH_FLAGS_SHIFT+8))
|
||||
#define JSCLASS_FOREGROUND_FINALIZE (1<<(JSCLASS_HIGH_FLAGS_SHIFT+9))
|
||||
static const uint32_t JSCLASS_BACKGROUND_FINALIZE = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 8);
|
||||
static const uint32_t JSCLASS_FOREGROUND_FINALIZE = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 9);
|
||||
|
||||
// Bits 26 through 31 are reserved for the CACHED_PROTO_KEY mechanism, see
|
||||
// below.
|
||||
|
@ -850,10 +853,11 @@ struct JSClass {
|
|||
// JSCLASS_GLOBAL_APPLICATION_SLOTS is the number of slots reserved at
|
||||
// the beginning of every global object's slots for use by the
|
||||
// application.
|
||||
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
|
||||
#define JSCLASS_GLOBAL_SLOT_COUNT \
|
||||
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 46)
|
||||
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
|
||||
static const uint32_t JSCLASS_GLOBAL_APPLICATION_SLOTS = 5;
|
||||
static const uint32_t JSCLASS_GLOBAL_SLOT_COUNT =
|
||||
JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 46;
|
||||
|
||||
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
|
||||
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
|
||||
#define JSCLASS_GLOBAL_FLAGS \
|
||||
JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0)
|
||||
|
@ -862,8 +866,9 @@ struct JSClass {
|
|||
&& JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT)
|
||||
|
||||
// Fast access to the original value of each standard class's prototype.
|
||||
#define JSCLASS_CACHED_PROTO_SHIFT (JSCLASS_HIGH_FLAGS_SHIFT + 10)
|
||||
#define JSCLASS_CACHED_PROTO_MASK JS_BITMASK(js::JSCLASS_CACHED_PROTO_WIDTH)
|
||||
static const uint32_t JSCLASS_CACHED_PROTO_SHIFT = JSCLASS_HIGH_FLAGS_SHIFT + 10;
|
||||
static const uint32_t JSCLASS_CACHED_PROTO_MASK = JS_BITMASK(js::JSCLASS_CACHED_PROTO_WIDTH);
|
||||
|
||||
#define JSCLASS_HAS_CACHED_PROTO(key) (uint32_t(key) << JSCLASS_CACHED_PROTO_SHIFT)
|
||||
#define JSCLASS_CACHED_PROTO_KEY(clasp) ((JSProtoKey) \
|
||||
(((clasp)->flags \
|
||||
|
|
|
@ -755,6 +755,24 @@ class alignas(8) DispatchWrapper
|
|||
|
||||
namespace JS {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
* For pointer types, the TraceKind for tracing is based on the list it is
|
||||
* in (selected via MapTypeToRootKind), so no additional storage is
|
||||
* required here. Non-pointer types, however, share the same list, so the
|
||||
* function to call for tracing is stored adjacent to the struct. Since C++
|
||||
* cannot templatize on storage class, this is implemented via the wrapper
|
||||
* class DispatchWrapper.
|
||||
*/
|
||||
template <typename T>
|
||||
using MaybeWrapped = typename mozilla::Conditional<
|
||||
MapTypeToRootKind<T>::kind == JS::RootKind::Traceable,
|
||||
js::DispatchWrapper<T>,
|
||||
T>::Type;
|
||||
|
||||
} /* namespace detail */
|
||||
|
||||
/**
|
||||
* Local variable of type T whose value is always rooted. This is typically
|
||||
* used for local variables, or for non-rooted values being passed to a
|
||||
|
@ -825,19 +843,7 @@ class MOZ_RAII Rooted : public js::RootedBase<T, Rooted<T>>
|
|||
Rooted<void*>** stack;
|
||||
Rooted<void*>* prev;
|
||||
|
||||
/*
|
||||
* For pointer types, the TraceKind for tracing is based on the list it is
|
||||
* in (selected via MapTypeToRootKind), so no additional storage is
|
||||
* required here. Non-pointer types, however, share the same list, so the
|
||||
* function to call for tracing is stored adjacent to the struct. Since C++
|
||||
* cannot templatize on storage class, this is implemented via the wrapper
|
||||
* class DispatchWrapper.
|
||||
*/
|
||||
using MaybeWrapped = typename mozilla::Conditional<
|
||||
MapTypeToRootKind<T>::kind == JS::RootKind::Traceable,
|
||||
js::DispatchWrapper<T>,
|
||||
T>::Type;
|
||||
MaybeWrapped ptr;
|
||||
detail::MaybeWrapped<T> ptr;
|
||||
|
||||
Rooted(const Rooted&) = delete;
|
||||
} JS_HAZ_ROOTED;
|
||||
|
@ -1188,12 +1194,7 @@ class PersistentRooted : public js::RootedBase<T, PersistentRooted<T>>,
|
|||
ptr = mozilla::Forward<U>(value);
|
||||
}
|
||||
|
||||
// See the comment above Rooted::ptr.
|
||||
using MaybeWrapped = typename mozilla::Conditional<
|
||||
MapTypeToRootKind<T>::kind == JS::RootKind::Traceable,
|
||||
js::DispatchWrapper<T>,
|
||||
T>::Type;
|
||||
MaybeWrapped ptr;
|
||||
detail::MaybeWrapped<T> ptr;
|
||||
} JS_HAZ_ROOTED;
|
||||
|
||||
class JS_PUBLIC_API(ObjectPtr)
|
||||
|
|
|
@ -4,6 +4,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libz-sys 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -26,6 +27,14 @@ dependencies = [
|
|||
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.9"
|
||||
|
@ -35,4 +44,5 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum gcc 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)" = "291055c78f59ca3d84c99026c9501c469413d386bb46be1e1cf1d285cd1db3b0"
|
||||
"checksum libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "684f330624d8c3784fb9558ca46c4ce488073a8d22450415c5eb4f4cfb0d11b5"
|
||||
"checksum libz-sys 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "7616099a575493da60cddc1174b686fcfb00ece89dc6f61f31ff47c35f07bbe8"
|
||||
"checksum num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a225d1e2717567599c24f88e49f00856c6e825a12125181ee42c4257e3688d39"
|
||||
"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
|
||||
|
|
|
@ -13,6 +13,9 @@ promises = []
|
|||
name = "mozjs_sys"
|
||||
path = "lib.rs"
|
||||
|
||||
[build-dependencies]
|
||||
num_cpus = "1.1.0"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
libz-sys = "1.0"
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
extern crate num_cpus;
|
||||
|
||||
use std::env;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
|
@ -11,6 +13,7 @@ fn main() {
|
|||
|
||||
let js_src = env::var("CARGO_MANIFEST_DIR").expect("Should have env var CARGO_MANIFEST_DIR");
|
||||
|
||||
env::set_var("MAKEFLAGS", format!("-j{}", num_cpus::get()));
|
||||
env::set_current_dir(&js_src).unwrap();
|
||||
|
||||
let variant = if cfg!(feature = "debugmozjs") {
|
||||
|
@ -37,6 +40,11 @@ fn main() {
|
|||
assert!(result.success(), "autospider should exit OK");
|
||||
|
||||
println!("cargo:rustc-link-search=native={}/js/src/build", out_dir);
|
||||
println!("cargo:rustc-link-search=native={}/js/src", out_dir);
|
||||
println!("cargo:rustc-link-lib=static=js_static");
|
||||
|
||||
println!("cargo:rustc-link-search=native={}/dist/bin", out_dir);
|
||||
println!("cargo:rustc-link-lib=nspr4");
|
||||
|
||||
if target.contains("windows") {
|
||||
println!("cargo:rustc-link-lib=winmm");
|
||||
|
@ -47,6 +55,5 @@ fn main() {
|
|||
println!("cargo:rustc-link-lib=stdc++");
|
||||
}
|
||||
|
||||
println!("cargo:rustc-link-lib=static=js_static");
|
||||
println!("cargo:outdir={}", out_dir);
|
||||
}
|
||||
|
|
|
@ -379,7 +379,7 @@ function CanonicalizeLanguageTag(locale) {
|
|||
locale = callFunction(std_String_toLowerCase, locale);
|
||||
|
||||
// Handle mappings for complete tags.
|
||||
if (callFunction(std_Object_hasOwnProperty, langTagMappings, locale))
|
||||
if (hasOwn(locale, langTagMappings))
|
||||
return langTagMappings[locale];
|
||||
|
||||
var subtags = StringSplitString(ToString(locale), "-");
|
||||
|
@ -411,7 +411,7 @@ function CanonicalizeLanguageTag(locale) {
|
|||
subtag = callFunction(std_String_toUpperCase, subtag);
|
||||
}
|
||||
}
|
||||
if (callFunction(std_Object_hasOwnProperty, langSubtagMappings, subtag)) {
|
||||
if (hasOwn(subtag, langSubtagMappings)) {
|
||||
// Replace deprecated subtags with their preferred values.
|
||||
// "BU" -> "MM"
|
||||
// This has to come after we capitalize region codes because
|
||||
|
@ -421,7 +421,7 @@ function CanonicalizeLanguageTag(locale) {
|
|||
// Note that the script generating langSubtagMappings makes sure
|
||||
// that no regular subtag mapping will replace an extlang code.
|
||||
subtag = langSubtagMappings[subtag];
|
||||
} else if (callFunction(std_Object_hasOwnProperty, extlangMappings, subtag)) {
|
||||
} else if (hasOwn(subtag, extlangMappings)) {
|
||||
// Replace deprecated extlang subtags with their preferred values,
|
||||
// and remove the preceding subtag if it's a redundant prefix.
|
||||
// "zh-nan" -> "nan"
|
||||
|
@ -510,11 +510,10 @@ function ValidateAndCanonicalizeLanguageTag(locale) {
|
|||
|
||||
// langTagMappings doesn't contain any 2*3ALPHA keys, so we don't need
|
||||
// to check for possible replacements in this map.
|
||||
assert(!callFunction(std_Object_hasOwnProperty, langTagMappings, locale),
|
||||
"langTagMappings contains no 2*3ALPHA mappings");
|
||||
assert(!hasOwn(locale, langTagMappings), "langTagMappings contains no 2*3ALPHA mappings");
|
||||
|
||||
// Replace deprecated subtags with their preferred values.
|
||||
locale = callFunction(std_Object_hasOwnProperty, langSubtagMappings, locale)
|
||||
locale = hasOwn(locale, langSubtagMappings)
|
||||
? langSubtagMappings[locale]
|
||||
: locale;
|
||||
assert(locale === CanonicalizeLanguageTag(locale), "expected same canonicalization");
|
||||
|
@ -607,7 +606,7 @@ function DefaultLocaleIgnoringAvailableLocales() {
|
|||
// remove any present in the candidate.
|
||||
candidate = removeUnicodeExtensions(candidate);
|
||||
|
||||
if (callFunction(std_Object_hasOwnProperty, oldStyleLanguageTagMappings, candidate))
|
||||
if (hasOwn(candidate, oldStyleLanguageTagMappings))
|
||||
candidate = oldStyleLanguageTagMappings[candidate];
|
||||
}
|
||||
|
||||
|
@ -1370,16 +1369,14 @@ function getIntlObjectInternals(obj) {
|
|||
var internals = UnsafeGetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT);
|
||||
|
||||
assert(IsObject(internals), "internals not an object");
|
||||
assert(callFunction(std_Object_hasOwnProperty, internals, "type"), "missing type");
|
||||
assert(hasOwn("type", internals), "missing type");
|
||||
assert((internals.type === "Collator" && IsCollator(obj)) ||
|
||||
(internals.type === "DateTimeFormat" && IsDateTimeFormat(obj)) ||
|
||||
(internals.type === "NumberFormat" && IsNumberFormat(obj)) ||
|
||||
(internals.type === "PluralRules" && IsPluralRules(obj)),
|
||||
"type must match the object's class");
|
||||
assert(callFunction(std_Object_hasOwnProperty, internals, "lazyData"),
|
||||
"missing lazyData");
|
||||
assert(callFunction(std_Object_hasOwnProperty, internals, "internalProps"),
|
||||
"missing internalProps");
|
||||
assert(hasOwn("lazyData", internals), "missing lazyData");
|
||||
assert(hasOwn("internalProps", internals), "missing internalProps");
|
||||
|
||||
return internals;
|
||||
}
|
||||
|
@ -2119,7 +2116,7 @@ function CurrencyDigits(currency) {
|
|||
assert(typeof currency === "string", "CurrencyDigits");
|
||||
assert(regexp_test_no_statics(getCurrencyDigitsRE(), currency), "CurrencyDigits");
|
||||
|
||||
if (callFunction(std_Object_hasOwnProperty, currencyDigits, currency))
|
||||
if (hasOwn(currency, currencyDigits))
|
||||
return currencyDigits[currency];
|
||||
return 2;
|
||||
}
|
||||
|
@ -2269,7 +2266,7 @@ function Intl_NumberFormat_resolvedOptions() {
|
|||
];
|
||||
for (var i = 0; i < optionalProperties.length; i++) {
|
||||
var p = optionalProperties[i];
|
||||
if (callFunction(std_Object_hasOwnProperty, internals, p))
|
||||
if (hasOwn(p, internals))
|
||||
_DefineDataProperty(result, p, internals[p]);
|
||||
}
|
||||
return result;
|
||||
|
@ -3060,7 +3057,7 @@ function resolveICUPattern(pattern, result) {
|
|||
default:
|
||||
// skip other pattern characters and literal text
|
||||
}
|
||||
if (callFunction(std_Object_hasOwnProperty, icuPatternCharToComponent, c))
|
||||
if (hasOwn(c, icuPatternCharToComponent))
|
||||
_DefineDataProperty(result, icuPatternCharToComponent[c], value);
|
||||
if (c === "h" || c === "K")
|
||||
_DefineDataProperty(result, "hour12", true);
|
||||
|
@ -3295,7 +3292,7 @@ function Intl_PluralRules_resolvedOptions() {
|
|||
|
||||
for (var i = 0; i < optionalProperties.length; i++) {
|
||||
var p = optionalProperties[i];
|
||||
if (callFunction(std_Object_hasOwnProperty, internals, p))
|
||||
if (hasOwn(p, internals))
|
||||
_DefineDataProperty(result, p, internals[p]);
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -685,50 +685,6 @@ obj_unwatch(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
#endif /* JS_HAS_OBJ_WATCHPOINT */
|
||||
|
||||
/* ECMA 15.2.4.5. */
|
||||
bool
|
||||
js::obj_hasOwnProperty(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
HandleValue idValue = args.get(0);
|
||||
|
||||
// As an optimization, provide a fast path when rooting is not necessary and
|
||||
// we can safely retrieve the object's shape.
|
||||
|
||||
/* Step 1, 2. */
|
||||
jsid id;
|
||||
if (args.thisv().isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
|
||||
JSObject* obj = &args.thisv().toObject();
|
||||
PropertyResult prop;
|
||||
if (obj->isNative() &&
|
||||
NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id, &prop))
|
||||
{
|
||||
args.rval().setBoolean(prop.isFound());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 1. */
|
||||
RootedId idRoot(cx);
|
||||
if (!ToPropertyKey(cx, idValue, &idRoot))
|
||||
return false;
|
||||
|
||||
/* Step 2. */
|
||||
RootedObject obj(cx, ToObject(cx, args.thisv()));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
/* Step 3. */
|
||||
bool found;
|
||||
if (!HasOwnProperty(cx, obj, idRoot, &found))
|
||||
return false;
|
||||
|
||||
/* Step 4,5. */
|
||||
args.rval().setBoolean(found);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ES5 15.2.4.6. */
|
||||
static bool
|
||||
obj_isPrototypeOf(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
@ -1320,7 +1276,7 @@ static const JSFunctionSpec object_methods[] = {
|
|||
JS_FN(js_watch_str, obj_watch, 2,0),
|
||||
JS_FN(js_unwatch_str, obj_unwatch, 1,0),
|
||||
#endif
|
||||
JS_FN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0),
|
||||
JS_SELF_HOSTED_FN(js_hasOwnProperty_str, "Object_hasOwnProperty", 1,0),
|
||||
JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0),
|
||||
JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0),
|
||||
#if JS_OLD_GETTER_SETTER_METHODS
|
||||
|
|
|
@ -48,8 +48,6 @@ obj_getOwnPropertyDescriptor(JSContext* cx, unsigned argc, JS::Value* vp);
|
|||
MOZ_MUST_USE bool
|
||||
obj_getPrototypeOf(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
MOZ_MUST_USE bool
|
||||
obj_hasOwnProperty(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
MOZ_MUST_USE bool
|
||||
obj_isExtensible(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
|
|
@ -93,6 +93,13 @@ function Object_valueOf() {
|
|||
return ToObject(this);
|
||||
}
|
||||
|
||||
// ES 2018 draft 19.1.3.2
|
||||
function Object_hasOwnProperty(V) {
|
||||
// Implement hasOwnProperty as a pseudo function that becomes a JSOp
|
||||
// to easier add an inline cache for this.
|
||||
return hasOwn(V, this);
|
||||
}
|
||||
|
||||
// ES7 draft (2016 March 8) B.2.2.3
|
||||
function ObjectDefineSetter(name, setter) {
|
||||
// Step 1.
|
||||
|
@ -160,7 +167,7 @@ function ObjectLookupSetter(name) {
|
|||
// Step 3.b.
|
||||
if (desc) {
|
||||
// Step.b.i.
|
||||
if (callFunction(std_Object_hasOwnProperty, desc, "set"))
|
||||
if (hasOwn("set", desc))
|
||||
return desc.set;
|
||||
|
||||
// Step.b.ii.
|
||||
|
@ -189,7 +196,7 @@ function ObjectLookupGetter(name) {
|
|||
// Step 3.b.
|
||||
if (desc) {
|
||||
// Step.b.i.
|
||||
if (callFunction(std_Object_hasOwnProperty, desc, "get"))
|
||||
if (hasOwn("get", desc))
|
||||
return desc.get;
|
||||
|
||||
// Step.b.ii.
|
||||
|
|
|
@ -28,11 +28,6 @@
|
|||
#define DESCR_STRUCT_FIELD_OFFSETS(obj) \
|
||||
UnsafeGetObjectFromReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS)
|
||||
|
||||
// Other
|
||||
|
||||
#define HAS_PROPERTY(obj, prop) \
|
||||
callFunction(std_Object_hasOwnProperty, obj, prop)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Getting values
|
||||
//
|
||||
|
|
|
@ -139,7 +139,11 @@ JOBS = { 'dbs':
|
|||
'%(analysis_scriptdir)s/explain.py',
|
||||
'%(hazards)s', '%(gcFunctions)s',
|
||||
'[explained_hazards]', '[unnecessary]', '[refs]'),
|
||||
('hazards.txt', 'unnecessary.txt', 'refs.txt'))
|
||||
('hazards.txt', 'unnecessary.txt', 'refs.txt')),
|
||||
|
||||
'heapwrites':
|
||||
(('%(js)s', '%(analysis_scriptdir)s/analyzeHeapWrites.js'),
|
||||
'heapWriteHazards.txt'),
|
||||
}
|
||||
|
||||
def out_indexes(command):
|
||||
|
@ -258,7 +262,7 @@ if 'SOURCE' in os.environ:
|
|||
data['source'] = os.environ['SOURCE']
|
||||
if not data.get('source') and data.get('sixgill_bin'):
|
||||
path = subprocess.check_output(['sh', '-c', data['sixgill_bin'] + '/xdbkeys file_source.xdb | grep jsapi.cpp'])
|
||||
data['source'] = path.replace("/js/src/jsapi.cpp", "")
|
||||
data['source'] = path.replace("\n", "").replace("/js/src/jsapi.cpp", "")
|
||||
|
||||
steps = [ 'dbs',
|
||||
'gcTypes',
|
||||
|
@ -266,7 +270,8 @@ steps = [ 'dbs',
|
|||
'gcFunctions',
|
||||
'allFunctions',
|
||||
'hazards',
|
||||
'explain' ]
|
||||
'explain',
|
||||
'heapwrites' ]
|
||||
|
||||
if args.list:
|
||||
for step in steps:
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,215 @@
|
|||
loadRelativeToScript('utility.js');
|
||||
loadRelativeToScript('annotations.js');
|
||||
loadRelativeToScript('CFG.js');
|
||||
|
||||
var subclasses = new Map(); // Map from csu => set of immediate subclasses
|
||||
var superclasses = new Map(); // Map from csu => set of immediate superclasses
|
||||
var classFunctions = new Map(); // Map from "csu:name" => set of full method name
|
||||
|
||||
var virtualResolutionsSeen = new Set();
|
||||
|
||||
// map is a map from names to sets of entries.
|
||||
function addToNamedSet(map, name, entry)
|
||||
{
|
||||
if (!map.has(name))
|
||||
map.set(name, new Set());
|
||||
map.get(name).add(entry);
|
||||
}
|
||||
|
||||
function fieldKey(csuName, field)
|
||||
{
|
||||
// Note: not dealing with overloading correctly.
|
||||
var nargs = 0;
|
||||
if (field.Type.Kind == "Function" && "TypeFunctionArguments" in field.Type)
|
||||
nargs = field.Type.TypeFunctionArguments.length;
|
||||
return csuName + ":" + field.Name[0] + ":" + nargs;
|
||||
}
|
||||
|
||||
// CSU is "Class/Struct/Union"
|
||||
function processCSU(csuName, csu)
|
||||
{
|
||||
if (!("FunctionField" in csu))
|
||||
return;
|
||||
for (const field of csu.FunctionField) {
|
||||
if (1 in field.Field) {
|
||||
const superclass = field.Field[1].Type.Name;
|
||||
const subclass = field.Field[1].FieldCSU.Type.Name;
|
||||
assert(subclass == csuName);
|
||||
addToNamedSet(subclasses, superclass, subclass);
|
||||
addToNamedSet(superclasses, subclass, superclass);
|
||||
}
|
||||
if ("Variable" in field) {
|
||||
// Note: not dealing with overloading correctly.
|
||||
const name = field.Variable.Name[0];
|
||||
addToNamedSet(classFunctions, fieldKey(csuName, field.Field[0]), name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the nearest ancestor method definition, or all nearest definitions in
|
||||
// the case of multiple inheritance.
|
||||
function nearestAncestorMethods(csu, field)
|
||||
{
|
||||
const key = fieldKey(csu, field);
|
||||
|
||||
if (classFunctions.has(key))
|
||||
return new Set(classFunctions.get(key));
|
||||
|
||||
const functions = new Set();
|
||||
if (superclasses.has(csu)) {
|
||||
for (const parent of superclasses.get(csu))
|
||||
functions.update(nearestAncestorMethods(parent, field));
|
||||
}
|
||||
|
||||
return functions;
|
||||
}
|
||||
|
||||
// Return [ instantations, suppressed ], where instantiations is a Set of all
|
||||
// possible implementations of 'field' given static type 'initialCSU', plus
|
||||
// null if arbitrary other implementations are possible, and suppressed is true
|
||||
// if we the method is assumed to be non-GC'ing by annotation.
|
||||
function findVirtualFunctions(initialCSU, field)
|
||||
{
|
||||
const fieldName = field.Name[0];
|
||||
const worklist = [initialCSU];
|
||||
const functions = new Set();
|
||||
|
||||
// Loop through all methods of initialCSU (by looking at all methods of ancestor csus).
|
||||
//
|
||||
// If field is nsISupports::AddRef or ::Release, return an empty list and a
|
||||
// boolean that says we assert that it cannot GC.
|
||||
//
|
||||
// If this is a method that is annotated to be dangerous (eg, it could be
|
||||
// overridden with an implementation that could GC), then use null as a
|
||||
// signal value that it should be considered to GC, even though we'll also
|
||||
// collect all of the instantiations for other purposes.
|
||||
|
||||
while (worklist.length) {
|
||||
const csu = worklist.pop();
|
||||
if (isSuppressedVirtualMethod(csu, fieldName))
|
||||
return [ new Set(), true ];
|
||||
if (isOverridableField(initialCSU, csu, fieldName)) {
|
||||
// We will still resolve the virtual function call, because it's
|
||||
// nice to have as complete a callgraph as possible for other uses.
|
||||
// But push a token saying that we can run arbitrary code.
|
||||
functions.add(null);
|
||||
}
|
||||
|
||||
if (superclasses.has(csu))
|
||||
worklist.push(...superclasses.get(csu));
|
||||
}
|
||||
|
||||
// Now return a list of all the instantiations of the method named 'field'
|
||||
// that could execute on an instance of initialCSU or a descendant class.
|
||||
|
||||
// Start with the class itself, or if it doesn't define the method, all
|
||||
// nearest ancestor definitions.
|
||||
functions.update(nearestAncestorMethods(initialCSU, field));
|
||||
|
||||
// Then recurse through all descendants to add in their definitions.
|
||||
|
||||
worklist.push(initialCSU);
|
||||
while (worklist.length) {
|
||||
const csu = worklist.pop();
|
||||
const key = fieldKey(csu, field);
|
||||
|
||||
if (classFunctions.has(key))
|
||||
functions.update(classFunctions.get(key));
|
||||
|
||||
if (subclasses.has(csu))
|
||||
worklist.push(...subclasses.get(csu));
|
||||
}
|
||||
|
||||
return [ functions, false ];
|
||||
}
|
||||
|
||||
// Return a list of all callees that the given edge might be a call to. Each
|
||||
// one is represented by an object with a 'kind' field that is one of
|
||||
// ('direct', 'field', 'resolved-field', 'indirect', 'unknown'), though note
|
||||
// that 'resolved-field' is really a global record of virtual method
|
||||
// resolutions, indepedent of this particular edge.
|
||||
function getCallees(edge)
|
||||
{
|
||||
if (edge.Kind != "Call")
|
||||
return [];
|
||||
|
||||
const callee = edge.Exp[0];
|
||||
if (callee.Kind == "Var") {
|
||||
assert(callee.Variable.Kind == "Func");
|
||||
return [{'kind': 'direct', 'name': callee.Variable.Name[0]}];
|
||||
}
|
||||
|
||||
assert(callee.Kind == "Drf");
|
||||
const called = callee.Exp[0];
|
||||
if (called.Kind == "Var") {
|
||||
// indirect call through a variable.
|
||||
return [{'kind': "indirect", 'variable': callee.Exp[0].Variable.Name[0]}];
|
||||
}
|
||||
|
||||
if (called.Kind != "Fld") {
|
||||
// unknown call target.
|
||||
return [{'kind': "unknown"}];
|
||||
}
|
||||
|
||||
const callees = [];
|
||||
const field = callee.Exp[0].Field;
|
||||
const fieldName = field.Name[0];
|
||||
const csuName = field.FieldCSU.Type.Name;
|
||||
let functions;
|
||||
if ("FieldInstanceFunction" in field) {
|
||||
let suppressed;
|
||||
[ functions, suppressed ] = findVirtualFunctions(csuName, field, suppressed);
|
||||
if (suppressed) {
|
||||
// Field call known to not GC; mark it as suppressed so direct
|
||||
// invocations will be ignored
|
||||
callees.push({'kind': "field", 'csu': csuName, 'field': fieldName,
|
||||
'suppressed': true, 'isVirtual': true});
|
||||
}
|
||||
} else {
|
||||
functions = new Set([null]); // field call
|
||||
}
|
||||
|
||||
// Known set of virtual call targets. Treat them as direct calls to all
|
||||
// possible resolved types, but also record edges from this field call to
|
||||
// each final callee. When the analysis is checking whether an edge can GC
|
||||
// and it sees an unrooted pointer held live across this field call, it
|
||||
// will know whether any of the direct callees can GC or not.
|
||||
const targets = [];
|
||||
let fullyResolved = true;
|
||||
for (const name of functions) {
|
||||
if (name === null) {
|
||||
// Unknown set of call targets, meaning either a function pointer
|
||||
// call ("field call") or a virtual method that can be overridden
|
||||
// in extensions. Use the isVirtual property so that callers can
|
||||
// tell which case holds.
|
||||
callees.push({'kind': "field", 'csu': csuName, 'field': fieldName,
|
||||
'isVirtual': "FieldInstanceFunction" in field});
|
||||
fullyResolved = false;
|
||||
} else {
|
||||
callees.push({'kind': "direct", 'name': name});
|
||||
targets.push({'kind': "direct", 'name': name});
|
||||
}
|
||||
}
|
||||
if (fullyResolved)
|
||||
callees.push({'kind': "resolved-field", 'csu': csuName, 'field': fieldName, 'callees': targets});
|
||||
|
||||
return callees;
|
||||
}
|
||||
|
||||
function loadTypes(type_xdb_filename) {
|
||||
const xdb = xdbLibrary();
|
||||
xdb.open(type_xdb_filename);
|
||||
|
||||
const minStream = xdb.min_data_stream();
|
||||
const maxStream = xdb.max_data_stream();
|
||||
|
||||
for (var csuIndex = minStream; csuIndex <= maxStream; csuIndex++) {
|
||||
const csu = xdb.read_key(csuIndex);
|
||||
const data = xdb.read_entry(csu);
|
||||
const json = JSON.parse(data.readString());
|
||||
processCSU(csu.readString(), json[0]);
|
||||
|
||||
xdb.free_string(csu);
|
||||
xdb.free_string(data);
|
||||
}
|
||||
}
|
|
@ -2,9 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
loadRelativeToScript('utility.js');
|
||||
loadRelativeToScript('annotations.js');
|
||||
loadRelativeToScript('CFG.js');
|
||||
loadRelativeToScript('callgraph.js');
|
||||
|
||||
var theFunctionNameToFind;
|
||||
if (scriptArgs[0] == '--function') {
|
||||
|
@ -14,116 +12,6 @@ if (scriptArgs[0] == '--function') {
|
|||
|
||||
var typeInfo_filename = scriptArgs[0] || "typeInfo.txt";
|
||||
|
||||
var subclasses = new Map(); // Map from csu => set of immediate subclasses
|
||||
var superclasses = new Map(); // Map from csu => set of immediate superclasses
|
||||
var classFunctions = new Map(); // Map from "csu:name" => set of full method name
|
||||
|
||||
var virtualResolutionsSeen = new Set();
|
||||
|
||||
function addEntry(map, name, entry)
|
||||
{
|
||||
if (!map.has(name))
|
||||
map.set(name, new Set());
|
||||
map.get(name).add(entry);
|
||||
}
|
||||
|
||||
// CSU is "Class/Struct/Union"
|
||||
function processCSU(csuName, csu)
|
||||
{
|
||||
if (!("FunctionField" in csu))
|
||||
return;
|
||||
for (var field of csu.FunctionField) {
|
||||
if (1 in field.Field) {
|
||||
var superclass = field.Field[1].Type.Name;
|
||||
var subclass = field.Field[1].FieldCSU.Type.Name;
|
||||
assert(subclass == csuName);
|
||||
addEntry(subclasses, superclass, subclass);
|
||||
addEntry(superclasses, subclass, superclass);
|
||||
}
|
||||
if ("Variable" in field) {
|
||||
// Note: not dealing with overloading correctly.
|
||||
var name = field.Variable.Name[0];
|
||||
var key = csuName + ":" + field.Field[0].Name[0];
|
||||
addEntry(classFunctions, key, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the nearest ancestor method definition, or all nearest definitions in
|
||||
// the case of multiple inheritance.
|
||||
function nearestAncestorMethods(csu, method)
|
||||
{
|
||||
var key = csu + ":" + method;
|
||||
|
||||
if (classFunctions.has(key))
|
||||
return new Set(classFunctions.get(key));
|
||||
|
||||
var functions = new Set();
|
||||
if (superclasses.has(csu)) {
|
||||
for (var parent of superclasses.get(csu))
|
||||
functions.update(nearestAncestorMethods(parent, method));
|
||||
}
|
||||
|
||||
return functions;
|
||||
}
|
||||
|
||||
// Return [ instantations, suppressed ], where instantiations is a Set of all
|
||||
// possible implementations of 'field' given static type 'initialCSU', plus
|
||||
// null if arbitrary other implementations are possible, and suppressed is true
|
||||
// if we the method is assumed to be non-GC'ing by annotation.
|
||||
function findVirtualFunctions(initialCSU, field)
|
||||
{
|
||||
var worklist = [initialCSU];
|
||||
var functions = new Set();
|
||||
|
||||
// Loop through all methods of initialCSU (by looking at all methods of ancestor csus).
|
||||
//
|
||||
// If field is nsISupports::AddRef or ::Release, return an empty list and a
|
||||
// boolean that says we assert that it cannot GC.
|
||||
//
|
||||
// If this is a method that is annotated to be dangerous (eg, it could be
|
||||
// overridden with an implementation that could GC), then use null as a
|
||||
// signal value that it should be considered to GC, even though we'll also
|
||||
// collect all of the instantiations for other purposes.
|
||||
|
||||
while (worklist.length) {
|
||||
var csu = worklist.pop();
|
||||
if (isSuppressedVirtualMethod(csu, field))
|
||||
return [ new Set(), true ];
|
||||
if (isOverridableField(initialCSU, csu, field)) {
|
||||
// We will still resolve the virtual function call, because it's
|
||||
// nice to have as complete a callgraph as possible for other uses.
|
||||
// But push a token saying that we can run arbitrary code.
|
||||
functions.add(null);
|
||||
}
|
||||
|
||||
if (superclasses.has(csu))
|
||||
worklist.push(...superclasses.get(csu));
|
||||
}
|
||||
|
||||
// Now return a list of all the instantiations of the method named 'field'
|
||||
// that could execute on an instance of initialCSU or a descendant class.
|
||||
|
||||
// Start with the class itself, or if it doesn't define the method, all
|
||||
// nearest ancestor definitions.
|
||||
functions.update(nearestAncestorMethods(initialCSU, field));
|
||||
|
||||
// Then recurse through all descendants to add in their definitions.
|
||||
var worklist = [initialCSU];
|
||||
while (worklist.length) {
|
||||
var csu = worklist.pop();
|
||||
var key = csu + ":" + field;
|
||||
|
||||
if (classFunctions.has(key))
|
||||
functions.update(classFunctions.get(key));
|
||||
|
||||
if (subclasses.has(csu))
|
||||
worklist.push(...subclasses.get(csu));
|
||||
}
|
||||
|
||||
return [ functions, false ];
|
||||
}
|
||||
|
||||
var memoized = new Map();
|
||||
var memoizedCount = 0;
|
||||
|
||||
|
@ -137,75 +25,6 @@ function memo(name)
|
|||
return memoized.get(name);
|
||||
}
|
||||
|
||||
// Return a list of all callees that the given edge might be a call to. Each
|
||||
// one is represented by an object with a 'kind' field that is one of
|
||||
// ('direct', 'field', 'resolved-field', 'indirect', 'unknown'), though note
|
||||
// that 'resolved-field' is really a global record of virtual method
|
||||
// resolutions, indepedent of this particular edge.
|
||||
function getCallees(edge)
|
||||
{
|
||||
if (edge.Kind != "Call")
|
||||
return [];
|
||||
|
||||
var callee = edge.Exp[0];
|
||||
var callees = [];
|
||||
if (callee.Kind == "Var") {
|
||||
assert(callee.Variable.Kind == "Func");
|
||||
callees.push({'kind': 'direct', 'name': callee.Variable.Name[0]});
|
||||
} else {
|
||||
assert(callee.Kind == "Drf");
|
||||
if (callee.Exp[0].Kind == "Fld") {
|
||||
var field = callee.Exp[0].Field;
|
||||
var fieldName = field.Name[0];
|
||||
var csuName = field.FieldCSU.Type.Name;
|
||||
var functions;
|
||||
if ("FieldInstanceFunction" in field) {
|
||||
let suppressed;
|
||||
[ functions, suppressed ] = findVirtualFunctions(csuName, fieldName, suppressed);
|
||||
if (suppressed) {
|
||||
// Field call known to not GC; mark it as suppressed so
|
||||
// direct invocations will be ignored
|
||||
callees.push({'kind': "field", 'csu': csuName, 'field': fieldName,
|
||||
'suppressed': true});
|
||||
}
|
||||
} else {
|
||||
functions = new Set([null]); // field call
|
||||
}
|
||||
|
||||
// Known set of virtual call targets. Treat them as direct calls to
|
||||
// all possible resolved types, but also record edges from this
|
||||
// field call to each final callee. When the analysis is checking
|
||||
// whether an edge can GC and it sees an unrooted pointer held live
|
||||
// across this field call, it will know whether any of the direct
|
||||
// callees can GC or not.
|
||||
var targets = [];
|
||||
var fullyResolved = true;
|
||||
for (var name of functions) {
|
||||
if (name === null) {
|
||||
// Unknown set of call targets, meaning either a function
|
||||
// pointer call ("field call") or a virtual method that can
|
||||
// be overridden in extensions.
|
||||
callees.push({'kind': "field", 'csu': csuName, 'field': fieldName});
|
||||
fullyResolved = false;
|
||||
} else {
|
||||
callees.push({'kind': "direct", 'name': name});
|
||||
targets.push({'kind': "direct", 'name': name});
|
||||
}
|
||||
}
|
||||
if (fullyResolved)
|
||||
callees.push({'kind': "resolved-field", 'csu': csuName, 'field': fieldName, 'callees': targets});
|
||||
} else if (callee.Exp[0].Kind == "Var") {
|
||||
// indirect call through a variable.
|
||||
callees.push({'kind': "indirect", 'variable': callee.Exp[0].Variable.Name[0]});
|
||||
} else {
|
||||
// unknown call target.
|
||||
callees.push({'kind': "unknown"});
|
||||
}
|
||||
}
|
||||
|
||||
return callees;
|
||||
}
|
||||
|
||||
var lastline;
|
||||
function printOnce(line)
|
||||
{
|
||||
|
@ -279,8 +98,9 @@ function processBody(functionName, body)
|
|||
printOnce("D " + prologue + memo(callee.name));
|
||||
}
|
||||
} else if (callee.kind == 'field') {
|
||||
var { csu, field } = callee;
|
||||
printOnce("F " + prologue + "CLASS " + csu + " FIELD " + field);
|
||||
var { csu, field, isVirtual } = callee;
|
||||
const tag = isVirtual ? 'V' : 'F';
|
||||
printOnce(tag + " " + prologue + "CLASS " + csu + " FIELD " + field);
|
||||
} else if (callee.kind == 'resolved-field') {
|
||||
// Fully-resolved field (virtual method) call. Record the
|
||||
// callgraph edges. Do not consider suppression, since it is
|
||||
|
@ -310,22 +130,9 @@ function processBody(functionName, body)
|
|||
|
||||
GCSuppressionTypes = loadTypeInfo(typeInfo_filename)["Suppress GC"] || [];
|
||||
|
||||
loadTypes("src_comp.xdb");
|
||||
|
||||
var xdb = xdbLibrary();
|
||||
xdb.open("src_comp.xdb");
|
||||
|
||||
var minStream = xdb.min_data_stream();
|
||||
var maxStream = xdb.max_data_stream();
|
||||
|
||||
for (var csuIndex = minStream; csuIndex <= maxStream; csuIndex++) {
|
||||
var csu = xdb.read_key(csuIndex);
|
||||
var data = xdb.read_entry(csu);
|
||||
var json = JSON.parse(data.readString());
|
||||
processCSU(csu.readString(), json[0]);
|
||||
|
||||
xdb.free_string(csu);
|
||||
xdb.free_string(data);
|
||||
}
|
||||
|
||||
xdb.open("src_body.xdb");
|
||||
|
||||
printErr("Finished loading data structures");
|
||||
|
|
|
@ -99,7 +99,7 @@ function loadCallgraph(file)
|
|||
var name = match[2];
|
||||
if (!indirectCallCannotGC(functionNames[match[1]], name) && !suppressed)
|
||||
addGCFunction(mangledCaller, "IndirectCall: " + name);
|
||||
} else if (match = tag == 'F' && /^F (\d+) CLASS (.*?) FIELD (.*)/.exec(line)) {
|
||||
} else if (match = (tag == 'F' || tag == 'V') && /^[FV] (\d+) CLASS (.*?) FIELD (.*)/.exec(line)) {
|
||||
var caller = idToMangled[match[1]];
|
||||
var csu = match[2];
|
||||
var fullfield = csu + "." + match[3];
|
||||
|
|
|
@ -51,6 +51,70 @@ function xprint(x, padding)
|
|||
}
|
||||
}
|
||||
|
||||
function parse_options(parameters, inArgs = scriptArgs) {
|
||||
const options = {};
|
||||
|
||||
const optional = {};
|
||||
const positional = [];
|
||||
for (const param of parameters) {
|
||||
if (param.name.startsWith("-")) {
|
||||
optional[param.name] = param;
|
||||
param.dest = param.dest || param.name.substring(2).replace("-", "_");
|
||||
} else {
|
||||
positional.push(param);
|
||||
param.dest = param.dest || param.name.replace("-", "_");
|
||||
}
|
||||
|
||||
param.type = param.type || 'bool';
|
||||
if ('default' in param)
|
||||
options[param.dest] = param.default;
|
||||
}
|
||||
|
||||
options.rest = [];
|
||||
const args = [...inArgs];
|
||||
while (args.length > 0) {
|
||||
let param;
|
||||
let pos = -1;
|
||||
if (args[0] in optional)
|
||||
param = optional[args[0]];
|
||||
else {
|
||||
pos = args[0].indexOf("=");
|
||||
if (pos != -1) {
|
||||
param = optional[args[0].substring(0, pos)];
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!param) {
|
||||
if (positional.length > 0) {
|
||||
param = positional.shift();
|
||||
options[param.dest] = args.shift();
|
||||
} else {
|
||||
options.rest.push(args.shift());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (param.type != 'bool') {
|
||||
if (pos != -1) {
|
||||
options[param.dest] = args.shift().substring(pos);
|
||||
} else {
|
||||
args.shift();
|
||||
if (args.length == 0)
|
||||
throw(new Error(`--${param.name} requires an argument`));
|
||||
options[param.dest] = args.shift();
|
||||
}
|
||||
} else {
|
||||
if (pos != -1)
|
||||
throw(new Error(`--${param.name} does not take an argument`));
|
||||
options[param.dest] = true;
|
||||
args.shift();
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function sameBlockId(id0, id1)
|
||||
{
|
||||
if (id0.Kind != id1.Kind)
|
||||
|
|
|
@ -9170,10 +9170,28 @@ BytecodeEmitter::emitSelfHostedDefineDataProperty(ParseNode* pn)
|
|||
// This will leave the object on the stack instead of pushing |undefined|,
|
||||
// but that's fine because the self-hosted code doesn't use the return
|
||||
// value.
|
||||
if (!emit1(JSOP_INITELEM))
|
||||
return emit1(JSOP_INITELEM);
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitSelfHostedHasOwn(ParseNode* pn)
|
||||
{
|
||||
if (pn->pn_count != 3) {
|
||||
reportError(pn, JSMSG_MORE_ARGS_NEEDED, "hasOwn", "2", "");
|
||||
return false;
|
||||
}
|
||||
|
||||
ParseNode* funNode = pn->pn_head; // The hasOwn node.
|
||||
|
||||
ParseNode* idNode = funNode->pn_next;
|
||||
if (!emitTree(idNode))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
ParseNode* objNode = idNode->pn_next;
|
||||
if (!emitTree(objNode))
|
||||
return false;
|
||||
|
||||
return emit1(JSOP_HASOWN);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -9296,6 +9314,8 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUs
|
|||
return emitSelfHostedAllowContentIter(pn);
|
||||
if (pn2->name() == cx->names().defineDataPropertyIntrinsic && pn->pn_count == 4)
|
||||
return emitSelfHostedDefineDataProperty(pn);
|
||||
if (pn2->name() == cx->names().hasOwn)
|
||||
return emitSelfHostedHasOwn(pn);
|
||||
// Fall through.
|
||||
}
|
||||
if (!emitGetName(pn2, callop))
|
||||
|
|
|
@ -761,6 +761,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
MOZ_MUST_USE bool emitSelfHostedForceInterpreter(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitSelfHostedAllowContentIter(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitSelfHostedDefineDataProperty(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitSelfHostedHasOwn(ParseNode* pn);
|
||||
|
||||
MOZ_MUST_USE bool emitComprehensionFor(ParseNode* compFor);
|
||||
MOZ_MUST_USE bool emitComprehensionForIn(ParseNode* pn);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "jit/IonBuilder.h"
|
||||
#include "jit/JitCompartment.h"
|
||||
|
||||
namespace js {
|
||||
|
@ -22,7 +23,8 @@ ZoneGroup::ZoneGroup(JSRuntime* runtime)
|
|||
ionBailAfter_(this, 0),
|
||||
#endif
|
||||
jitZoneGroup(this, nullptr),
|
||||
debuggerList_(this)
|
||||
debuggerList_(this),
|
||||
ionLazyLinkListSize_(0)
|
||||
{}
|
||||
|
||||
bool
|
||||
|
@ -39,6 +41,14 @@ ZoneGroup::init()
|
|||
|
||||
ZoneGroup::~ZoneGroup()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
{
|
||||
AutoLockHelperThreadState lock;
|
||||
MOZ_ASSERT(ionLazyLinkListSize_ == 0);
|
||||
MOZ_ASSERT(ionLazyLinkList().isEmpty());
|
||||
}
|
||||
#endif
|
||||
|
||||
js_delete(jitZoneGroup.ref());
|
||||
|
||||
if (this == runtime->gc.systemZoneGroup)
|
||||
|
@ -52,7 +62,7 @@ ZoneGroup::enter()
|
|||
if (ownerContext().context() == cx) {
|
||||
MOZ_ASSERT(enterCount);
|
||||
} else {
|
||||
MOZ_ASSERT(ownerContext().context() == nullptr);
|
||||
MOZ_RELEASE_ASSERT(ownerContext().context() == nullptr);
|
||||
MOZ_ASSERT(enterCount == 0);
|
||||
ownerContext_ = CooperatingContext(cx);
|
||||
if (cx->generationalDisabled)
|
||||
|
@ -77,4 +87,34 @@ ZoneGroup::ownedByCurrentThread()
|
|||
return ownerContext().context() == TlsContext.get();
|
||||
}
|
||||
|
||||
ZoneGroup::IonBuilderList&
|
||||
ZoneGroup::ionLazyLinkList()
|
||||
{
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime),
|
||||
"Should only be mutated by the active thread.");
|
||||
return ionLazyLinkList_.ref();
|
||||
}
|
||||
|
||||
void
|
||||
ZoneGroup::ionLazyLinkListRemove(jit::IonBuilder* builder)
|
||||
{
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime),
|
||||
"Should only be mutated by the active thread.");
|
||||
MOZ_ASSERT(ionLazyLinkListSize_ > 0);
|
||||
|
||||
builder->removeFrom(ionLazyLinkList());
|
||||
ionLazyLinkListSize_--;
|
||||
|
||||
MOZ_ASSERT(ionLazyLinkList().isEmpty() == (ionLazyLinkListSize_ == 0));
|
||||
}
|
||||
|
||||
void
|
||||
ZoneGroup::ionLazyLinkListAdd(jit::IonBuilder* builder)
|
||||
{
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime),
|
||||
"Should only be mutated by the active thread.");
|
||||
ionLazyLinkList().insertFront(builder);
|
||||
ionLazyLinkListSize_++;
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
|
|
@ -95,6 +95,23 @@ class ZoneGroup
|
|||
ZoneGroupData<mozilla::LinkedList<js::Debugger>> debuggerList_;
|
||||
public:
|
||||
mozilla::LinkedList<js::Debugger>& debuggerList() { return debuggerList_.ref(); }
|
||||
|
||||
private:
|
||||
/* List of Ion compilation waiting to get linked. */
|
||||
typedef mozilla::LinkedList<js::jit::IonBuilder> IonBuilderList;
|
||||
|
||||
js::HelperThreadLockData<IonBuilderList> ionLazyLinkList_;
|
||||
js::HelperThreadLockData<size_t> ionLazyLinkListSize_;
|
||||
|
||||
public:
|
||||
IonBuilderList& ionLazyLinkList();
|
||||
|
||||
size_t ionLazyLinkListSize() {
|
||||
return ionLazyLinkListSize_;
|
||||
}
|
||||
|
||||
void ionLazyLinkListRemove(js::jit::IonBuilder* builder);
|
||||
void ionLazyLinkListAdd(js::jit::IonBuilder* builder);
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
|
|
@ -37,6 +37,8 @@ function assertStackContainsSeq(got, expect)
|
|||
for (var j = 0; j < parts.length; j++) {
|
||||
var frame = parts[j];
|
||||
frame = frame.replace(/ \([^\)]*\)/g, "");
|
||||
frame = frame.replace(/fast FFI trampoline to native/g, "N");
|
||||
frame = frame.replace(/^call to( asm.js)? native .*\(in wasm\)$/g, "N");
|
||||
frame = frame.replace(/(fast|slow) FFI trampoline/g, "<");
|
||||
frame = frame.replace(/entry trampoline/g, ">");
|
||||
frame = frame.replace(/(\/[^\/,<]+)*\/testProfiling.js/g, "");
|
||||
|
@ -112,7 +114,7 @@ function testBuiltinD2D(name) {
|
|||
enableSingleStepProfiling();
|
||||
assertEq(f(.1), eval("Math." + name + "(.1)"));
|
||||
var stacks = disableSingleStepProfiling();
|
||||
assertStackContainsSeq(stacks, ">,f,>,f,>,>");
|
||||
assertStackContainsSeq(stacks, ">,f,>,N,f,>,f,>,>");
|
||||
}
|
||||
}
|
||||
for (name of ['sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'ceil', 'floor', 'exp', 'log'])
|
||||
|
@ -125,7 +127,7 @@ function testBuiltinF2F(name) {
|
|||
enableSingleStepProfiling();
|
||||
assertEq(f(.1), eval("Math.fround(Math." + name + "(Math.fround(.1)))"));
|
||||
var stacks = disableSingleStepProfiling();
|
||||
assertStackContainsSeq(stacks, ">,f,>,f,>,>");
|
||||
assertStackContainsSeq(stacks, ">,f,>,N,f,>,f,>,>");
|
||||
}
|
||||
}
|
||||
for (name of ['ceil', 'floor'])
|
||||
|
@ -138,7 +140,7 @@ function testBuiltinDD2D(name) {
|
|||
enableSingleStepProfiling();
|
||||
assertEq(f(.1, .2), eval("Math." + name + "(.1, .2)"));
|
||||
var stacks = disableSingleStepProfiling();
|
||||
assertStackContainsSeq(stacks, ">,f,>,f,>,>");
|
||||
assertStackContainsSeq(stacks, ">,f,>,N,f,>,f,>,>");
|
||||
}
|
||||
}
|
||||
for (name of ['atan2', 'pow'])
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
var max = 40;
|
||||
setJitCompilerOption("ion.warmup.trigger", max - 10);
|
||||
|
||||
function simple() {
|
||||
var array = [{a: 1}, {b: 1, a: 1}, {c: 1, a: 1}];
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
var x = array[i];
|
||||
assertEq(x.hasOwnProperty("a"), true);
|
||||
assertEq(x.hasOwnProperty("d"), false);
|
||||
}
|
||||
}
|
||||
|
||||
function megamorphic() {
|
||||
var array = [{a: 1}, {b: 1, a: 1}, {c: 1, a: 1},
|
||||
{a: 1, b: 1}, {c: 1, e: 1, a: 1},
|
||||
{e: 1, f: 1, a: 1, g: 1},
|
||||
{e: 1, f: 1, a: 1, g: 1, h: 1}];
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
var x = array[i];
|
||||
assertEq(x.hasOwnProperty("a"), true);
|
||||
assertEq(x.hasOwnProperty("d"), false);
|
||||
}
|
||||
}
|
||||
|
||||
function key() {
|
||||
var sym = Symbol(), sym2 = Symbol();
|
||||
var keys = [[sym, true], [sym2, false],
|
||||
["a", true], ["b", false],
|
||||
[{}, false]];
|
||||
var obj = {[sym]: 1, a: 1};
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var [key, result] = keys[i];
|
||||
assertEq(obj.hasOwnProperty(key), result);
|
||||
}
|
||||
}
|
||||
|
||||
function test() {
|
||||
for (var i = 0; i < max; i++) {
|
||||
simple();
|
||||
megamorphic();
|
||||
key();
|
||||
}
|
||||
}
|
||||
|
||||
test();
|
||||
test();
|
|
@ -15,7 +15,8 @@ function normalize(stack)
|
|||
var wasmFrameTypes = [
|
||||
{re:/^entry trampoline \(in wasm\)$/, sub:">"},
|
||||
{re:/^wasm-function\[(\d+)\] \(.*\)$/, sub:"$1"},
|
||||
{re:/^(fast|slow) FFI trampoline (to native |)\(in wasm\)$/, sub:"<"},
|
||||
{re:/^(fast|slow) FFI trampoline (to native )?\(in wasm\)$/, sub:"<"},
|
||||
{re:/^call to[ asm.js]? native (.*) \(in wasm\)$/, sub:"$1"},
|
||||
{re:/ \(in wasm\)$/, sub:""}
|
||||
];
|
||||
|
||||
|
@ -123,6 +124,58 @@ test(`(module
|
|||
this,
|
||||
["", ">", "1,>", "<,1,>", "1,>", ">", ""]);
|
||||
|
||||
if (getBuildConfiguration()["arm-simulator"]) {
|
||||
// On ARM, some int64 operations are calls to C++.
|
||||
for (let op of ['div_s', 'rem_s', 'div_u', 'rem_u']) {
|
||||
test(`(module
|
||||
(func (export "") (param i32) (result i32)
|
||||
get_local 0
|
||||
i64.extend_s/i32
|
||||
i64.const 0x1a2b3c4d5e6f
|
||||
i64.${op}
|
||||
i32.wrap/i64
|
||||
)
|
||||
)`,
|
||||
this,
|
||||
["", ">", "0,>", "<,0,>", `i64.${op},0,>`, "<,0,>", "0,>", ">", ""]);
|
||||
}
|
||||
}
|
||||
|
||||
// current_memory is a callout.
|
||||
test(`(module
|
||||
(memory 1)
|
||||
(func (export "") (result i32)
|
||||
current_memory
|
||||
)
|
||||
)`,
|
||||
this,
|
||||
["", ">", "0,>", "<,0,>", "current_memory,0,>", "<,0,>", "0,>", ">", ""]);
|
||||
|
||||
// grow_memory is a callout.
|
||||
test(`(module
|
||||
(memory 1)
|
||||
(func (export "") (result i32)
|
||||
i32.const 1
|
||||
grow_memory
|
||||
)
|
||||
)`,
|
||||
this,
|
||||
["", ">", "0,>", "<,0,>", "grow_memory,0,>", "<,0,>", "0,>", ">", ""]);
|
||||
|
||||
// A few math builtins.
|
||||
for (let type of ['f32', 'f64']) {
|
||||
for (let func of ['ceil', 'floor', 'nearest', 'trunc']) {
|
||||
test(`(module
|
||||
(func (export "") (param ${type}) (result ${type})
|
||||
get_local 0
|
||||
${type}.${func}
|
||||
)
|
||||
)`,
|
||||
this,
|
||||
["", ">", "0,>", "<,0,>", `${type}.${func},0,>`, "<,0,>", "0,>", ">", ""]);
|
||||
}
|
||||
}
|
||||
|
||||
function testError(code, error, expect)
|
||||
{
|
||||
enableGeckoProfiling();
|
||||
|
|
|
@ -672,8 +672,9 @@ BacktrackingAllocator::buildLivenessInfo()
|
|||
// use and temp are fixed registers, as they can't alias.
|
||||
if (ins->isCall() && use->usedAtStart()) {
|
||||
for (size_t i = 0; i < ins->numTemps(); i++) {
|
||||
MOZ_ASSERT(vreg(ins->getTemp(i)).type() != vreg(use).type() ||
|
||||
(use->isFixedRegister() && ins->getTemp(i)->isFixed()));
|
||||
MOZ_ASSERT_IF(!ins->getTemp(i)->isBogusTemp(),
|
||||
vreg(ins->getTemp(i)).type() != vreg(use).type() ||
|
||||
(use->isFixedRegister() && ins->getTemp(i)->isFixed()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1843,6 +1843,7 @@ BaselineCacheIRCompiler::init(CacheKind kind)
|
|||
case CacheKind::GetElem:
|
||||
case CacheKind::SetProp:
|
||||
case CacheKind::In:
|
||||
case CacheKind::HasOwn:
|
||||
MOZ_ASSERT(numInputs == 2);
|
||||
allocator.initInputLocation(0, R0);
|
||||
allocator.initInputLocation(1, R1);
|
||||
|
@ -1898,6 +1899,7 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
|
|||
CacheIRStubKind stubKind;
|
||||
switch (kind) {
|
||||
case CacheKind::In:
|
||||
case CacheKind::HasOwn:
|
||||
stubDataOffset = sizeof(ICCacheIR_Regular);
|
||||
stubKind = CacheIRStubKind::Regular;
|
||||
break;
|
||||
|
|
|
@ -2352,6 +2352,19 @@ BaselineCompiler::emit_JSOP_IN()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_HASOWN()
|
||||
{
|
||||
frame.popRegsAndSync(2);
|
||||
|
||||
ICHasOwn_Fallback::Compiler stubCompiler(cx);
|
||||
if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
|
||||
return false;
|
||||
|
||||
frame.push(R0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_GETGNAME()
|
||||
{
|
||||
|
|
|
@ -123,6 +123,7 @@ namespace jit {
|
|||
_(JSOP_DELELEM) \
|
||||
_(JSOP_STRICTDELELEM) \
|
||||
_(JSOP_IN) \
|
||||
_(JSOP_HASOWN) \
|
||||
_(JSOP_GETGNAME) \
|
||||
_(JSOP_BINDGNAME) \
|
||||
_(JSOP_SETGNAME) \
|
||||
|
|
|
@ -1258,6 +1258,77 @@ ICIn_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
|||
return tailCallVM(DoInFallbackInfo, masm);
|
||||
}
|
||||
|
||||
//
|
||||
// HasOwn_Fallback
|
||||
//
|
||||
|
||||
static bool
|
||||
DoHasOwnFallback(JSContext* cx, BaselineFrame* frame, ICHasOwn_Fallback* stub_,
|
||||
HandleValue keyValue, HandleValue objValue, MutableHandleValue res)
|
||||
{
|
||||
// This fallback stub may trigger debug mode toggling.
|
||||
DebugModeOSRVolatileStub<ICIn_Fallback*> stub(frame, stub_);
|
||||
|
||||
FallbackICSpew(cx, stub, "HasOwn");
|
||||
|
||||
if (stub->state().maybeTransition())
|
||||
stub->discardStubs(cx);
|
||||
|
||||
if (stub->state().canAttachStub()) {
|
||||
RootedScript script(cx, frame->script());
|
||||
jsbytecode* pc = stub->icEntry()->pc(script);
|
||||
|
||||
ICStubEngine engine = ICStubEngine::Baseline;
|
||||
HasOwnIRGenerator gen(cx, script, pc, stub->state().mode(), keyValue, objValue);
|
||||
bool attached = false;
|
||||
if (gen.tryAttachStub()) {
|
||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||
engine, script, stub, &attached);
|
||||
if (newStub)
|
||||
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
||||
}
|
||||
if (!attached)
|
||||
stub->state().trackNotAttached();
|
||||
}
|
||||
|
||||
bool found;
|
||||
if (!HasOwnProperty(cx, objValue, keyValue, &found))
|
||||
return false;
|
||||
|
||||
res.setBoolean(found);
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*DoHasOwnFallbackFn)(JSContext*, BaselineFrame*, ICHasOwn_Fallback*, HandleValue,
|
||||
HandleValue, MutableHandleValue);
|
||||
static const VMFunction DoHasOwnFallbackInfo =
|
||||
FunctionInfo<DoHasOwnFallbackFn>(DoHasOwnFallback, "DoHasOwnFallback", TailCall, PopValues(2));
|
||||
|
||||
bool
|
||||
ICHasOwn_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
{
|
||||
MOZ_ASSERT(engine_ == Engine::Baseline);
|
||||
|
||||
EmitRestoreTailCallReg(masm);
|
||||
|
||||
// Sync for the decompiler.
|
||||
masm.pushValue(R0);
|
||||
masm.pushValue(R1);
|
||||
|
||||
// Push arguments.
|
||||
masm.pushValue(R1);
|
||||
masm.pushValue(R0);
|
||||
masm.push(ICStubReg);
|
||||
pushStubPayload(masm, R0.scratchReg());
|
||||
|
||||
return tailCallVM(DoHasOwnFallbackInfo, masm);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// GetName_Fallback
|
||||
//
|
||||
|
||||
static bool
|
||||
DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_,
|
||||
HandleObject envChain, MutableHandleValue res)
|
||||
|
|
|
@ -473,6 +473,31 @@ class ICIn_Fallback : public ICFallbackStub
|
|||
};
|
||||
};
|
||||
|
||||
// HasOwn
|
||||
// JSOP_HASOWN
|
||||
class ICHasOwn_Fallback : public ICFallbackStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
explicit ICHasOwn_Fallback(JitCode* stubCode)
|
||||
: ICFallbackStub(ICStub::HasOwn_Fallback, stubCode)
|
||||
{ }
|
||||
|
||||
public:
|
||||
class Compiler : public ICStubCompiler {
|
||||
protected:
|
||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
||||
|
||||
public:
|
||||
explicit Compiler(JSContext* cx)
|
||||
: ICStubCompiler(cx, ICStub::HasOwn_Fallback, Engine::Baseline)
|
||||
{ }
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
return newStub<ICHasOwn_Fallback>(space, getStubCode());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// GetName
|
||||
// JSOP_GETNAME
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace jit {
|
|||
_(SetElem_Fallback) \
|
||||
\
|
||||
_(In_Fallback) \
|
||||
_(HasOwn_Fallback) \
|
||||
\
|
||||
_(GetName_Fallback) \
|
||||
\
|
||||
|
|
|
@ -2020,6 +2020,135 @@ InIRGenerator::trackNotAttached()
|
|||
#endif
|
||||
}
|
||||
|
||||
HasOwnIRGenerator::HasOwnIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
|
||||
ICState::Mode mode, HandleValue key, HandleValue value)
|
||||
: IRGenerator(cx, script, pc, CacheKind::HasOwn, mode),
|
||||
key_(key), val_(value)
|
||||
{ }
|
||||
|
||||
|
||||
bool
|
||||
HasOwnIRGenerator::tryAttachNativeHasOwn(HandleId key, ValOperandId keyId,
|
||||
HandleObject obj, ObjOperandId objId)
|
||||
{
|
||||
PropertyResult prop;
|
||||
if (!LookupOwnPropertyPure(cx_, obj, key, &prop))
|
||||
return false;
|
||||
|
||||
if (!prop.isNativeProperty())
|
||||
return false;
|
||||
|
||||
if (mode_ == ICState::Mode::Megamorphic) {
|
||||
writer.megamorphicHasOwnResult(objId, keyId);
|
||||
writer.returnFromIC();
|
||||
trackAttached("MegamorphicHasOwn");
|
||||
return true;
|
||||
}
|
||||
|
||||
Maybe<ObjOperandId> holderId;
|
||||
emitIdGuard(keyId, key);
|
||||
EmitReadSlotGuard(writer, obj, obj, prop.shape(), objId, &holderId);
|
||||
writer.loadBooleanResult(true);
|
||||
writer.returnFromIC();
|
||||
|
||||
trackAttached("NativeHasOwn");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
HasOwnIRGenerator::tryAttachNativeHasOwnDoesNotExist(HandleId key, ValOperandId keyId,
|
||||
HandleObject obj, ObjOperandId objId)
|
||||
{
|
||||
if (!CheckHasNoSuchOwnProperty(cx_, obj, key))
|
||||
return false;
|
||||
|
||||
if (mode_ == ICState::Mode::Megamorphic) {
|
||||
writer.megamorphicHasOwnResult(objId, keyId);
|
||||
writer.returnFromIC();
|
||||
trackAttached("MegamorphicHasOwn");
|
||||
return true;
|
||||
}
|
||||
|
||||
Maybe<ObjOperandId> expandoId;
|
||||
emitIdGuard(keyId, key);
|
||||
TestMatchingReceiver(writer, obj, nullptr, objId, &expandoId);
|
||||
writer.loadBooleanResult(false);
|
||||
writer.returnFromIC();
|
||||
|
||||
trackAttached("NativeHasOwnDoesNotExist");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
HasOwnIRGenerator::tryAttachStub()
|
||||
{
|
||||
MOZ_ASSERT(cacheKind_ == CacheKind::HasOwn);
|
||||
|
||||
AutoAssertNoPendingException aanpe(cx_);
|
||||
|
||||
ValOperandId keyId(writer.setInputOperandId(0));
|
||||
ValOperandId valId(writer.setInputOperandId(1));
|
||||
|
||||
if (!val_.isObject()) {
|
||||
trackNotAttached();
|
||||
return false;
|
||||
}
|
||||
RootedObject obj(cx_, &val_.toObject());
|
||||
|
||||
ObjOperandId objId = writer.guardIsObject(valId);
|
||||
|
||||
RootedId id(cx_);
|
||||
bool nameOrSymbol;
|
||||
if (!ValueToNameOrSymbolId(cx_, key_, &id, &nameOrSymbol)) {
|
||||
cx_->clearPendingException();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nameOrSymbol) {
|
||||
if (tryAttachNativeHasOwn(id, keyId, obj, objId))
|
||||
return true;
|
||||
if (tryAttachNativeHasOwnDoesNotExist(id, keyId, obj, objId))
|
||||
return true;
|
||||
|
||||
trackNotAttached();
|
||||
return false;
|
||||
}
|
||||
|
||||
trackNotAttached();
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
HasOwnIRGenerator::trackAttached(const char* name)
|
||||
{
|
||||
#ifdef JS_JITSPEW
|
||||
CacheIRSpewer& sp = GetCacheIRSpewerSingleton();
|
||||
if (sp.enabled()) {
|
||||
LockGuard<Mutex> guard(sp.lock());
|
||||
sp.beginCache(guard, *this);
|
||||
sp.valueProperty(guard, "base", val_);
|
||||
sp.valueProperty(guard, "property", key_);
|
||||
sp.attached(guard, name);
|
||||
sp.endCache(guard);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
HasOwnIRGenerator::trackNotAttached()
|
||||
{
|
||||
#ifdef JS_JITSPEW
|
||||
CacheIRSpewer& sp = GetCacheIRSpewerSingleton();
|
||||
if (sp.enabled()) {
|
||||
LockGuard<Mutex> guard(sp.lock());
|
||||
sp.beginCache(guard, *this);
|
||||
sp.valueProperty(guard, "base", val_);
|
||||
sp.valueProperty(guard, "property", key_);
|
||||
sp.endCache(guard);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
IRGenerator::maybeGuardInt32Index(const Value& index, ValOperandId indexId,
|
||||
uint32_t* int32Index, Int32OperandId* int32IndexId)
|
||||
|
|
|
@ -139,7 +139,8 @@ class TypedOperandId : public OperandId
|
|||
_(GetName) \
|
||||
_(SetProp) \
|
||||
_(SetElem) \
|
||||
_(In)
|
||||
_(In) \
|
||||
_(HasOwn)
|
||||
|
||||
enum class CacheKind : uint8_t
|
||||
{
|
||||
|
@ -185,6 +186,7 @@ extern const char* CacheKindNames[];
|
|||
_(MegamorphicLoadSlotResult) \
|
||||
_(MegamorphicLoadSlotByValueResult) \
|
||||
_(MegamorphicStoreSlot) \
|
||||
_(MegamorphicHasOwnResult) \
|
||||
\
|
||||
/* See CacheIR.cpp 'DOM proxies' comment. */ \
|
||||
_(LoadDOMExpandoValue) \
|
||||
|
@ -780,6 +782,10 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
|
|||
writeOperandId(rhs);
|
||||
buffer_.writeByte(needsTypeBarrier);
|
||||
}
|
||||
void megamorphicHasOwnResult(ObjOperandId obj, ValOperandId id) {
|
||||
writeOpWithOperandId(CacheOp::MegamorphicHasOwnResult, obj);
|
||||
writeOperandId(id);
|
||||
}
|
||||
|
||||
void loadBooleanResult(bool val) {
|
||||
writeOp(CacheOp::LoadBooleanResult);
|
||||
|
@ -1253,6 +1259,27 @@ class MOZ_RAII InIRGenerator : public IRGenerator
|
|||
bool tryAttachStub();
|
||||
};
|
||||
|
||||
// HasOwnIRGenerator generates CacheIR for a HasOwn IC.
|
||||
class MOZ_RAII HasOwnIRGenerator : public IRGenerator
|
||||
{
|
||||
HandleValue key_;
|
||||
HandleValue val_;
|
||||
|
||||
bool tryAttachNativeHasOwn(HandleId key, ValOperandId keyId,
|
||||
HandleObject obj, ObjOperandId objId);
|
||||
bool tryAttachNativeHasOwnDoesNotExist(HandleId key, ValOperandId keyId,
|
||||
HandleObject obj, ObjOperandId objId);
|
||||
|
||||
void trackAttached(const char* name);
|
||||
void trackNotAttached();
|
||||
|
||||
public:
|
||||
HasOwnIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode mode, HandleValue key,
|
||||
HandleValue value);
|
||||
|
||||
bool tryAttachStub();
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -1626,10 +1626,12 @@ CacheIRCompiler::emitLoadBooleanResult()
|
|||
if (output.hasValue()) {
|
||||
Value val = BooleanValue(reader.readBool());
|
||||
masm.moveValue(val, output.valueReg());
|
||||
} else {
|
||||
MOZ_ASSERT(output.type() == JSVAL_TYPE_BOOLEAN);
|
||||
bool b = reader.readBool();
|
||||
masm.movePtr(ImmWord(b), output.typedReg().gpr());
|
||||
}
|
||||
else {
|
||||
MOZ_CRASH("NYI: Typed LoadBooleanResult");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2279,3 +2281,51 @@ CacheIRCompiler::emitMegamorphicLoadSlotByValueResult()
|
|||
masm.adjustStack(sizeof(Value));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CacheIRCompiler::emitMegamorphicHasOwnResult()
|
||||
{
|
||||
AutoOutputRegister output(*this);
|
||||
|
||||
Register obj = allocator.useRegister(masm, reader.objOperandId());
|
||||
ValueOperand idVal = allocator.useValueRegister(masm, reader.valOperandId());
|
||||
|
||||
AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
|
||||
|
||||
FailurePath* failure;
|
||||
if (!addFailurePath(&failure))
|
||||
return false;
|
||||
|
||||
// idVal will be in vp[0], result will be stored in vp[1].
|
||||
masm.reserveStack(sizeof(Value));
|
||||
masm.Push(idVal);
|
||||
masm.moveStackPtrTo(idVal.scratchReg());
|
||||
|
||||
LiveRegisterSet volatileRegs(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
|
||||
volatileRegs.takeUnchecked(scratch);
|
||||
volatileRegs.takeUnchecked(idVal);
|
||||
masm.PushRegsInMask(volatileRegs);
|
||||
|
||||
masm.setupUnalignedABICall(scratch);
|
||||
masm.loadJSContext(scratch);
|
||||
masm.passABIArg(scratch);
|
||||
masm.passABIArg(obj);
|
||||
masm.passABIArg(idVal.scratchReg());
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, HasOwnNativeDataProperty));
|
||||
masm.mov(ReturnReg, scratch);
|
||||
masm.PopRegsInMask(volatileRegs);
|
||||
|
||||
masm.Pop(idVal);
|
||||
|
||||
Label ok;
|
||||
uint32_t framePushed = masm.framePushed();
|
||||
masm.branchIfTrueBool(scratch, &ok);
|
||||
masm.adjustStack(sizeof(Value));
|
||||
masm.jump(failure->label());
|
||||
|
||||
masm.bind(&ok);
|
||||
masm.setFramePushed(framePushed);
|
||||
masm.loadTypedOrValue(Address(masm.getStackPointer(), 0), output);
|
||||
masm.adjustStack(sizeof(Value));
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace jit {
|
|||
_(LoadUnboxedArrayElementResult) \
|
||||
_(LoadTypedElementResult) \
|
||||
_(MegamorphicLoadSlotByValueResult) \
|
||||
_(MegamorphicHasOwnResult) \
|
||||
_(WrapResult)
|
||||
|
||||
// Represents a Value on the Baseline frame's expression stack. Slot 0 is the
|
||||
|
|
|
@ -236,6 +236,11 @@ typedef bool (*IonGetNameICFn)(JSContext*, HandleScript, IonGetNameIC*, HandleOb
|
|||
static const VMFunction IonGetNameICInfo =
|
||||
FunctionInfo<IonGetNameICFn>(IonGetNameIC::update, "IonGetNameIC::update");
|
||||
|
||||
typedef bool (*IonHasOwnICFn)(JSContext*, HandleScript, IonHasOwnIC*, HandleValue, HandleValue,
|
||||
int32_t*);
|
||||
static const VMFunction IonHasOwnICInfo =
|
||||
FunctionInfo<IonHasOwnICFn>(IonHasOwnIC::update, "IonHasOwnIC::update");
|
||||
|
||||
void
|
||||
CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool)
|
||||
{
|
||||
|
@ -306,6 +311,24 @@ CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool)
|
|||
}
|
||||
case CacheKind::In:
|
||||
MOZ_CRASH("Baseline-specific for now");
|
||||
case CacheKind::HasOwn: {
|
||||
IonHasOwnIC* hasOwnIC = ic->asHasOwnIC();
|
||||
|
||||
saveLive(lir);
|
||||
|
||||
pushArg(hasOwnIC->id());
|
||||
pushArg(hasOwnIC->value());
|
||||
icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
|
||||
pushArg(ImmGCPtr(gen->info().script()));
|
||||
|
||||
callVM(IonHasOwnICInfo, lir);
|
||||
|
||||
StoreRegisterTo(hasOwnIC->output()).generate(this);
|
||||
restoreLiveIgnore(lir, StoreRegisterTo(hasOwnIC->output()).clobbered());
|
||||
|
||||
masm.jump(ool->rejoin());
|
||||
return;
|
||||
}
|
||||
}
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
@ -7010,18 +7033,21 @@ CodeGenerator::visitModD(LModD* ins)
|
|||
{
|
||||
FloatRegister lhs = ToFloatRegister(ins->lhs());
|
||||
FloatRegister rhs = ToFloatRegister(ins->rhs());
|
||||
Register temp = ToRegister(ins->temp());
|
||||
|
||||
MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
|
||||
MOZ_ASSERT(ins->temp()->isBogusTemp() == gen->compilingWasm());
|
||||
|
||||
masm.setupUnalignedABICall(temp);
|
||||
masm.passABIArg(lhs, MoveOp::DOUBLE);
|
||||
masm.passABIArg(rhs, MoveOp::DOUBLE);
|
||||
|
||||
if (gen->compilingWasm())
|
||||
masm.callWithABI(wasm::SymbolicAddress::ModD, MoveOp::DOUBLE);
|
||||
else
|
||||
if (gen->compilingWasm()) {
|
||||
masm.setupWasmABICall();
|
||||
masm.passABIArg(lhs, MoveOp::DOUBLE);
|
||||
masm.passABIArg(rhs, MoveOp::DOUBLE);
|
||||
masm.callWithABI(ins->mir()->bytecodeOffset(), wasm::SymbolicAddress::ModD, MoveOp::DOUBLE);
|
||||
} else {
|
||||
masm.setupUnalignedABICall(ToRegister(ins->temp()));
|
||||
masm.passABIArg(lhs, MoveOp::DOUBLE);
|
||||
masm.passABIArg(rhs, MoveOp::DOUBLE);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, NumberMod), MoveOp::DOUBLE);
|
||||
}
|
||||
}
|
||||
|
||||
typedef bool (*BinaryFn)(JSContext*, MutableHandleValue, MutableHandleValue, MutableHandleValue);
|
||||
|
@ -9537,7 +9563,7 @@ CodeGenerator::visitRest(LRest* lir)
|
|||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::generateWasm(wasm::SigIdDesc sigId, wasm::TrapOffset trapOffset,
|
||||
CodeGenerator::generateWasm(wasm::SigIdDesc sigId, wasm::BytecodeOffset trapOffset,
|
||||
wasm::FuncOffsets* offsets)
|
||||
{
|
||||
JitSpew(JitSpew_Codegen, "# Emitting wasm code");
|
||||
|
@ -10413,6 +10439,20 @@ CodeGenerator::visitBindNameIC(OutOfLineUpdateCache* ool, DataPtr<BindNameIC>& i
|
|||
masm.jump(ool->rejoin());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitHasOwnCache(LHasOwnCache* ins)
|
||||
{
|
||||
LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
|
||||
TypedOrValueRegister value =
|
||||
toConstantOrRegister(ins, LHasOwnCache::Value, ins->mir()->value()->type()).reg();
|
||||
TypedOrValueRegister id =
|
||||
toConstantOrRegister(ins, LHasOwnCache::Id, ins->mir()->idval()->type()).reg();
|
||||
Register output = ToRegister(ins->output());
|
||||
|
||||
IonHasOwnIC cache(liveRegs, value, id, output);
|
||||
addIC(ins, allocateIC(cache));
|
||||
}
|
||||
|
||||
typedef bool (*SetPropertyFn)(JSContext*, HandleObject,
|
||||
HandlePropertyName, const HandleValue, bool, jsbytecode*);
|
||||
static const VMFunction SetPropertyInfo = FunctionInfo<SetPropertyFn>(SetProperty, "SetProperty");
|
||||
|
|
|
@ -68,8 +68,8 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
|||
|
||||
public:
|
||||
MOZ_MUST_USE bool generate();
|
||||
MOZ_MUST_USE bool generateWasm(wasm::SigIdDesc sigId, wasm::TrapOffset trapOffset,
|
||||
wasm::FuncOffsets *offsets);
|
||||
MOZ_MUST_USE bool generateWasm(wasm::SigIdDesc sigId, wasm::BytecodeOffset trapOffset,
|
||||
wasm::FuncOffsets* offsets);
|
||||
MOZ_MUST_USE bool link(JSContext* cx, CompilerConstraintList* constraints);
|
||||
MOZ_MUST_USE bool linkSharedStubs(JSContext* cx);
|
||||
|
||||
|
@ -422,6 +422,7 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
|||
void visitCallSetProperty(LInstruction* ins);
|
||||
void visitSetPropertyCache(LSetPropertyCache* ins);
|
||||
void visitGetNameCache(LGetNameCache* ins);
|
||||
void visitHasOwnCache(LHasOwnCache* ins);
|
||||
|
||||
void visitBindNameIC(OutOfLineUpdateCache* ool, DataPtr<BindNameIC>& ic);
|
||||
|
||||
|
|
|
@ -477,10 +477,8 @@ jit::FinishOffThreadBuilder(JSRuntime* runtime, IonBuilder* builder,
|
|||
}
|
||||
|
||||
// If the builder is still in one of the helper thread list, then remove it.
|
||||
if (builder->isInList()) {
|
||||
MOZ_ASSERT(runtime);
|
||||
runtime->ionLazyLinkListRemove(builder);
|
||||
}
|
||||
if (builder->isInList())
|
||||
builder->script()->zone()->group()->ionLazyLinkListRemove(builder);
|
||||
|
||||
// Clear the recompiling flag of the old ionScript, since we continue to
|
||||
// use the old ionScript if recompiling fails.
|
||||
|
@ -550,7 +548,7 @@ jit::LinkIonScript(JSContext* cx, HandleScript calleeScript)
|
|||
calleeScript->baselineScript()->removePendingIonBuilder(calleeScript);
|
||||
|
||||
// Remove from pending.
|
||||
cx->runtime()->ionLazyLinkListRemove(builder);
|
||||
cx->zone()->group()->ionLazyLinkListRemove(builder);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -2097,12 +2095,12 @@ AttachFinishedCompilations(JSContext* cx)
|
|||
JSScript* script = builder->script();
|
||||
MOZ_ASSERT(script->hasBaselineScript());
|
||||
script->baselineScript()->setPendingIonBuilder(cx->runtime(), script, builder);
|
||||
cx->runtime()->ionLazyLinkListAdd(builder);
|
||||
cx->zone()->group()->ionLazyLinkListAdd(builder);
|
||||
|
||||
// Don't keep more than 100 lazy link builders.
|
||||
// Don't keep more than 100 lazy link builders in a zone group.
|
||||
// Link the oldest ones immediately.
|
||||
while (cx->runtime()->ionLazyLinkListSize() > 100) {
|
||||
jit::IonBuilder* builder = cx->runtime()->ionLazyLinkList().getLast();
|
||||
while (cx->zone()->group()->ionLazyLinkListSize() > 100) {
|
||||
jit::IonBuilder* builder = cx->zone()->group()->ionLazyLinkList().getLast();
|
||||
RootedScript script(cx, builder->script());
|
||||
|
||||
AutoUnlockHelperThreadState unlock(lock);
|
||||
|
|
|
@ -658,6 +658,7 @@ IonBuilder::analyzeNewLoopTypes(const CFGBlock* loopEntryBlock)
|
|||
case JSOP_STRICTNE:
|
||||
case JSOP_IN:
|
||||
case JSOP_INSTANCEOF:
|
||||
case JSOP_HASOWN:
|
||||
type = MIRType::Boolean;
|
||||
break;
|
||||
case JSOP_DOUBLE:
|
||||
|
@ -2268,6 +2269,9 @@ IonBuilder::inspectOpcode(JSOp op)
|
|||
case JSOP_IN:
|
||||
return jsop_in();
|
||||
|
||||
case JSOP_HASOWN:
|
||||
return jsop_hasown();
|
||||
|
||||
case JSOP_SETRVAL:
|
||||
MOZ_ASSERT(!script()->noScriptRval());
|
||||
current->setSlot(info().returnValueSlot(), current->pop());
|
||||
|
@ -6978,7 +6982,7 @@ IonBuilder::testSingletonPropertyTypes(MDefinition* obj, jsid id)
|
|||
}
|
||||
|
||||
AbortReasonOr<bool>
|
||||
IonBuilder::testNotDefinedProperty(MDefinition* obj, jsid id)
|
||||
IonBuilder::testNotDefinedProperty(MDefinition* obj, jsid id, bool ownProperty /* = false */)
|
||||
{
|
||||
TemporaryTypeSet* types = obj->resultTypeSet();
|
||||
if (!types || types->unknownObject() || types->getKnownMIRType() != MIRType::Object)
|
||||
|
@ -7014,6 +7018,10 @@ IonBuilder::testNotDefinedProperty(MDefinition* obj, jsid id)
|
|||
if (property.isOwnProperty(constraints()))
|
||||
return false;
|
||||
|
||||
// If we only care about own properties don't check the proto.
|
||||
if (ownProperty)
|
||||
break;
|
||||
|
||||
JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull());
|
||||
if (!proto)
|
||||
break;
|
||||
|
@ -12600,7 +12608,7 @@ IonBuilder::jsop_in()
|
|||
if (emitted)
|
||||
return Ok();
|
||||
|
||||
MOZ_TRY(inTryFold(&emitted, obj, id));
|
||||
MOZ_TRY(hasTryNotDefined(&emitted, obj, id, /* ownProperty = */ false));
|
||||
if (emitted)
|
||||
return Ok();
|
||||
|
||||
|
@ -12664,7 +12672,7 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id)
|
|||
}
|
||||
|
||||
AbortReasonOr<Ok>
|
||||
IonBuilder::inTryFold(bool* emitted, MDefinition* obj, MDefinition* id)
|
||||
IonBuilder::hasTryNotDefined(bool* emitted, MDefinition* obj, MDefinition* id, bool ownProperty)
|
||||
{
|
||||
// Fold |id in obj| to |false|, if we know the object (or an object on its
|
||||
// prototype chain) does not have this property.
|
||||
|
@ -12680,7 +12688,7 @@ IonBuilder::inTryFold(bool* emitted, MDefinition* obj, MDefinition* id)
|
|||
return Ok();
|
||||
|
||||
bool res;
|
||||
MOZ_TRY_VAR(res, testNotDefinedProperty(obj, propId));
|
||||
MOZ_TRY_VAR(res, testNotDefinedProperty(obj, propId, ownProperty));
|
||||
if (!res)
|
||||
return Ok();
|
||||
|
||||
|
@ -12692,6 +12700,27 @@ IonBuilder::inTryFold(bool* emitted, MDefinition* obj, MDefinition* id)
|
|||
return Ok();
|
||||
}
|
||||
|
||||
AbortReasonOr<Ok>
|
||||
IonBuilder::jsop_hasown()
|
||||
{
|
||||
MDefinition* obj = convertUnboxedObjects(current->pop());
|
||||
MDefinition* id = current->pop();
|
||||
|
||||
if (!forceInlineCaches()) {
|
||||
bool emitted = false;
|
||||
MOZ_TRY(hasTryNotDefined(&emitted, obj, id, /* ownProperty = */ true));
|
||||
if (emitted)
|
||||
return Ok();
|
||||
}
|
||||
|
||||
MHasOwnCache* ins = MHasOwnCache::New(alloc(), obj, id);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
MOZ_TRY(resumeAfter(ins));
|
||||
return Ok();
|
||||
}
|
||||
|
||||
AbortReasonOr<bool>
|
||||
IonBuilder::hasOnProtoChain(TypeSet::ObjectKey* key, JSObject* protoObject, bool* onProto)
|
||||
{
|
||||
|
|
|
@ -335,9 +335,9 @@ class IonBuilder
|
|||
AbortReasonOr<Ok> newObjectTryTemplateObject(bool* emitted, JSObject* templateObject);
|
||||
AbortReasonOr<Ok> newObjectTryVM(bool* emitted, JSObject* templateObject);
|
||||
|
||||
// jsop_in helpers.
|
||||
// jsop_in/jsop_hasown helpers.
|
||||
AbortReasonOr<Ok> inTryDense(bool* emitted, MDefinition* obj, MDefinition* id);
|
||||
AbortReasonOr<Ok> inTryFold(bool* emitted, MDefinition* obj, MDefinition* id);
|
||||
AbortReasonOr<Ok> hasTryNotDefined(bool* emitted, MDefinition* obj, MDefinition* id, bool ownProperty);
|
||||
|
||||
// binary data lookup helpers.
|
||||
TypedObjectPrediction typedObjectPrediction(MDefinition* typedObj);
|
||||
|
@ -578,6 +578,7 @@ class IonBuilder
|
|||
AbortReasonOr<Ok> jsop_isnoiter();
|
||||
AbortReasonOr<Ok> jsop_iterend();
|
||||
AbortReasonOr<Ok> jsop_in();
|
||||
AbortReasonOr<Ok> jsop_hasown();
|
||||
AbortReasonOr<Ok> jsop_instanceof();
|
||||
AbortReasonOr<Ok> jsop_getaliasedvar(EnvironmentCoordinate ec);
|
||||
AbortReasonOr<Ok> jsop_setaliasedvar(EnvironmentCoordinate ec);
|
||||
|
@ -856,7 +857,7 @@ class IonBuilder
|
|||
JSObject* testSingletonProperty(JSObject* obj, jsid id);
|
||||
JSObject* testSingletonPropertyTypes(MDefinition* obj, jsid id);
|
||||
|
||||
AbortReasonOr<bool> testNotDefinedProperty(MDefinition* obj, jsid id);
|
||||
AbortReasonOr<bool> testNotDefinedProperty(MDefinition* obj, jsid id, bool ownProperty = false);
|
||||
|
||||
uint32_t getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed);
|
||||
MDefinition* convertUnboxedObjects(MDefinition* obj);
|
||||
|
|
|
@ -428,6 +428,20 @@ IonCacheIRCompiler::init()
|
|||
}
|
||||
case CacheKind::In:
|
||||
MOZ_CRASH("Invalid cache");
|
||||
case CacheKind::HasOwn: {
|
||||
IonHasOwnIC* ic = ic_->asHasOwnIC();
|
||||
Register output = ic->output();
|
||||
|
||||
available.add(output);
|
||||
|
||||
liveRegs_.emplace(ic->liveRegs());
|
||||
outputUnchecked_.emplace(TypedOrValueRegister(MIRType::Boolean, AnyRegister(output)));
|
||||
|
||||
MOZ_ASSERT(numInputs == 2);
|
||||
allocator.initInputLocation(0, ic->id());
|
||||
allocator.initInputLocation(1, ic->value());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (liveRegs_)
|
||||
|
|
|
@ -48,6 +48,8 @@ IonIC::scratchRegisterForEntryJump()
|
|||
return asGetNameIC()->temp();
|
||||
case CacheKind::In:
|
||||
MOZ_CRASH("Baseline-specific for now");
|
||||
case CacheKind::HasOwn:
|
||||
return asHasOwnIC()->output();
|
||||
}
|
||||
|
||||
MOZ_CRASH("Invalid kind");
|
||||
|
@ -321,6 +323,36 @@ IonGetNameIC::update(JSContext* cx, HandleScript outerScript, IonGetNameIC* ic,
|
|||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
IonHasOwnIC::update(JSContext* cx, HandleScript outerScript, IonHasOwnIC* ic,
|
||||
HandleValue val, HandleValue idVal, int32_t* res)
|
||||
{
|
||||
IonScript* ionScript = outerScript->ionScript();
|
||||
|
||||
if (ic->state().maybeTransition())
|
||||
ic->discardStubs(cx->zone());
|
||||
|
||||
jsbytecode* pc = ic->pc();
|
||||
|
||||
if (ic->state().canAttachStub()) {
|
||||
bool attached = false;
|
||||
RootedScript script(cx, ic->script());
|
||||
HasOwnIRGenerator gen(cx, script, pc, ic->state().mode(), idVal, val);
|
||||
if (gen.tryAttachStub())
|
||||
ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
|
||||
|
||||
if (!attached)
|
||||
ic->state().trackNotAttached();
|
||||
}
|
||||
|
||||
bool found;
|
||||
if (!HasOwnProperty(cx, val, idVal, &found))
|
||||
return false;
|
||||
|
||||
*res = found;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
IonICStub::stubDataStart()
|
||||
{
|
||||
|
|
|
@ -59,6 +59,7 @@ class IonICStub
|
|||
class IonGetPropertyIC;
|
||||
class IonSetPropertyIC;
|
||||
class IonGetNameIC;
|
||||
class IonHasOwnIC;
|
||||
|
||||
class IonIC
|
||||
{
|
||||
|
@ -144,6 +145,10 @@ class IonIC
|
|||
MOZ_ASSERT(kind_ == CacheKind::GetName);
|
||||
return (IonGetNameIC*)this;
|
||||
}
|
||||
IonHasOwnIC* asHasOwnIC() {
|
||||
MOZ_ASSERT(kind_ == CacheKind::HasOwn);
|
||||
return (IonHasOwnIC*)this;
|
||||
}
|
||||
|
||||
void updateBaseAddress(JitCode* code, MacroAssembler& masm);
|
||||
|
||||
|
@ -275,6 +280,33 @@ class IonGetNameIC : public IonIC
|
|||
HandleObject envChain, MutableHandleValue res);
|
||||
};
|
||||
|
||||
class IonHasOwnIC : public IonIC
|
||||
{
|
||||
LiveRegisterSet liveRegs_;
|
||||
|
||||
TypedOrValueRegister value_;
|
||||
TypedOrValueRegister id_;
|
||||
Register output_;
|
||||
|
||||
public:
|
||||
IonHasOwnIC(LiveRegisterSet liveRegs, TypedOrValueRegister value,
|
||||
TypedOrValueRegister id, Register output)
|
||||
: IonIC(CacheKind::HasOwn),
|
||||
liveRegs_(liveRegs),
|
||||
value_(value),
|
||||
id_(id),
|
||||
output_(output)
|
||||
{ }
|
||||
|
||||
TypedOrValueRegister value() const { return value_; }
|
||||
TypedOrValueRegister id() const { return id_; }
|
||||
Register output() const { return output_; }
|
||||
LiveRegisterSet liveRegs() const { return liveRegs_; }
|
||||
|
||||
static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonHasOwnIC* ic,
|
||||
HandleValue val, HandleValue idVal, int32_t* res);
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -1918,9 +1918,18 @@ LIRGenerator::visitMod(MMod* ins)
|
|||
MOZ_ASSERT(ins->lhs()->type() == MIRType::Double);
|
||||
MOZ_ASSERT(ins->rhs()->type() == MIRType::Double);
|
||||
|
||||
gen->setPerformsCall();
|
||||
|
||||
// Ion does an unaligned ABI call and thus needs a temp register. Wasm
|
||||
// doesn't.
|
||||
LDefinition maybeTemp = gen->compilingWasm()
|
||||
? LDefinition::BogusTemp()
|
||||
: tempFixed(CallTempReg0);
|
||||
|
||||
// Note: useRegisterAtStart is safe here, the temp is not a FP register.
|
||||
LModD* lir = new(alloc()) LModD(useRegisterAtStart(ins->lhs()), useRegisterAtStart(ins->rhs()),
|
||||
tempFixed(CallTempReg0));
|
||||
LModD* lir = new(alloc()) LModD(useRegisterAtStart(ins->lhs()),
|
||||
useRegisterAtStart(ins->rhs()),
|
||||
maybeTemp);
|
||||
defineReturn(lir, ins);
|
||||
return;
|
||||
}
|
||||
|
@ -2236,8 +2245,7 @@ LIRGenerator::visitTruncateToInt32(MTruncateToInt32* truncate)
|
|||
MDefinition* opd = truncate->input();
|
||||
|
||||
switch (opd->type()) {
|
||||
case MIRType::Value:
|
||||
{
|
||||
case MIRType::Value: {
|
||||
LValueToInt32* lir = new(alloc()) LValueToInt32(useBox(opd), tempDouble(), temp(),
|
||||
LValueToInt32::TRUNCATE);
|
||||
assignSnapshot(lir, Bailout_NonPrimitiveInput);
|
||||
|
@ -2257,10 +2265,14 @@ LIRGenerator::visitTruncateToInt32(MTruncateToInt32* truncate)
|
|||
break;
|
||||
|
||||
case MIRType::Double:
|
||||
// May call into JS::ToInt32().
|
||||
gen->setPerformsCall();
|
||||
lowerTruncateDToInt32(truncate);
|
||||
break;
|
||||
|
||||
case MIRType::Float32:
|
||||
// May call into JS::ToInt32().
|
||||
gen->setPerformsCall();
|
||||
lowerTruncateFToInt32(truncate);
|
||||
break;
|
||||
|
||||
|
@ -4186,6 +4198,27 @@ LIRGenerator::visitIn(MIn* ins)
|
|||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitHasOwnCache(MHasOwnCache* ins)
|
||||
{
|
||||
MDefinition* value = ins->value();
|
||||
MOZ_ASSERT(value->type() == MIRType::Object || value->type() == MIRType::Value);
|
||||
|
||||
MDefinition* id = ins->idval();
|
||||
MOZ_ASSERT(id->type() == MIRType::String ||
|
||||
id->type() == MIRType::Symbol ||
|
||||
id->type() == MIRType::Int32 ||
|
||||
id->type() == MIRType::Value);
|
||||
|
||||
gen->setPerformsCall();
|
||||
|
||||
LHasOwnCache* lir =
|
||||
new(alloc()) LHasOwnCache(useBoxOrTyped(value),
|
||||
useBoxOrTyped(id));
|
||||
define(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitInstanceOf(MInstanceOf* ins)
|
||||
{
|
||||
|
|
|
@ -288,6 +288,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
void visitThrow(MThrow* ins);
|
||||
void visitIn(MIn* ins);
|
||||
void visitInArray(MInArray* ins);
|
||||
void visitHasOwnCache(MHasOwnCache* ins);
|
||||
void visitInstanceOf(MInstanceOf* ins);
|
||||
void visitCallInstanceOf(MCallInstanceOf* ins);
|
||||
void visitIsCallable(MIsCallable* ins);
|
||||
|
|
|
@ -1375,12 +1375,12 @@ MSimdGeneralShuffle::foldsTo(TempAllocator& alloc)
|
|||
|
||||
MInstruction*
|
||||
MSimdConvert::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* obj,
|
||||
MIRType toType, SimdSign sign, wasm::TrapOffset trapOffset)
|
||||
MIRType toType, SimdSign sign, wasm::BytecodeOffset bytecodeOffset)
|
||||
{
|
||||
MIRType fromType = obj->type();
|
||||
|
||||
if (SupportsUint32x4FloatConversions || sign != SimdSign::Unsigned) {
|
||||
MInstruction* ins = New(alloc, obj, toType, sign, trapOffset);
|
||||
MInstruction* ins = New(alloc, obj, toType, sign, bytecodeOffset);
|
||||
addTo->add(ins);
|
||||
return ins;
|
||||
}
|
||||
|
@ -1456,7 +1456,7 @@ MSimdConvert::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition
|
|||
if (fromType == MIRType::Float32x4 && toType == MIRType::Int32x4) {
|
||||
// The Float32x4 -> Uint32x4 conversion can throw if the input is out of
|
||||
// range. This is handled by the LFloat32x4ToUint32x4 expansion.
|
||||
MInstruction* ins = New(alloc, obj, toType, sign, trapOffset);
|
||||
MInstruction* ins = New(alloc, obj, toType, sign, bytecodeOffset);
|
||||
addTo->add(ins);
|
||||
return ins;
|
||||
}
|
||||
|
@ -5443,7 +5443,7 @@ InlinePropertyTable::trimTo(const ObjectVector& targets, const BoolVector& choic
|
|||
// If the target wasn't a function we would have veto'ed it
|
||||
// and it will not be in the entries list.
|
||||
if (!targets[i]->is<JSFunction>())
|
||||
continue;
|
||||
continue;
|
||||
|
||||
JSFunction* target = &targets[i]->as<JSFunction>();
|
||||
|
||||
|
|
162
js/src/jit/MIR.h
162
js/src/jit/MIR.h
|
@ -1855,10 +1855,11 @@ class MSimdConvert
|
|||
// as signed or unsigned. Note that we don't support int-int conversions -
|
||||
// use MSimdReinterpretCast for that.
|
||||
SimdSign sign_;
|
||||
wasm::TrapOffset trapOffset_;
|
||||
wasm::BytecodeOffset bytecodeOffset_;
|
||||
|
||||
MSimdConvert(MDefinition* obj, MIRType toType, SimdSign sign, wasm::TrapOffset trapOffset)
|
||||
: MUnaryInstruction(obj), sign_(sign), trapOffset_(trapOffset)
|
||||
MSimdConvert(MDefinition* obj, MIRType toType, SimdSign sign,
|
||||
wasm::BytecodeOffset bytecodeOffset)
|
||||
: MUnaryInstruction(obj), sign_(sign), bytecodeOffset_(bytecodeOffset)
|
||||
{
|
||||
MIRType fromType = obj->type();
|
||||
MOZ_ASSERT(IsSimdType(fromType));
|
||||
|
@ -1877,9 +1878,9 @@ class MSimdConvert
|
|||
}
|
||||
|
||||
static MSimdConvert* New(TempAllocator& alloc, MDefinition* obj, MIRType toType, SimdSign sign,
|
||||
wasm::TrapOffset trapOffset)
|
||||
wasm::BytecodeOffset bytecodeOffset)
|
||||
{
|
||||
return new (alloc) MSimdConvert(obj, toType, sign, trapOffset);
|
||||
return new (alloc) MSimdConvert(obj, toType, sign, bytecodeOffset);
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -1891,13 +1892,13 @@ class MSimdConvert
|
|||
// Return the inserted MInstruction that computes the converted value.
|
||||
static MInstruction* AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* obj,
|
||||
MIRType toType, SimdSign sign,
|
||||
wasm::TrapOffset trapOffset = wasm::TrapOffset());
|
||||
wasm::BytecodeOffset bytecodeOffset = wasm::BytecodeOffset());
|
||||
|
||||
SimdSign signedness() const {
|
||||
return sign_;
|
||||
}
|
||||
wasm::TrapOffset trapOffset() const {
|
||||
return trapOffset_;
|
||||
wasm::BytecodeOffset bytecodeOffset() const {
|
||||
return bytecodeOffset_;
|
||||
}
|
||||
|
||||
AliasSet getAliasSet() const override {
|
||||
|
@ -5466,12 +5467,12 @@ class MWasmTruncateToInt64
|
|||
public NoTypePolicy::Data
|
||||
{
|
||||
bool isUnsigned_;
|
||||
wasm::TrapOffset trapOffset_;
|
||||
wasm::BytecodeOffset bytecodeOffset_;
|
||||
|
||||
MWasmTruncateToInt64(MDefinition* def, bool isUnsigned, wasm::TrapOffset trapOffset)
|
||||
MWasmTruncateToInt64(MDefinition* def, bool isUnsigned, wasm::BytecodeOffset bytecodeOffset)
|
||||
: MUnaryInstruction(def),
|
||||
isUnsigned_(isUnsigned),
|
||||
trapOffset_(trapOffset)
|
||||
bytecodeOffset_(bytecodeOffset)
|
||||
{
|
||||
setResultType(MIRType::Int64);
|
||||
setGuard(); // neither removable nor movable because of possible side-effects.
|
||||
|
@ -5482,7 +5483,7 @@ class MWasmTruncateToInt64
|
|||
TRIVIAL_NEW_WRAPPERS
|
||||
|
||||
bool isUnsigned() const { return isUnsigned_; }
|
||||
wasm::TrapOffset trapOffset() const { return trapOffset_; }
|
||||
wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
|
||||
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
return congruentIfOperandsEqual(ins) &&
|
||||
|
@ -5500,10 +5501,11 @@ class MWasmTruncateToInt32
|
|||
public NoTypePolicy::Data
|
||||
{
|
||||
bool isUnsigned_;
|
||||
wasm::TrapOffset trapOffset_;
|
||||
wasm::BytecodeOffset bytecodeOffset_;
|
||||
|
||||
explicit MWasmTruncateToInt32(MDefinition* def, bool isUnsigned, wasm::TrapOffset trapOffset)
|
||||
: MUnaryInstruction(def), isUnsigned_(isUnsigned), trapOffset_(trapOffset)
|
||||
explicit MWasmTruncateToInt32(MDefinition* def, bool isUnsigned,
|
||||
wasm::BytecodeOffset bytecodeOffset)
|
||||
: MUnaryInstruction(def), isUnsigned_(isUnsigned), bytecodeOffset_(bytecodeOffset)
|
||||
{
|
||||
setResultType(MIRType::Int32);
|
||||
setGuard(); // neither removable nor movable because of possible side-effects.
|
||||
|
@ -5516,8 +5518,8 @@ class MWasmTruncateToInt32
|
|||
bool isUnsigned() const {
|
||||
return isUnsigned_;
|
||||
}
|
||||
wasm::TrapOffset trapOffset() const {
|
||||
return trapOffset_;
|
||||
wasm::BytecodeOffset bytecodeOffset() const {
|
||||
return bytecodeOffset_;
|
||||
}
|
||||
|
||||
MDefinition* foldsTo(TempAllocator& alloc) override;
|
||||
|
@ -5537,10 +5539,13 @@ class MInt64ToFloatingPoint
|
|||
public NoTypePolicy::Data
|
||||
{
|
||||
bool isUnsigned_;
|
||||
wasm::BytecodeOffset bytecodeOffset_;
|
||||
|
||||
MInt64ToFloatingPoint(MDefinition* def, MIRType type, bool isUnsigned)
|
||||
MInt64ToFloatingPoint(MDefinition* def, MIRType type, wasm::BytecodeOffset bytecodeOffset,
|
||||
bool isUnsigned)
|
||||
: MUnaryInstruction(def),
|
||||
isUnsigned_(isUnsigned)
|
||||
isUnsigned_(isUnsigned),
|
||||
bytecodeOffset_(bytecodeOffset)
|
||||
{
|
||||
MOZ_ASSERT(IsFloatingPointType(type));
|
||||
setResultType(type);
|
||||
|
@ -5552,6 +5557,7 @@ class MInt64ToFloatingPoint
|
|||
TRIVIAL_NEW_WRAPPERS
|
||||
|
||||
bool isUnsigned() const { return isUnsigned_; }
|
||||
wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
|
||||
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
if (!ins->isInt64ToFloatingPoint())
|
||||
|
@ -5635,8 +5641,12 @@ class MTruncateToInt32
|
|||
: public MUnaryInstruction,
|
||||
public ToInt32Policy::Data
|
||||
{
|
||||
explicit MTruncateToInt32(MDefinition* def)
|
||||
: MUnaryInstruction(def)
|
||||
wasm::BytecodeOffset bytecodeOffset_;
|
||||
|
||||
explicit MTruncateToInt32(MDefinition* def,
|
||||
wasm::BytecodeOffset bytecodeOffset = wasm::BytecodeOffset())
|
||||
: MUnaryInstruction(def),
|
||||
bytecodeOffset_(bytecodeOffset)
|
||||
{
|
||||
setResultType(MIRType::Int32);
|
||||
setMovable();
|
||||
|
@ -5673,6 +5683,8 @@ class MTruncateToInt32
|
|||
return input()->type() < MIRType::Symbol;
|
||||
}
|
||||
|
||||
wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
|
||||
|
||||
ALLOW_CLONE(MTruncateToInt32)
|
||||
};
|
||||
|
||||
|
@ -7062,7 +7074,7 @@ class MDiv : public MBinaryArithInstruction
|
|||
bool canBeNegativeDividend_;
|
||||
bool unsigned_; // If false, signedness will be derived from operands
|
||||
bool trapOnError_;
|
||||
wasm::TrapOffset trapOffset_;
|
||||
wasm::BytecodeOffset bytecodeOffset_;
|
||||
|
||||
MDiv(MDefinition* left, MDefinition* right, MIRType type)
|
||||
: MBinaryArithInstruction(left, right),
|
||||
|
@ -7088,13 +7100,13 @@ class MDiv : public MBinaryArithInstruction
|
|||
}
|
||||
static MDiv* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
|
||||
MIRType type, bool unsignd, bool trapOnError = false,
|
||||
wasm::TrapOffset trapOffset = wasm::TrapOffset(),
|
||||
wasm::BytecodeOffset bytecodeOffset = wasm::BytecodeOffset(),
|
||||
bool mustPreserveNaN = false)
|
||||
{
|
||||
auto* div = new(alloc) MDiv(left, right, type);
|
||||
div->unsigned_ = unsignd;
|
||||
div->trapOnError_ = trapOnError;
|
||||
div->trapOffset_ = trapOffset;
|
||||
div->bytecodeOffset_ = bytecodeOffset;
|
||||
if (trapOnError) {
|
||||
div->setGuard(); // not removable because of possible side-effects.
|
||||
div->setNotMovable();
|
||||
|
@ -7166,9 +7178,9 @@ class MDiv : public MBinaryArithInstruction
|
|||
bool trapOnError() const {
|
||||
return trapOnError_;
|
||||
}
|
||||
wasm::TrapOffset trapOffset() const {
|
||||
MOZ_ASSERT(trapOnError_);
|
||||
return trapOffset_;
|
||||
wasm::BytecodeOffset bytecodeOffset() const {
|
||||
MOZ_ASSERT(bytecodeOffset_.isValid());
|
||||
return bytecodeOffset_;
|
||||
}
|
||||
|
||||
bool isFloat32Commutative() const override { return true; }
|
||||
|
@ -7203,7 +7215,7 @@ class MMod : public MBinaryArithInstruction
|
|||
bool canBePowerOfTwoDivisor_;
|
||||
bool canBeDivideByZero_;
|
||||
bool trapOnError_;
|
||||
wasm::TrapOffset trapOffset_;
|
||||
wasm::BytecodeOffset bytecodeOffset_;
|
||||
|
||||
MMod(MDefinition* left, MDefinition* right, MIRType type)
|
||||
: MBinaryArithInstruction(left, right),
|
||||
|
@ -7225,12 +7237,12 @@ class MMod : public MBinaryArithInstruction
|
|||
}
|
||||
static MMod* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
|
||||
MIRType type, bool unsignd, bool trapOnError = false,
|
||||
wasm::TrapOffset trapOffset = wasm::TrapOffset())
|
||||
wasm::BytecodeOffset bytecodeOffset = wasm::BytecodeOffset())
|
||||
{
|
||||
auto* mod = new(alloc) MMod(left, right, type);
|
||||
mod->unsigned_ = unsignd;
|
||||
mod->trapOnError_ = trapOnError;
|
||||
mod->trapOffset_ = trapOffset;
|
||||
mod->bytecodeOffset_ = bytecodeOffset;
|
||||
if (trapOnError) {
|
||||
mod->setGuard(); // not removable because of possible side-effects.
|
||||
mod->setNotMovable();
|
||||
|
@ -7271,9 +7283,9 @@ class MMod : public MBinaryArithInstruction
|
|||
bool trapOnError() const {
|
||||
return trapOnError_;
|
||||
}
|
||||
wasm::TrapOffset trapOffset() const {
|
||||
MOZ_ASSERT(trapOnError_);
|
||||
return trapOffset_;
|
||||
wasm::BytecodeOffset bytecodeOffset() const {
|
||||
MOZ_ASSERT(bytecodeOffset_.isValid());
|
||||
return bytecodeOffset_;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
|
||||
|
@ -7985,11 +7997,11 @@ class MWasmTrap
|
|||
public NoTypePolicy::Data
|
||||
{
|
||||
wasm::Trap trap_;
|
||||
wasm::TrapOffset trapOffset_;
|
||||
wasm::BytecodeOffset bytecodeOffset_;
|
||||
|
||||
explicit MWasmTrap(wasm::Trap trap, wasm::TrapOffset trapOffset)
|
||||
explicit MWasmTrap(wasm::Trap trap, wasm::BytecodeOffset bytecodeOffset)
|
||||
: trap_(trap),
|
||||
trapOffset_(trapOffset)
|
||||
bytecodeOffset_(bytecodeOffset)
|
||||
{}
|
||||
|
||||
public:
|
||||
|
@ -8001,7 +8013,7 @@ class MWasmTrap
|
|||
}
|
||||
|
||||
wasm::Trap trap() const { return trap_; }
|
||||
wasm::TrapOffset trapOffset() const { return trapOffset_; }
|
||||
wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
|
||||
};
|
||||
|
||||
// Checks if a value is JS_UNINITIALIZED_LEXICAL, bailout out if so, leaving
|
||||
|
@ -12546,6 +12558,22 @@ class MInArray
|
|||
}
|
||||
};
|
||||
|
||||
class MHasOwnCache
|
||||
: public MBinaryInstruction,
|
||||
public MixPolicy<BoxExceptPolicy<0, MIRType::Object>, CacheIdPolicy<1>>::Data
|
||||
{
|
||||
MHasOwnCache(MDefinition* obj, MDefinition* id)
|
||||
: MBinaryInstruction(obj, id)
|
||||
{
|
||||
setResultType(MIRType::Boolean);
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(HasOwnCache)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, value), (1, idval))
|
||||
};
|
||||
|
||||
// Implementation for instanceof operator with specific rhs.
|
||||
class MInstanceOf
|
||||
: public MUnaryInstruction,
|
||||
|
@ -13750,11 +13778,12 @@ class MWasmBoundsCheck
|
|||
: public MBinaryInstruction,
|
||||
public NoTypePolicy::Data
|
||||
{
|
||||
wasm::TrapOffset trapOffset_;
|
||||
wasm::BytecodeOffset bytecodeOffset_;
|
||||
|
||||
explicit MWasmBoundsCheck(MDefinition* index, MDefinition* boundsCheckLimit, wasm::TrapOffset trapOffset)
|
||||
explicit MWasmBoundsCheck(MDefinition* index, MDefinition* boundsCheckLimit,
|
||||
wasm::BytecodeOffset bytecodeOffset)
|
||||
: MBinaryInstruction(index, boundsCheckLimit),
|
||||
trapOffset_(trapOffset)
|
||||
bytecodeOffset_(bytecodeOffset)
|
||||
{
|
||||
// Bounds check is effectful: it throws for OOB.
|
||||
setGuard();
|
||||
|
@ -13777,8 +13806,8 @@ class MWasmBoundsCheck
|
|||
setNotGuard();
|
||||
}
|
||||
|
||||
wasm::TrapOffset trapOffset() const {
|
||||
return trapOffset_;
|
||||
wasm::BytecodeOffset bytecodeOffset() const {
|
||||
return bytecodeOffset_;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -13787,12 +13816,12 @@ class MWasmAddOffset
|
|||
public NoTypePolicy::Data
|
||||
{
|
||||
uint32_t offset_;
|
||||
wasm::TrapOffset trapOffset_;
|
||||
wasm::BytecodeOffset bytecodeOffset_;
|
||||
|
||||
MWasmAddOffset(MDefinition* base, uint32_t offset, wasm::TrapOffset trapOffset)
|
||||
MWasmAddOffset(MDefinition* base, uint32_t offset, wasm::BytecodeOffset bytecodeOffset)
|
||||
: MUnaryInstruction(base),
|
||||
offset_(offset),
|
||||
trapOffset_(trapOffset)
|
||||
bytecodeOffset_(bytecodeOffset)
|
||||
{
|
||||
setGuard();
|
||||
setResultType(MIRType::Int32);
|
||||
|
@ -13812,8 +13841,8 @@ class MWasmAddOffset
|
|||
uint32_t offset() const {
|
||||
return offset_;
|
||||
}
|
||||
wasm::TrapOffset trapOffset() const {
|
||||
return trapOffset_;
|
||||
wasm::BytecodeOffset bytecodeOffset() const {
|
||||
return bytecodeOffset_;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -14055,11 +14084,14 @@ class MAsmJSCompareExchangeHeap
|
|||
public NoTypePolicy::Data
|
||||
{
|
||||
wasm::MemoryAccessDesc access_;
|
||||
wasm::BytecodeOffset bytecodeOffset_;
|
||||
|
||||
explicit MAsmJSCompareExchangeHeap(const wasm::MemoryAccessDesc& access)
|
||||
: access_(access)
|
||||
explicit MAsmJSCompareExchangeHeap(const wasm::MemoryAccessDesc& access,
|
||||
wasm::BytecodeOffset bytecodeOffset)
|
||||
: access_(access),
|
||||
bytecodeOffset_(bytecodeOffset)
|
||||
{
|
||||
setGuard(); // Not removable
|
||||
setGuard(); // Not removable
|
||||
setResultType(MIRType::Int32);
|
||||
}
|
||||
|
||||
|
@ -14067,6 +14099,7 @@ class MAsmJSCompareExchangeHeap
|
|||
INSTRUCTION_HEADER(AsmJSCompareExchangeHeap)
|
||||
|
||||
static MAsmJSCompareExchangeHeap* New(TempAllocator& alloc,
|
||||
wasm::BytecodeOffset bytecodeOffset,
|
||||
MDefinition* memoryBase,
|
||||
MDefinition* base,
|
||||
const wasm::MemoryAccessDesc& access,
|
||||
|
@ -14074,21 +14107,20 @@ class MAsmJSCompareExchangeHeap
|
|||
MDefinition* newv,
|
||||
MDefinition* tls)
|
||||
{
|
||||
MAsmJSCompareExchangeHeap* cas = new(alloc) MAsmJSCompareExchangeHeap(access);
|
||||
MAsmJSCompareExchangeHeap* cas = new(alloc) MAsmJSCompareExchangeHeap(access, bytecodeOffset);
|
||||
if (!cas->init(alloc, 4 + !!memoryBase))
|
||||
return nullptr;
|
||||
|
||||
cas->initOperand(0, base);
|
||||
cas->initOperand(1, oldv);
|
||||
cas->initOperand(2, newv);
|
||||
cas->initOperand(3, tls);
|
||||
if (memoryBase)
|
||||
cas->initOperand(4, memoryBase);
|
||||
|
||||
return cas;
|
||||
}
|
||||
|
||||
const wasm::MemoryAccessDesc& access() const { return access_; }
|
||||
wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
|
||||
|
||||
MDefinition* base() const { return getOperand(0); }
|
||||
MDefinition* oldValue() const { return getOperand(1); }
|
||||
|
@ -14106,9 +14138,12 @@ class MAsmJSAtomicExchangeHeap
|
|||
public NoTypePolicy::Data
|
||||
{
|
||||
wasm::MemoryAccessDesc access_;
|
||||
wasm::BytecodeOffset bytecodeOffset_;
|
||||
|
||||
explicit MAsmJSAtomicExchangeHeap(const wasm::MemoryAccessDesc& access)
|
||||
: access_(access)
|
||||
explicit MAsmJSAtomicExchangeHeap(const wasm::MemoryAccessDesc& access,
|
||||
wasm::BytecodeOffset bytecodeOffset)
|
||||
: access_(access),
|
||||
bytecodeOffset_(bytecodeOffset)
|
||||
{
|
||||
setGuard(); // Not removable
|
||||
setResultType(MIRType::Int32);
|
||||
|
@ -14118,13 +14153,14 @@ class MAsmJSAtomicExchangeHeap
|
|||
INSTRUCTION_HEADER(AsmJSAtomicExchangeHeap)
|
||||
|
||||
static MAsmJSAtomicExchangeHeap* New(TempAllocator& alloc,
|
||||
wasm::BytecodeOffset bytecodeOffset,
|
||||
MDefinition* memoryBase,
|
||||
MDefinition* base,
|
||||
const wasm::MemoryAccessDesc& access,
|
||||
MDefinition* value,
|
||||
MDefinition* tls)
|
||||
{
|
||||
MAsmJSAtomicExchangeHeap* xchg = new(alloc) MAsmJSAtomicExchangeHeap(access);
|
||||
MAsmJSAtomicExchangeHeap* xchg = new(alloc) MAsmJSAtomicExchangeHeap(access, bytecodeOffset);
|
||||
if (!xchg->init(alloc, 3 + !!memoryBase))
|
||||
return nullptr;
|
||||
|
||||
|
@ -14138,6 +14174,7 @@ class MAsmJSAtomicExchangeHeap
|
|||
}
|
||||
|
||||
const wasm::MemoryAccessDesc& access() const { return access_; }
|
||||
wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
|
||||
|
||||
MDefinition* base() const { return getOperand(0); }
|
||||
MDefinition* value() const { return getOperand(1); }
|
||||
|
@ -14155,10 +14192,13 @@ class MAsmJSAtomicBinopHeap
|
|||
{
|
||||
AtomicOp op_;
|
||||
wasm::MemoryAccessDesc access_;
|
||||
wasm::BytecodeOffset bytecodeOffset_;
|
||||
|
||||
explicit MAsmJSAtomicBinopHeap(AtomicOp op, const wasm::MemoryAccessDesc& access)
|
||||
: op_(op),
|
||||
access_(access)
|
||||
explicit MAsmJSAtomicBinopHeap(AtomicOp op, const wasm::MemoryAccessDesc& access,
|
||||
wasm::BytecodeOffset bytecodeOffset)
|
||||
: op_(op),
|
||||
access_(access),
|
||||
bytecodeOffset_(bytecodeOffset)
|
||||
{
|
||||
setGuard(); // Not removable
|
||||
setResultType(MIRType::Int32);
|
||||
|
@ -14168,6 +14208,7 @@ class MAsmJSAtomicBinopHeap
|
|||
INSTRUCTION_HEADER(AsmJSAtomicBinopHeap)
|
||||
|
||||
static MAsmJSAtomicBinopHeap* New(TempAllocator& alloc,
|
||||
wasm::BytecodeOffset bytecodeOffset,
|
||||
AtomicOp op,
|
||||
MDefinition* memoryBase,
|
||||
MDefinition* base,
|
||||
|
@ -14175,7 +14216,7 @@ class MAsmJSAtomicBinopHeap
|
|||
MDefinition* v,
|
||||
MDefinition* tls)
|
||||
{
|
||||
MAsmJSAtomicBinopHeap* binop = new(alloc) MAsmJSAtomicBinopHeap(op, access);
|
||||
MAsmJSAtomicBinopHeap* binop = new(alloc) MAsmJSAtomicBinopHeap(op, access, bytecodeOffset);
|
||||
if (!binop->init(alloc, 3 + !!memoryBase))
|
||||
return nullptr;
|
||||
|
||||
|
@ -14190,6 +14231,7 @@ class MAsmJSAtomicBinopHeap
|
|||
|
||||
AtomicOp operation() const { return op_; }
|
||||
const wasm::MemoryAccessDesc& access() const { return access_; }
|
||||
wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
|
||||
|
||||
MDefinition* base() const { return getOperand(0); }
|
||||
MDefinition* value() const { return getOperand(1); }
|
||||
|
|
|
@ -267,6 +267,7 @@ namespace jit {
|
|||
_(Round) \
|
||||
_(NearbyInt) \
|
||||
_(In) \
|
||||
_(HasOwnCache) \
|
||||
_(InstanceOf) \
|
||||
_(CallInstanceOf) \
|
||||
_(InterruptCheck) \
|
||||
|
|
|
@ -101,6 +101,14 @@ MacroAssembler::call(const wasm::CallSiteDesc& desc, wasm::Trap trap)
|
|||
append(desc, l, trap);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::call(const wasm::CallSiteDesc& desc, wasm::SymbolicAddress imm)
|
||||
{
|
||||
MOZ_ASSERT(wasm::NeedsBuiltinThunk(imm), "only for functions which may appear in profiler");
|
||||
call(imm);
|
||||
append(desc, CodeOffset(currentOffset()));
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// ABI function calls.
|
||||
|
||||
|
|
|
@ -2016,7 +2016,7 @@ MacroAssembler::convertTypedOrValueToFloatingPoint(TypedOrValueRegister src, Flo
|
|||
|
||||
void
|
||||
MacroAssembler::outOfLineTruncateSlow(FloatRegister src, Register dest, bool widenFloatToDouble,
|
||||
bool compilingWasm)
|
||||
bool compilingWasm, wasm::BytecodeOffset callOffset)
|
||||
{
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
|
||||
defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
|
@ -2030,7 +2030,7 @@ MacroAssembler::outOfLineTruncateSlow(FloatRegister src, Register dest, bool wid
|
|||
MOZ_ASSERT(src.isSingle());
|
||||
srcSingle = src;
|
||||
src = src.asDouble();
|
||||
push(srcSingle);
|
||||
Push(srcSingle);
|
||||
convertFloat32ToDouble(srcSingle, src);
|
||||
}
|
||||
#else
|
||||
|
@ -2040,12 +2040,15 @@ MacroAssembler::outOfLineTruncateSlow(FloatRegister src, Register dest, bool wid
|
|||
|
||||
MOZ_ASSERT(src.isDouble());
|
||||
|
||||
setupUnalignedABICall(dest);
|
||||
passABIArg(src, MoveOp::DOUBLE);
|
||||
if (compilingWasm)
|
||||
callWithABI(wasm::SymbolicAddress::ToInt32);
|
||||
else
|
||||
if (compilingWasm) {
|
||||
setupWasmABICall();
|
||||
passABIArg(src, MoveOp::DOUBLE);
|
||||
callWithABI(callOffset, wasm::SymbolicAddress::ToInt32);
|
||||
} else {
|
||||
setupUnalignedABICall(dest);
|
||||
passABIArg(src, MoveOp::DOUBLE);
|
||||
callWithABI(mozilla::BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
|
||||
}
|
||||
storeCallWordResult(dest);
|
||||
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
|
||||
|
@ -2053,7 +2056,7 @@ MacroAssembler::outOfLineTruncateSlow(FloatRegister src, Register dest, bool wid
|
|||
// Nothing
|
||||
#elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
|
||||
if (widenFloatToDouble)
|
||||
pop(srcSingle);
|
||||
Pop(srcSingle);
|
||||
#else
|
||||
MOZ_CRASH("MacroAssembler platform hook: outOfLineTruncateSlow");
|
||||
#endif
|
||||
|
@ -2681,12 +2684,26 @@ MacroAssembler::setupABICall()
|
|||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::setupWasmABICall()
|
||||
{
|
||||
MOZ_ASSERT(IsCompilingWasm(), "non-wasm should use setupAlignedABICall");
|
||||
setupABICall();
|
||||
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
// The builtin thunk does the FP -> GPR moving on soft-FP, so
|
||||
// use hard fp unconditionally.
|
||||
abiArgs_.setUseHardFp(true);
|
||||
#endif
|
||||
dynamicAlignment_ = false;
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::setupAlignedABICall()
|
||||
{
|
||||
MOZ_ASSERT(!IsCompilingWasm(), "wasm should use setupWasmABICall");
|
||||
setupABICall();
|
||||
dynamicAlignment_ = false;
|
||||
assertStackAlignment(ABIStackAlignment);
|
||||
|
||||
#if defined(JS_CODEGEN_ARM64)
|
||||
MOZ_CRASH("Not supported on arm64");
|
||||
|
@ -2738,12 +2755,21 @@ MacroAssembler::callWithABINoProfiler(void* fun, MoveOp::Type result)
|
|||
}
|
||||
|
||||
void
|
||||
MacroAssembler::callWithABINoProfiler(wasm::SymbolicAddress imm, MoveOp::Type result)
|
||||
MacroAssembler::callWithABI(wasm::BytecodeOffset callOffset, wasm::SymbolicAddress imm,
|
||||
MoveOp::Type result)
|
||||
{
|
||||
MOZ_ASSERT(wasm::NeedsBuiltinThunk(imm));
|
||||
|
||||
uint32_t stackAdjust;
|
||||
callWithABIPre(&stackAdjust, /* callFromWasm = */ true);
|
||||
call(imm);
|
||||
callWithABIPost(stackAdjust, result);
|
||||
|
||||
// The TLS register is used in builtin thunks and must be set, by ABI:
|
||||
// reload it after passing arguments, which might have used it at spill
|
||||
// points when placing arguments.
|
||||
loadWasmTlsRegFromFrame();
|
||||
|
||||
call(wasm::CallSiteDesc(callOffset.bytecodeOffset, wasm::CallSite::Symbolic), imm);
|
||||
callWithABIPost(stackAdjust, result, /* callFromWasm = */ true);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
|
@ -2838,7 +2864,8 @@ MacroAssembler::wasmCallImport(const wasm::CallSiteDesc& desc, const wasm::Calle
|
|||
}
|
||||
|
||||
void
|
||||
MacroAssembler::wasmCallBuiltinInstanceMethod(const ABIArg& instanceArg,
|
||||
MacroAssembler::wasmCallBuiltinInstanceMethod(const wasm::CallSiteDesc& desc,
|
||||
const ABIArg& instanceArg,
|
||||
wasm::SymbolicAddress builtin)
|
||||
{
|
||||
MOZ_ASSERT(instanceArg != ABIArg());
|
||||
|
@ -2854,7 +2881,7 @@ MacroAssembler::wasmCallBuiltinInstanceMethod(const ABIArg& instanceArg,
|
|||
MOZ_CRASH("Unknown abi passing style for pointer");
|
||||
}
|
||||
|
||||
call(builtin);
|
||||
call(desc, builtin);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2888,7 +2915,7 @@ MacroAssembler::wasmCallIndirect(const wasm::CallSiteDesc& desc, const wasm::Cal
|
|||
break;
|
||||
}
|
||||
|
||||
wasm::TrapOffset trapOffset(desc.lineOrBytecode());
|
||||
wasm::BytecodeOffset trapOffset(desc.lineOrBytecode());
|
||||
|
||||
// WebAssembly throws if the index is out-of-bounds.
|
||||
if (needsBoundsCheck) {
|
||||
|
|
|
@ -495,6 +495,8 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
// Call a target native function, which is neither traceable nor movable.
|
||||
void call(ImmPtr imm) PER_SHARED_ARCH;
|
||||
void call(wasm::SymbolicAddress imm) PER_SHARED_ARCH;
|
||||
inline void call(const wasm::CallSiteDesc& desc, wasm::SymbolicAddress imm);
|
||||
|
||||
// Call a target JitCode, which must be traceable, and may be movable.
|
||||
void call(JitCode* c) PER_SHARED_ARCH;
|
||||
|
||||
|
@ -546,6 +548,11 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
// was properly aligned. Note that this only supports cdecl.
|
||||
void setupAlignedABICall(); // CRASH_ON(arm64)
|
||||
|
||||
// As setupAlignedABICall, but for WebAssembly native ABI calls, which pass
|
||||
// through a builtin thunk that uses the wasm ABI. All the wasm ABI calls
|
||||
// can be native, since we always know the stack alignment a priori.
|
||||
void setupWasmABICall(); // CRASH_ON(arm64)
|
||||
|
||||
// Setup an ABI call for when the alignment is not known. This may need a
|
||||
// scratch register.
|
||||
void setupUnalignedABICall(Register scratch) PER_ARCH;
|
||||
|
@ -563,6 +570,9 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
template <typename T>
|
||||
inline void callWithABI(const T& fun, MoveOp::Type result = MoveOp::GENERAL);
|
||||
|
||||
void callWithABI(wasm::BytecodeOffset offset, wasm::SymbolicAddress fun,
|
||||
MoveOp::Type result = MoveOp::GENERAL);
|
||||
|
||||
private:
|
||||
// Reinitialize the variables which have to be cleared before making a call
|
||||
// with callWithABI.
|
||||
|
@ -573,12 +583,11 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
|
||||
// Emits a call to a C/C++ function, resolving all argument moves.
|
||||
void callWithABINoProfiler(void* fun, MoveOp::Type result);
|
||||
void callWithABINoProfiler(wasm::SymbolicAddress imm, MoveOp::Type result);
|
||||
void callWithABINoProfiler(Register fun, MoveOp::Type result) PER_ARCH;
|
||||
void callWithABINoProfiler(const Address& fun, MoveOp::Type result) PER_ARCH;
|
||||
|
||||
// Restore the stack to its state before the setup function call.
|
||||
void callWithABIPost(uint32_t stackAdjust, MoveOp::Type result) PER_ARCH;
|
||||
void callWithABIPost(uint32_t stackAdjust, MoveOp::Type result, bool callFromWasm = false) PER_ARCH;
|
||||
|
||||
// Create the signature to be able to decode the arguments of a native
|
||||
// function, when calling a function within the simulator.
|
||||
|
@ -1439,14 +1448,14 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
// wasm specific methods, used in both the wasm baseline compiler and ion.
|
||||
void wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64, arm);
|
||||
void wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared, arm);
|
||||
void outOfLineWasmTruncateDoubleToInt32(FloatRegister input, bool isUnsigned, wasm::TrapOffset off, Label* rejoin) DEFINED_ON(x86_shared);
|
||||
void outOfLineWasmTruncateDoubleToInt32(FloatRegister input, bool isUnsigned, wasm::BytecodeOffset off, Label* rejoin) DEFINED_ON(x86_shared);
|
||||
|
||||
void wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64, arm);
|
||||
void wasmTruncateFloat32ToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared, arm);
|
||||
void outOfLineWasmTruncateFloat32ToInt32(FloatRegister input, bool isUnsigned, wasm::TrapOffset off, Label* rejoin) DEFINED_ON(x86_shared);
|
||||
void outOfLineWasmTruncateFloat32ToInt32(FloatRegister input, bool isUnsigned, wasm::BytecodeOffset off, Label* rejoin) DEFINED_ON(x86_shared);
|
||||
|
||||
void outOfLineWasmTruncateDoubleToInt64(FloatRegister input, bool isUnsigned, wasm::TrapOffset off, Label* rejoin) DEFINED_ON(x86_shared);
|
||||
void outOfLineWasmTruncateFloat32ToInt64(FloatRegister input, bool isUnsigned, wasm::TrapOffset off, Label* rejoin) DEFINED_ON(x86_shared);
|
||||
void outOfLineWasmTruncateDoubleToInt64(FloatRegister input, bool isUnsigned, wasm::BytecodeOffset off, Label* rejoin) DEFINED_ON(x86_shared);
|
||||
void outOfLineWasmTruncateFloat32ToInt64(FloatRegister input, bool isUnsigned, wasm::BytecodeOffset off, Label* rejoin) DEFINED_ON(x86_shared);
|
||||
|
||||
// This function takes care of loading the callee's TLS and pinned regs but
|
||||
// it is the caller's responsibility to save/restore TLS or pinned regs.
|
||||
|
@ -1458,7 +1467,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
// This function takes care of loading the pointer to the current instance
|
||||
// as the implicit first argument. It preserves TLS and pinned registers.
|
||||
// (TLS & pinned regs are non-volatile registers in the system ABI).
|
||||
void wasmCallBuiltinInstanceMethod(const ABIArg& instanceArg,
|
||||
void wasmCallBuiltinInstanceMethod(const wasm::CallSiteDesc& desc, const ABIArg& instanceArg,
|
||||
wasm::SymbolicAddress builtin);
|
||||
|
||||
// Emit the out-of-line trap code to which trapping jumps/branches are
|
||||
|
@ -1977,7 +1986,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
Label* fail, MIRType outputType);
|
||||
|
||||
void outOfLineTruncateSlow(FloatRegister src, Register dest, bool widenFloatToDouble,
|
||||
bool compilingWasm);
|
||||
bool compilingWasm, wasm::BytecodeOffset callOffset);
|
||||
|
||||
void convertInt32ValueToDouble(const Address& address, Register scratch, Label* done);
|
||||
void convertInt32ValueToDouble(ValueOperand val);
|
||||
|
|
|
@ -1918,6 +1918,33 @@ StripPreliminaryObjectStubs(JSContext* cx, ICFallbackStub* stub)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CheckHasNoSuchOwnProperty(JSContext* cx, JSObject* obj, jsid id)
|
||||
{
|
||||
if (obj->isNative()) {
|
||||
// Don't handle proto chains with resolve hooks.
|
||||
if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj))
|
||||
return false;
|
||||
if (obj->as<NativeObject>().contains(cx, id))
|
||||
return false;
|
||||
if (obj->getClass()->getGetProperty())
|
||||
return false;
|
||||
} else if (obj->is<UnboxedPlainObject>()) {
|
||||
if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id))
|
||||
return false;
|
||||
} else if (obj->is<UnboxedArrayObject>()) {
|
||||
if (JSID_IS_ATOM(id, cx->names().length))
|
||||
return false;
|
||||
} else if (obj->is<TypedObject>()) {
|
||||
if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id))
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, jsid id,
|
||||
JSObject** lastProto, size_t* protoChainDepthOut)
|
||||
|
@ -1925,28 +1952,13 @@ CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, jsid id,
|
|||
size_t depth = 0;
|
||||
JSObject* curObj = obj;
|
||||
while (curObj) {
|
||||
if (curObj->isNative()) {
|
||||
// Don't handle proto chains with resolve hooks.
|
||||
if (ClassMayResolveId(cx->names(), curObj->getClass(), id, curObj))
|
||||
return false;
|
||||
if (curObj->as<NativeObject>().contains(cx, id))
|
||||
return false;
|
||||
if (curObj->getClass()->getGetProperty())
|
||||
return false;
|
||||
} else if (curObj != obj) {
|
||||
if (!CheckHasNoSuchOwnProperty(cx, curObj, id))
|
||||
return false;
|
||||
|
||||
if (!curObj->isNative()) {
|
||||
// Non-native objects are only handled as the original receiver.
|
||||
return false;
|
||||
} else if (curObj->is<UnboxedPlainObject>()) {
|
||||
if (curObj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id))
|
||||
if (curObj != obj)
|
||||
return false;
|
||||
} else if (curObj->is<UnboxedArrayObject>()) {
|
||||
if (JSID_IS_ATOM(id, cx->names().length))
|
||||
return false;
|
||||
} else if (curObj->is<TypedObject>()) {
|
||||
if (curObj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id))
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject* proto = curObj->staticPrototype();
|
||||
|
|
|
@ -2298,6 +2298,9 @@ IsPreliminaryObject(JSObject* obj);
|
|||
void
|
||||
StripPreliminaryObjectStubs(JSContext* cx, ICFallbackStub* stub);
|
||||
|
||||
MOZ_MUST_USE bool
|
||||
CheckHasNoSuchOwnProperty(JSContext* cx, JSObject* obj, jsid id);
|
||||
|
||||
MOZ_MUST_USE bool
|
||||
CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, jsid id,
|
||||
JSObject** lastProto = nullptr, size_t* protoChainDepthOut = nullptr);
|
||||
|
|
|
@ -1599,6 +1599,38 @@ GetNativeDataProperty<true>(JSContext* cx, JSObject* obj, PropertyName* name, Va
|
|||
template bool
|
||||
GetNativeDataProperty<false>(JSContext* cx, JSObject* obj, PropertyName* name, Value* vp);
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
ValueToAtomOrSymbol(JSContext* cx, Value& idVal, jsid* id)
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
|
||||
if (MOZ_LIKELY(idVal.isString())) {
|
||||
JSString* s = idVal.toString();
|
||||
JSAtom* atom;
|
||||
if (s->isAtom()) {
|
||||
atom = &s->asAtom();
|
||||
} else {
|
||||
atom = AtomizeString(cx, s);
|
||||
if (!atom)
|
||||
return false;
|
||||
}
|
||||
*id = AtomToId(atom);
|
||||
} else if (idVal.isSymbol()) {
|
||||
*id = SYMBOL_TO_JSID(idVal.toSymbol());
|
||||
} else {
|
||||
if (!ValueToIdPure(idVal, id))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Watch out for ids that may be stored in dense elements.
|
||||
static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT < JSID_INT_MAX,
|
||||
"All dense elements must have integer jsids");
|
||||
if (MOZ_UNLIKELY(JSID_IS_INT(*id)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <bool HandleMissing>
|
||||
bool
|
||||
GetNativeDataPropertyByValue(JSContext* cx, JSObject* obj, Value* vp)
|
||||
|
@ -1610,30 +1642,8 @@ GetNativeDataPropertyByValue(JSContext* cx, JSObject* obj, Value* vp)
|
|||
|
||||
// vp[0] contains the id, result will be stored in vp[1].
|
||||
Value idVal = vp[0];
|
||||
|
||||
jsid id;
|
||||
if (MOZ_LIKELY(idVal.isString())) {
|
||||
JSString* s = idVal.toString();
|
||||
JSAtom* atom;
|
||||
if (s->isAtom()) {
|
||||
atom = &s->asAtom();
|
||||
} else {
|
||||
atom = AtomizeString(cx, s);
|
||||
if (!atom)
|
||||
return false;
|
||||
}
|
||||
id = AtomToId(atom);
|
||||
} else if (idVal.isSymbol()) {
|
||||
id = SYMBOL_TO_JSID(idVal.toSymbol());
|
||||
} else {
|
||||
if (!ValueToIdPure(idVal, &id))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Watch out for ids that may be stored in dense elements.
|
||||
static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT < JSID_INT_MAX,
|
||||
"All dense elements must have integer jsids");
|
||||
if (MOZ_UNLIKELY(JSID_IS_INT(id)))
|
||||
if (!ValueToAtomOrSymbol(cx, idVal, &id))
|
||||
return false;
|
||||
|
||||
Value* res = vp + 1;
|
||||
|
@ -1725,5 +1735,36 @@ ObjectHasGetterSetter(JSContext* cx, JSObject* objArg, Shape* propShape)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
HasOwnNativeDataProperty(JSContext* cx, JSObject* obj, Value* vp)
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
|
||||
if (MOZ_UNLIKELY(!obj->isNative()))
|
||||
return false;
|
||||
|
||||
// vp[0] contains the id, result will be stored in vp[1].
|
||||
Value idVal = vp[0];
|
||||
jsid id;
|
||||
if (!ValueToAtomOrSymbol(cx, idVal, &id))
|
||||
return false;
|
||||
|
||||
NativeObject* nobj = &obj->as<NativeObject>();
|
||||
if (nobj->lastProperty()->search(cx, id)) {
|
||||
vp[1].setBoolean(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Property not found. Watch out for Class hooks.
|
||||
if (MOZ_UNLIKELY(!nobj->is<PlainObject>())) {
|
||||
if (ClassMayResolveId(cx->names(), nobj->getClass(), id, nobj))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Missing property.
|
||||
vp[1].setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
|
|
@ -856,6 +856,9 @@ template <bool HandleMissing>
|
|||
bool
|
||||
GetNativeDataPropertyByValue(JSContext* cx, JSObject* obj, Value* vp);
|
||||
|
||||
bool
|
||||
HasOwnNativeDataProperty(JSContext* cx, JSObject* obj, Value* vp);
|
||||
|
||||
template <bool NeedsTypeBarrier>
|
||||
bool
|
||||
SetNativeDataProperty(JSContext* cx, JSObject* obj, PropertyName* name, Value* val);
|
||||
|
|
|
@ -621,13 +621,17 @@ CodeGeneratorARM::visitSoftDivI(LSoftDivI* ins)
|
|||
Label done;
|
||||
divICommon(mir, lhs, rhs, output, ins->snapshot(), done);
|
||||
|
||||
masm.setupAlignedABICall();
|
||||
masm.passABIArg(lhs);
|
||||
masm.passABIArg(rhs);
|
||||
if (gen->compilingWasm())
|
||||
masm.callWithABI(wasm::SymbolicAddress::aeabi_idivmod);
|
||||
else
|
||||
if (gen->compilingWasm()) {
|
||||
masm.setupWasmABICall();
|
||||
masm.passABIArg(lhs);
|
||||
masm.passABIArg(rhs);
|
||||
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::aeabi_idivmod);
|
||||
} else {
|
||||
masm.setupAlignedABICall();
|
||||
masm.passABIArg(lhs);
|
||||
masm.passABIArg(rhs);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod));
|
||||
}
|
||||
|
||||
// idivmod returns the quotient in r0, and the remainder in r1.
|
||||
if (!mir->canTruncateRemainder()) {
|
||||
|
@ -780,7 +784,6 @@ CodeGeneratorARM::visitSoftModI(LSoftModI* ins)
|
|||
MOZ_ASSERT(callTemp.code() > r3.code() && callTemp.code() < r12.code());
|
||||
masm.ma_mov(lhs, callTemp);
|
||||
|
||||
|
||||
// Prevent INT_MIN % -1;
|
||||
// The integer division will give INT_MIN, but we want -(double)INT_MIN.
|
||||
if (mir->canBeNegativeDividend()) {
|
||||
|
@ -806,13 +809,17 @@ CodeGeneratorARM::visitSoftModI(LSoftModI* ins)
|
|||
|
||||
modICommon(mir, lhs, rhs, output, ins->snapshot(), done);
|
||||
|
||||
masm.setupAlignedABICall();
|
||||
masm.passABIArg(lhs);
|
||||
masm.passABIArg(rhs);
|
||||
if (gen->compilingWasm())
|
||||
masm.callWithABI(wasm::SymbolicAddress::aeabi_idivmod);
|
||||
else
|
||||
if (gen->compilingWasm()) {
|
||||
masm.setupWasmABICall();
|
||||
masm.passABIArg(lhs);
|
||||
masm.passABIArg(rhs);
|
||||
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::aeabi_idivmod);
|
||||
} else {
|
||||
masm.setupAlignedABICall();
|
||||
masm.passABIArg(lhs);
|
||||
masm.passABIArg(rhs);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod));
|
||||
}
|
||||
|
||||
// If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0
|
||||
if (mir->canBeNegativeDividend()) {
|
||||
|
@ -2229,62 +2236,6 @@ CodeGeneratorARM::visitWasmReinterpret(LWasmReinterpret* lir)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM::emitWasmCall(LWasmCallBase* ins)
|
||||
{
|
||||
MWasmCall* mir = ins->mir();
|
||||
|
||||
if (UseHardFpABI() || mir->callee().which() != wasm::CalleeDesc::Builtin) {
|
||||
emitWasmCallBase(ins);
|
||||
return;
|
||||
}
|
||||
|
||||
// The soft ABI passes floating point arguments in GPRs. Since basically
|
||||
// nothing is set up to handle this, the values are placed in the
|
||||
// corresponding VFP registers, then transferred to GPRs immediately
|
||||
// before the call. The mapping is sN <-> rN, where double registers
|
||||
// can be treated as their two component single registers.
|
||||
|
||||
for (unsigned i = 0, e = ins->numOperands(); i < e; i++) {
|
||||
LAllocation* a = ins->getOperand(i);
|
||||
if (a->isFloatReg()) {
|
||||
FloatRegister fr = ToFloatRegister(a);
|
||||
if (fr.isDouble()) {
|
||||
uint32_t srcId = fr.singleOverlay().id();
|
||||
masm.ma_vxfer(fr, Register::FromCode(srcId), Register::FromCode(srcId + 1));
|
||||
} else {
|
||||
uint32_t srcId = fr.id();
|
||||
masm.ma_vxfer(fr, Register::FromCode(srcId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emitWasmCallBase(ins);
|
||||
|
||||
switch (mir->type()) {
|
||||
case MIRType::Double:
|
||||
masm.ma_vxfer(r0, r1, d0);
|
||||
break;
|
||||
case MIRType::Float32:
|
||||
masm.as_vxfer(r0, InvalidReg, VFPRegister(d0).singleOverlay(), Assembler::CoreToFloat);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM::visitWasmCall(LWasmCall* ins)
|
||||
{
|
||||
emitWasmCall(ins);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM::visitWasmCallI64(LWasmCallI64* ins)
|
||||
{
|
||||
emitWasmCall(ins);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
|
||||
{
|
||||
|
@ -2616,13 +2567,13 @@ CodeGeneratorARM::visitAsmJSCompareExchangeCallout(LAsmJSCompareExchangeCallout*
|
|||
masm.loadPtr(Address(tls, offsetof(wasm::TlsData, instance)), instance);
|
||||
masm.ma_mov(Imm32(mir->access().type()), viewType);
|
||||
|
||||
masm.setupAlignedABICall();
|
||||
masm.setupWasmABICall();
|
||||
masm.passABIArg(instance);
|
||||
masm.passABIArg(viewType);
|
||||
masm.passABIArg(ptr);
|
||||
masm.passABIArg(oldval);
|
||||
masm.passABIArg(newval);
|
||||
masm.callWithABI(wasm::SymbolicAddress::AtomicCmpXchg);
|
||||
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::AtomicCmpXchg);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2658,12 +2609,12 @@ CodeGeneratorARM::visitAsmJSAtomicExchangeCallout(LAsmJSAtomicExchangeCallout* i
|
|||
masm.loadPtr(Address(tls, offsetof(wasm::TlsData, instance)), instance);
|
||||
masm.ma_mov(Imm32(mir->access().type()), viewType);
|
||||
|
||||
masm.setupAlignedABICall();
|
||||
masm.setupWasmABICall();
|
||||
masm.passABIArg(instance);
|
||||
masm.passABIArg(viewType);
|
||||
masm.passABIArg(ptr);
|
||||
masm.passABIArg(value);
|
||||
masm.callWithABI(wasm::SymbolicAddress::AtomicXchg);
|
||||
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::AtomicXchg);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2730,27 +2681,28 @@ CodeGeneratorARM::visitAsmJSAtomicBinopCallout(LAsmJSAtomicBinopCallout* ins)
|
|||
masm.loadPtr(Address(tls, offsetof(wasm::TlsData, instance)), instance);
|
||||
masm.move32(Imm32(mir->access().type()), viewType);
|
||||
|
||||
masm.setupAlignedABICall();
|
||||
masm.setupWasmABICall();
|
||||
masm.passABIArg(instance);
|
||||
masm.passABIArg(viewType);
|
||||
masm.passABIArg(ptr);
|
||||
masm.passABIArg(value);
|
||||
|
||||
wasm::BytecodeOffset bytecodeOffset = mir->bytecodeOffset();
|
||||
switch (mir->operation()) {
|
||||
case AtomicFetchAddOp:
|
||||
masm.callWithABI(wasm::SymbolicAddress::AtomicFetchAdd);
|
||||
masm.callWithABI(bytecodeOffset, wasm::SymbolicAddress::AtomicFetchAdd);
|
||||
break;
|
||||
case AtomicFetchSubOp:
|
||||
masm.callWithABI(wasm::SymbolicAddress::AtomicFetchSub);
|
||||
masm.callWithABI(bytecodeOffset, wasm::SymbolicAddress::AtomicFetchSub);
|
||||
break;
|
||||
case AtomicFetchAndOp:
|
||||
masm.callWithABI(wasm::SymbolicAddress::AtomicFetchAnd);
|
||||
masm.callWithABI(bytecodeOffset, wasm::SymbolicAddress::AtomicFetchAnd);
|
||||
break;
|
||||
case AtomicFetchOrOp:
|
||||
masm.callWithABI(wasm::SymbolicAddress::AtomicFetchOr);
|
||||
masm.callWithABI(bytecodeOffset, wasm::SymbolicAddress::AtomicFetchOr);
|
||||
break;
|
||||
case AtomicFetchXorOp:
|
||||
masm.callWithABI(wasm::SymbolicAddress::AtomicFetchXor);
|
||||
masm.callWithABI(bytecodeOffset, wasm::SymbolicAddress::AtomicFetchXor);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unknown op");
|
||||
|
@ -2884,13 +2836,18 @@ CodeGeneratorARM::visitSoftUDivOrMod(LSoftUDivOrMod* ins)
|
|||
generateUDivModZeroCheck(rhs, output, &done, ins->snapshot(), div);
|
||||
generateUDivModZeroCheck(rhs, output, &done, ins->snapshot(), mod);
|
||||
|
||||
masm.setupAlignedABICall();
|
||||
masm.passABIArg(lhs);
|
||||
masm.passABIArg(rhs);
|
||||
if (gen->compilingWasm())
|
||||
masm.callWithABI(wasm::SymbolicAddress::aeabi_uidivmod);
|
||||
else
|
||||
if (gen->compilingWasm()) {
|
||||
masm.setupWasmABICall();
|
||||
masm.passABIArg(lhs);
|
||||
masm.passABIArg(rhs);
|
||||
wasm::BytecodeOffset bytecodeOffset = (div ? div->bytecodeOffset() : mod->bytecodeOffset());
|
||||
masm.callWithABI(bytecodeOffset, wasm::SymbolicAddress::aeabi_uidivmod);
|
||||
} else {
|
||||
masm.setupAlignedABICall();
|
||||
masm.passABIArg(lhs);
|
||||
masm.passABIArg(rhs);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_uidivmod));
|
||||
}
|
||||
|
||||
// uidivmod returns the quotient in r0, and the remainder in r1.
|
||||
if (div && !div->canTruncateRemainder()) {
|
||||
|
@ -2998,12 +2955,13 @@ CodeGeneratorARM::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
|
|||
|
||||
masm.Push(input);
|
||||
|
||||
masm.setupUnalignedABICall(output.high);
|
||||
masm.setupWasmABICall();
|
||||
masm.passABIArg(inputDouble, MoveOp::DOUBLE);
|
||||
|
||||
if (lir->mir()->isUnsigned())
|
||||
masm.callWithABI(wasm::SymbolicAddress::TruncateDoubleToUint64);
|
||||
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::TruncateDoubleToUint64);
|
||||
else
|
||||
masm.callWithABI(wasm::SymbolicAddress::TruncateDoubleToInt64);
|
||||
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::TruncateDoubleToInt64);
|
||||
|
||||
masm.Pop(input);
|
||||
|
||||
|
@ -3022,7 +2980,7 @@ CodeGeneratorARM::visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCheck* oo
|
|||
{
|
||||
masm.outOfLineWasmTruncateToIntCheck(ool->input(), ool->fromType(), ool->toType(),
|
||||
ool->isUnsigned(), ool->rejoin(),
|
||||
ool->trapOffset());
|
||||
ool->bytecodeOffset());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -3033,13 +2991,7 @@ CodeGeneratorARM::visitInt64ToFloatingPointCall(LInt64ToFloatingPointCall* lir)
|
|||
MInt64ToFloatingPoint* mir = lir->mir();
|
||||
MIRType toType = mir->type();
|
||||
|
||||
// We are free to clobber all registers, since this is a call instruction.
|
||||
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
|
||||
regs.take(input.low);
|
||||
regs.take(input.high);
|
||||
Register temp = regs.takeAny();
|
||||
|
||||
masm.setupUnalignedABICall(temp);
|
||||
masm.setupWasmABICall();
|
||||
masm.passABIArg(input.high);
|
||||
masm.passABIArg(input.low);
|
||||
|
||||
|
@ -3050,7 +3002,8 @@ CodeGeneratorARM::visitInt64ToFloatingPointCall(LInt64ToFloatingPointCall* lir)
|
|||
: (isUnsigned ? wasm::SymbolicAddress::Uint64ToDouble
|
||||
: wasm::SymbolicAddress::Int64ToDouble);
|
||||
|
||||
masm.callWithABI(callee, toType == MIRType::Float32 ? MoveOp::FLOAT32 : MoveOp::DOUBLE);
|
||||
MoveOp::Type result = toType == MIRType::Float32 ? MoveOp::FLOAT32 : MoveOp::DOUBLE;
|
||||
masm.callWithABI(mir->bytecodeOffset(), callee, result);
|
||||
|
||||
DebugOnly<FloatRegister> output(ToFloatRegister(lir->output()));
|
||||
MOZ_ASSERT_IF(toType == MIRType::Double, output.value == ReturnDoubleReg);
|
||||
|
@ -3138,6 +3091,27 @@ CodeGeneratorARM::visitExtendInt32ToInt64(LExtendInt32ToInt64* lir)
|
|||
masm.ma_asr(Imm32(31), output.low, output.high);
|
||||
}
|
||||
|
||||
static Register
|
||||
WasmGetTemporaryForDivOrMod(Register64 lhs, Register64 rhs)
|
||||
{
|
||||
MOZ_ASSERT(IsCompilingWasm());
|
||||
|
||||
// All inputs are useAtStart for a call instruction. As a result we cannot
|
||||
// ask the register allocator for a non-aliasing temp.
|
||||
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
|
||||
regs.take(lhs.low);
|
||||
regs.take(lhs.high);
|
||||
|
||||
// The FramePointer shouldn't be clobbered for profiling.
|
||||
regs.take(FramePointer);
|
||||
|
||||
if (lhs != rhs) {
|
||||
regs.take(rhs.low);
|
||||
regs.take(rhs.high);
|
||||
}
|
||||
return regs.takeAny();
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM::visitDivOrModI64(LDivOrModI64* lir)
|
||||
{
|
||||
|
@ -3147,29 +3121,23 @@ CodeGeneratorARM::visitDivOrModI64(LDivOrModI64* lir)
|
|||
|
||||
MOZ_ASSERT(output == ReturnReg64);
|
||||
|
||||
// All inputs are useAtStart for a call instruction. As a result we cannot
|
||||
// ask for a non-aliasing temp. Using the following to get such a temp.
|
||||
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
|
||||
regs.take(lhs.low);
|
||||
regs.take(lhs.high);
|
||||
if (lhs != rhs) {
|
||||
regs.take(rhs.low);
|
||||
regs.take(rhs.high);
|
||||
}
|
||||
Register temp = regs.takeAny();
|
||||
|
||||
Label done;
|
||||
|
||||
// Handle divide by zero.
|
||||
if (lir->canBeDivideByZero())
|
||||
masm.branchTest64(Assembler::Zero, rhs, rhs, temp, trap(lir, wasm::Trap::IntegerDivideByZero));
|
||||
if (lir->canBeDivideByZero()) {
|
||||
Register temp = WasmGetTemporaryForDivOrMod(lhs, rhs);
|
||||
masm.branchTest64(Assembler::Zero, rhs, rhs, temp,
|
||||
trap(lir, wasm::Trap::IntegerDivideByZero));
|
||||
}
|
||||
|
||||
auto* mir = lir->mir();
|
||||
|
||||
// Handle an integer overflow exception from INT64_MIN / -1.
|
||||
if (lir->canBeNegativeOverflow()) {
|
||||
Label notmin;
|
||||
masm.branch64(Assembler::NotEqual, lhs, Imm64(INT64_MIN), ¬min);
|
||||
masm.branch64(Assembler::NotEqual, rhs, Imm64(-1), ¬min);
|
||||
if (lir->mir()->isMod())
|
||||
if (mir->isMod())
|
||||
masm.xor64(output, output);
|
||||
else
|
||||
masm.jump(trap(lir, wasm::Trap::IntegerOverflow));
|
||||
|
@ -3177,17 +3145,16 @@ CodeGeneratorARM::visitDivOrModI64(LDivOrModI64* lir)
|
|||
masm.bind(¬min);
|
||||
}
|
||||
|
||||
masm.setupUnalignedABICall(temp);
|
||||
masm.setupWasmABICall();
|
||||
masm.passABIArg(lhs.high);
|
||||
masm.passABIArg(lhs.low);
|
||||
masm.passABIArg(rhs.high);
|
||||
masm.passABIArg(rhs.low);
|
||||
|
||||
MOZ_ASSERT(gen->compilingWasm());
|
||||
if (lir->mir()->isMod())
|
||||
masm.callWithABI(wasm::SymbolicAddress::ModI64);
|
||||
if (mir->isMod())
|
||||
masm.callWithABI(lir->bytecodeOffset(), wasm::SymbolicAddress::ModI64);
|
||||
else
|
||||
masm.callWithABI(wasm::SymbolicAddress::DivI64);
|
||||
masm.callWithABI(lir->bytecodeOffset(), wasm::SymbolicAddress::DivI64);
|
||||
|
||||
MOZ_ASSERT(ReturnReg64 == output);
|
||||
|
||||
|
@ -3202,32 +3169,25 @@ CodeGeneratorARM::visitUDivOrModI64(LUDivOrModI64* lir)
|
|||
|
||||
MOZ_ASSERT(ToOutRegister64(lir) == ReturnReg64);
|
||||
|
||||
// All inputs are useAtStart for a call instruction. As a result we cannot
|
||||
// ask for a non-aliasing temp. Using the following to get such a temp.
|
||||
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
|
||||
regs.take(lhs.low);
|
||||
regs.take(lhs.high);
|
||||
if (lhs != rhs) {
|
||||
regs.take(rhs.low);
|
||||
regs.take(rhs.high);
|
||||
}
|
||||
Register temp = regs.takeAny();
|
||||
|
||||
// Prevent divide by zero.
|
||||
if (lir->canBeDivideByZero())
|
||||
masm.branchTest64(Assembler::Zero, rhs, rhs, temp, trap(lir, wasm::Trap::IntegerDivideByZero));
|
||||
if (lir->canBeDivideByZero()) {
|
||||
Register temp = WasmGetTemporaryForDivOrMod(lhs, rhs);
|
||||
masm.branchTest64(Assembler::Zero, rhs, rhs, temp,
|
||||
trap(lir, wasm::Trap::IntegerDivideByZero));
|
||||
}
|
||||
|
||||
masm.setupUnalignedABICall(temp);
|
||||
masm.setupWasmABICall();
|
||||
masm.passABIArg(lhs.high);
|
||||
masm.passABIArg(lhs.low);
|
||||
masm.passABIArg(rhs.high);
|
||||
masm.passABIArg(rhs.low);
|
||||
|
||||
MOZ_ASSERT(gen->compilingWasm());
|
||||
if (lir->mir()->isMod())
|
||||
masm.callWithABI(wasm::SymbolicAddress::UModI64);
|
||||
MDefinition* mir = lir->mir();
|
||||
if (mir->isMod())
|
||||
masm.callWithABI(lir->bytecodeOffset(), wasm::SymbolicAddress::UModI64);
|
||||
else
|
||||
masm.callWithABI(wasm::SymbolicAddress::UDivI64);
|
||||
masm.callWithABI(lir->bytecodeOffset(), wasm::SymbolicAddress::UDivI64);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -236,8 +236,6 @@ class CodeGeneratorARM : public CodeGeneratorShared
|
|||
void visitWasmSelect(LWasmSelect* ins);
|
||||
void visitWasmReinterpret(LWasmReinterpret* ins);
|
||||
void emitWasmCall(LWasmCallBase* ins);
|
||||
void visitWasmCall(LWasmCall* ins);
|
||||
void visitWasmCallI64(LWasmCallI64* ins);
|
||||
void visitWasmLoad(LWasmLoad* ins);
|
||||
void visitWasmLoadI64(LWasmLoadI64* ins);
|
||||
void visitWasmUnalignedLoad(LWasmUnalignedLoad* ins);
|
||||
|
|
|
@ -145,11 +145,11 @@ class LDivOrModI64 : public LCallInstructionHelper<INT64_PIECES, INT64_PIECES*2,
|
|||
return mir_->toMod()->canBeNegativeDividend();
|
||||
return mir_->toDiv()->canBeNegativeOverflow();
|
||||
}
|
||||
wasm::TrapOffset trapOffset() const {
|
||||
wasm::BytecodeOffset bytecodeOffset() const {
|
||||
MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
|
||||
if (mir_->isMod())
|
||||
return mir_->toMod()->trapOffset();
|
||||
return mir_->toDiv()->trapOffset();
|
||||
return mir_->toMod()->bytecodeOffset();
|
||||
return mir_->toDiv()->bytecodeOffset();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -181,11 +181,11 @@ class LUDivOrModI64 : public LCallInstructionHelper<INT64_PIECES, INT64_PIECES*2
|
|||
return mir_->toMod()->canBeNegativeDividend();
|
||||
return mir_->toDiv()->canBeNegativeOverflow();
|
||||
}
|
||||
wasm::TrapOffset trapOffset() const {
|
||||
wasm::BytecodeOffset bytecodeOffset() const {
|
||||
MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
|
||||
if (mir_->isMod())
|
||||
return mir_->toMod()->trapOffset();
|
||||
return mir_->toDiv()->trapOffset();
|
||||
return mir_->toMod()->bytecodeOffset();
|
||||
return mir_->toDiv()->bytecodeOffset();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -423,6 +423,8 @@ LIRGeneratorARM::lowerDivI64(MDiv* div)
|
|||
return;
|
||||
}
|
||||
|
||||
gen->setPerformsCall();
|
||||
|
||||
LDivOrModI64* lir = new(alloc()) LDivOrModI64(useInt64RegisterAtStart(div->lhs()),
|
||||
useInt64RegisterAtStart(div->rhs()));
|
||||
defineReturn(lir, div);
|
||||
|
@ -436,6 +438,8 @@ LIRGeneratorARM::lowerModI64(MMod* mod)
|
|||
return;
|
||||
}
|
||||
|
||||
gen->setPerformsCall();
|
||||
|
||||
LDivOrModI64* lir = new(alloc()) LDivOrModI64(useInt64RegisterAtStart(mod->lhs()),
|
||||
useInt64RegisterAtStart(mod->rhs()));
|
||||
defineReturn(lir, mod);
|
||||
|
@ -444,6 +448,8 @@ LIRGeneratorARM::lowerModI64(MMod* mod)
|
|||
void
|
||||
LIRGeneratorARM::lowerUDivI64(MDiv* div)
|
||||
{
|
||||
gen->setPerformsCall();
|
||||
|
||||
LUDivOrModI64* lir = new(alloc()) LUDivOrModI64(useInt64RegisterAtStart(div->lhs()),
|
||||
useInt64RegisterAtStart(div->rhs()));
|
||||
defineReturn(lir, div);
|
||||
|
@ -452,6 +458,8 @@ LIRGeneratorARM::lowerUDivI64(MDiv* div)
|
|||
void
|
||||
LIRGeneratorARM::lowerUModI64(MMod* mod)
|
||||
{
|
||||
gen->setPerformsCall();
|
||||
|
||||
LUDivOrModI64* lir = new(alloc()) LUDivOrModI64(useInt64RegisterAtStart(mod->lhs()),
|
||||
useInt64RegisterAtStart(mod->rhs()));
|
||||
defineReturn(lir, mod);
|
||||
|
@ -1016,6 +1024,8 @@ LIRGeneratorARM::visitWasmTruncateToInt64(MWasmTruncateToInt64* ins)
|
|||
MDefinition* opd = ins->input();
|
||||
MOZ_ASSERT(opd->type() == MIRType::Double || opd->type() == MIRType::Float32);
|
||||
|
||||
gen->setPerformsCall();
|
||||
|
||||
defineReturn(new(alloc()) LWasmTruncateToInt64(useRegisterAtStart(opd)), ins);
|
||||
}
|
||||
|
||||
|
@ -1024,6 +1034,8 @@ LIRGeneratorARM::visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins)
|
|||
{
|
||||
MOZ_ASSERT(ins->type() == MIRType::Double || ins->type() == MIRType::Float32);
|
||||
|
||||
gen->setPerformsCall();
|
||||
|
||||
auto lir = new(alloc()) LInt64ToFloatingPointCall();
|
||||
lir->setInt64Operand(0, useInt64RegisterAtStart(ins->input()));
|
||||
defineReturn(lir, ins);
|
||||
|
|
|
@ -5179,6 +5179,7 @@ MacroAssembler::popReturnAddress()
|
|||
void
|
||||
MacroAssembler::setupUnalignedABICall(Register scratch)
|
||||
{
|
||||
MOZ_ASSERT(!IsCompilingWasm(), "wasm should only use aligned ABI calls");
|
||||
setupABICall();
|
||||
dynamicAlignment_ = true;
|
||||
|
||||
|
@ -5227,12 +5228,14 @@ MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromWasm)
|
|||
}
|
||||
|
||||
void
|
||||
MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
|
||||
MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result, bool callFromWasm)
|
||||
{
|
||||
if (secondScratchReg_ != lr)
|
||||
ma_mov(secondScratchReg_, lr);
|
||||
|
||||
if (!UseHardFpABI()) {
|
||||
// Calls to native functions in wasm pass through a thunk which already
|
||||
// fixes up the return value for us.
|
||||
if (!callFromWasm && !UseHardFpABI()) {
|
||||
switch (result) {
|
||||
case MoveOp::DOUBLE:
|
||||
// Move double from r0/r1 to ReturnFloatReg.
|
||||
|
@ -5577,7 +5580,7 @@ MacroAssemblerARM::wasmTruncateToInt32(FloatRegister input, Register output, MIR
|
|||
void
|
||||
MacroAssemblerARM::outOfLineWasmTruncateToIntCheck(FloatRegister input, MIRType fromType,
|
||||
MIRType toType, bool isUnsigned, Label* rejoin,
|
||||
wasm::TrapOffset trapOffset)
|
||||
wasm::BytecodeOffset trapOffset)
|
||||
{
|
||||
ScratchDoubleScope scratchScope(asMasm());
|
||||
FloatRegister scratch;
|
||||
|
|
|
@ -104,7 +104,7 @@ class MacroAssemblerARM : public Assembler
|
|||
bool isUnsigned, Label* oolEntry);
|
||||
void outOfLineWasmTruncateToIntCheck(FloatRegister input, MIRType fromType,
|
||||
MIRType toType, bool isUnsigned, Label* rejoin,
|
||||
wasm::TrapOffset trapOffs);
|
||||
wasm::BytecodeOffset trapOffset);
|
||||
|
||||
// Somewhat direct wrappers for the low-level assembler funcitons
|
||||
// bitops. Attempt to encode a virtual alu instruction using two real
|
||||
|
|
|
@ -636,18 +636,6 @@ CodeGeneratorARM64::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementSta
|
|||
MOZ_CRASH("CodeGeneratorARM64::visitStoreTypedArrayElementStatic");
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM64::visitWasmCall(LWasmCall* ins)
|
||||
{
|
||||
MOZ_CRASH("vistWasmCall");
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM64::visitWasmCallI64(LWasmCallI64* ins)
|
||||
{
|
||||
MOZ_CRASH("vistWasmCallI64");
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM64::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
|
||||
{
|
||||
|
|
|
@ -198,8 +198,6 @@ class CodeGeneratorARM64 : public CodeGeneratorShared
|
|||
void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
|
||||
void visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir);
|
||||
void visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayElement* lir);
|
||||
void visitWasmCall(LWasmCall* ins);
|
||||
void visitWasmCallI64(LWasmCallI64* ins);
|
||||
void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
|
||||
void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
|
||||
void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);
|
||||
|
|
|
@ -693,7 +693,7 @@ MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromWasm)
|
|||
}
|
||||
|
||||
void
|
||||
MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
|
||||
MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result, bool callFromWasm)
|
||||
{
|
||||
// Call boundaries communicate stack via sp.
|
||||
if (!GetStackPointer64().Is(sp))
|
||||
|
|
|
@ -1866,18 +1866,6 @@ CodeGeneratorMIPSShared::visitStoreTypedArrayElementStatic(LStoreTypedArrayEleme
|
|||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorMIPSShared::visitWasmCall(LWasmCall* ins)
|
||||
{
|
||||
emitWasmCallBase(ins);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorMIPSShared::visitWasmCallI64(LWasmCallI64* ins)
|
||||
{
|
||||
emitWasmCallBase(ins);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
CodeGeneratorMIPSShared::emitWasmLoad(T* lir)
|
||||
|
|
|
@ -213,8 +213,6 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
|
|||
void visitNegF(LNegF* lir);
|
||||
void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
|
||||
void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
|
||||
void visitWasmCall(LWasmCall* ins);
|
||||
void visitWasmCallI64(LWasmCallI64* ins);
|
||||
void visitWasmLoad(LWasmLoad* ins);
|
||||
void visitWasmUnalignedLoad(LWasmUnalignedLoad* ins);
|
||||
void visitWasmStore(LWasmStore* ins);
|
||||
|
|
|
@ -2138,6 +2138,7 @@ MacroAssembler::PopRegsInMaskIgnore(LiveRegisterSet set, LiveRegisterSet ignore)
|
|||
void
|
||||
MacroAssembler::setupUnalignedABICall(Register scratch)
|
||||
{
|
||||
MOZ_ASSERT(!IsCompilingWasm(), "wasm should only use aligned ABI calls");
|
||||
setupABICall();
|
||||
dynamicAlignment_ = true;
|
||||
|
||||
|
@ -2189,7 +2190,7 @@ MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromWasm)
|
|||
}
|
||||
|
||||
void
|
||||
MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
|
||||
MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result, bool callFromWasm)
|
||||
{
|
||||
// Restore ra value (as stored in callWithABIPre()).
|
||||
loadPtr(Address(StackPointer, stackAdjust - sizeof(intptr_t)), ra);
|
||||
|
|
|
@ -2296,6 +2296,7 @@ MacroAssembler::PopRegsInMaskIgnore(LiveRegisterSet set, LiveRegisterSet ignore)
|
|||
void
|
||||
MacroAssembler::setupUnalignedABICall(Register scratch)
|
||||
{
|
||||
MOZ_ASSERT(!IsCompilingWasm(), "wasm should only use aligned ABI calls");
|
||||
setupABICall();
|
||||
dynamicAlignment_ = true;
|
||||
|
||||
|
@ -2347,7 +2348,7 @@ MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromWasm)
|
|||
}
|
||||
|
||||
void
|
||||
MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
|
||||
MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result, bool callFromWasm)
|
||||
{
|
||||
// Restore ra value (as stored in callWithABIPre()).
|
||||
loadPtr(Address(StackPointer, stackAdjust - sizeof(intptr_t)), ra);
|
||||
|
|
|
@ -698,11 +698,11 @@ class MemoryAccessDesc
|
|||
unsigned numSimdElems_;
|
||||
jit::MemoryBarrierBits barrierBefore_;
|
||||
jit::MemoryBarrierBits barrierAfter_;
|
||||
mozilla::Maybe<wasm::TrapOffset> trapOffset_;
|
||||
mozilla::Maybe<wasm::BytecodeOffset> trapOffset_;
|
||||
|
||||
public:
|
||||
explicit MemoryAccessDesc(Scalar::Type type, uint32_t align, uint32_t offset,
|
||||
const mozilla::Maybe<TrapOffset>& trapOffset,
|
||||
const mozilla::Maybe<BytecodeOffset>& trapOffset,
|
||||
unsigned numSimdElems = 0,
|
||||
jit::MemoryBarrierBits barrierBefore = jit::MembarNobits,
|
||||
jit::MemoryBarrierBits barrierAfter = jit::MembarNobits)
|
||||
|
@ -733,7 +733,7 @@ class MemoryAccessDesc
|
|||
jit::MemoryBarrierBits barrierBefore() const { return barrierBefore_; }
|
||||
jit::MemoryBarrierBits barrierAfter() const { return barrierAfter_; }
|
||||
bool hasTrap() const { return !!trapOffset_; }
|
||||
TrapOffset trapOffset() const { return *trapOffset_; }
|
||||
BytecodeOffset trapOffset() const { return *trapOffset_; }
|
||||
bool isAtomic() const { return (barrierBefore_ | barrierAfter_) != jit::MembarNobits; }
|
||||
bool isSimd() const { return Scalar::isSimdType(type_); }
|
||||
bool isPlainAsmJS() const { return !hasTrap(); }
|
||||
|
@ -780,15 +780,15 @@ typedef Vector<CallFarJump, 0, SystemAllocPolicy> CallFarJumpVector;
|
|||
// causing the trap, and the stack depth right before control is transferred to
|
||||
// the trap out-of-line path.
|
||||
|
||||
struct TrapDesc : TrapOffset
|
||||
struct TrapDesc : BytecodeOffset
|
||||
{
|
||||
enum Kind { Jump, MemoryAccess };
|
||||
Kind kind;
|
||||
Trap trap;
|
||||
uint32_t framePushed;
|
||||
|
||||
TrapDesc(TrapOffset offset, Trap trap, uint32_t framePushed, Kind kind = Jump)
|
||||
: TrapOffset(offset), kind(kind), trap(trap), framePushed(framePushed)
|
||||
TrapDesc(BytecodeOffset offset, Trap trap, uint32_t framePushed, Kind kind = Jump)
|
||||
: BytecodeOffset(offset), kind(kind), trap(trap), framePushed(framePushed)
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче