merge mozilla-central to autoland. r=merge a=merge

This commit is contained in:
Sebastian Hengst 2017-04-14 17:22:00 +02:00
commit 875f27030a
1301 changed files with 11616 additions and 40062 deletions

View File

@ -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,

View File

@ -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) {

View File

@ -165,10 +165,6 @@ const SocialErrorListener = {
this.setErrorPage();
}
},
onProgressChange() {},
onStatusChange() {},
onSecurityChange() {},
};
SocialErrorListener.init();

View File

@ -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();
});
},

View File

@ -429,9 +429,4 @@ ProgressListener.prototype = {
this.callbacks.onStopRequest();
}
},
onLocationChange() {},
onProgressChange() {},
onStatusChange() {},
onSecurityChange() {},
};

View File

@ -243,12 +243,6 @@ FrameTreeInternal.prototype = {
}
},
// Unused nsIWebProgressListener methods.
onLocationChange() {},
onProgressChange() {},
onSecurityChange() {},
onStatusChange() {},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference])
};

View File

@ -107,12 +107,6 @@ TranslationContentHandler.prototype = {
});
},
// Unused methods.
onProgressChange() {},
onLocationChange() {},
onStatusChange() {},
onSecurityChange() {},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference]),

View File

@ -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;

View File

@ -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,

View File

@ -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
)

View File

@ -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,

View File

@ -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]

View File

@ -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.");
});
}

View File

@ -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">

View File

@ -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}) {

View File

@ -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 {

View File

@ -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: () => {}
};

View File

@ -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);
},

View File

@ -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);

View File

@ -2019,11 +2019,6 @@ ConsoleProgressListener.prototype = {
}
},
onLocationChange: function () {},
onStatusChange: function () {},
onProgressChange: function () {},
onSecurityChange: function () {},
/**
* Destroy the ConsoleProgressListener.
*/

View File

@ -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()) {

View File

@ -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);

View File

@ -533,7 +533,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
@ -2891,8 +2891,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) {
@ -2911,8 +2910,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;
@ -2926,8 +2924,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) &&
@ -2949,8 +2946,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;
@ -5166,7 +5162,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);
@ -5182,7 +5178,7 @@ nsDocument::RemoveIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
if (!CheckGetElementByIdArg(id))
return;
nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(id);
nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aID);
if (!entry) {
return;
}

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -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());
}
}
}

View File

@ -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",

View File

@ -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 {

View File

@ -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>&lt;canvas&gt;</code> element.
</canvas>
</body>
</html>

View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -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)

10
js/src/Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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.

View File

@ -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
//

View File

@ -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:

File diff suppressed because it is too large Load Diff

View File

@ -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);
}
}

View File

@ -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");

View File

@ -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];

View File

@ -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)

View File

@ -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))

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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'])

View File

@ -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();

View File

@ -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();

View File

@ -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()));
}
}

View File

@ -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;

View File

@ -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()
{

View File

@ -123,6 +123,7 @@ namespace jit {
_(JSOP_DELELEM) \
_(JSOP_STRICTDELELEM) \
_(JSOP_IN) \
_(JSOP_HASOWN) \
_(JSOP_GETGNAME) \
_(JSOP_BINDGNAME) \
_(JSOP_SETGNAME) \

View File

@ -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)

View File

@ -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

View File

@ -52,6 +52,7 @@ namespace jit {
_(SetElem_Fallback) \
\
_(In_Fallback) \
_(HasOwn_Fallback) \
\
_(GetName_Fallback) \
\

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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");

View File

@ -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);

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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_)

View File

@ -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()
{

View File

@ -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

View File

@ -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)
{

View File

@ -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);

View File

@ -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>();

View File

@ -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); }

View File

@ -267,6 +267,7 @@ namespace jit {
_(Round) \
_(NearbyInt) \
_(In) \
_(HasOwnCache) \
_(InstanceOf) \
_(CallInstanceOf) \
_(InterruptCheck) \

View File

@ -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.

View File

@ -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) {

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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), &notmin);
masm.branch64(Assembler::NotEqual, rhs, Imm64(-1), &notmin);
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(&notmin);
}
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

View File

@ -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);

View File

@ -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();
}
};

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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)
{

View File

@ -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);

View File

@ -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))

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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)
{}
};

Some files were not shown because too many files have changed in this diff Show More