author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Tue, 04 Oct 2016 12:03:18 +0200 | |
changeset 316384 | edee4625c0be084f87c896dd3fefd72c44ec5bce |
parent 316383 | 1bb61a26fb376b8b8fc08ee686eaaa46c7e32469 (current diff) |
parent 316354 | 42c95d88aaaa7c2eca1d278399421d437441ac4d (diff) |
child 316385 | 1a5030f96cdb8ac74e44d8ba776633354d1fa428 |
push id | 30770 |
push user | kwierso@gmail.com |
push date | Wed, 05 Oct 2016 00:00:48 +0000 |
treeherder | mozilla-central@3470e326025c [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 52.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
devtools/client/webconsole/new-console-output/test/actions/filters.test.js | file | annotate | diff | comparison | revisions | |
devtools/client/webconsole/new-console-output/test/actions/messages.test.js | file | annotate | diff | comparison | revisions | |
devtools/client/webconsole/new-console-output/test/actions/ui.test.js | file | annotate | diff | comparison | revisions | |
devtools/client/webconsole/new-console-output/test/components/repeat.test.js | file | annotate | diff | comparison | revisions | |
gfx/layers/apz/test/mochitest/test_bug1285070.html | file | annotate | diff | comparison | revisions |
--- a/devtools/client/debugger/new/index.html +++ b/devtools/client/debugger/new/index.html @@ -1,13 +1,13 @@ <!-- This Source Code Form is subject to the terms of the Mozilla Public - 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/. --> <!DOCTYPE html> -<html> +<html dir=""> <head> <link rel="stylesheet" type="text/css" href="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css" /> <link rel="stylesheet" type="text/css" href="chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css" /> <link rel="stylesheet"
--- a/devtools/client/framework/toolbox.js +++ b/devtools/client/framework/toolbox.js @@ -481,16 +481,20 @@ Toolbox.prototype = { get React() { return this.browserRequire("devtools/client/shared/vendor/react"); }, get ReactDOM() { return this.browserRequire("devtools/client/shared/vendor/react-dom"); }, + get ReactRedux() { + return this.browserRequire("devtools/client/shared/vendor/react-redux"); + }, + // Return HostType id for telemetry _getTelemetryHostId: function () { switch (this.hostType) { case Toolbox.HostType.BOTTOM: return 0; case Toolbox.HostType.SIDE: return 1; case Toolbox.HostType.WINDOW: return 2; case Toolbox.HostType.CUSTOM: return 3; default: return 9;
--- a/devtools/client/inspector/inspector.js +++ b/devtools/client/inspector/inspector.js @@ -24,16 +24,17 @@ const Telemetry = require("devtools/clie const Menu = require("devtools/client/framework/menu"); const MenuItem = require("devtools/client/framework/menu-item"); const {CommandUtils} = require("devtools/client/shared/developer-toolbar"); const {ComputedViewTool} = require("devtools/client/inspector/computed/computed"); const {FontInspector} = require("devtools/client/inspector/fonts/fonts"); const {HTMLBreadcrumbs} = require("devtools/client/inspector/breadcrumbs"); const {InspectorSearch} = require("devtools/client/inspector/inspector-search"); +const {LayoutViewTool} = require("devtools/client/inspector/layout/layout"); const {MarkupView} = require("devtools/client/inspector/markup/markup"); const {RuleViewTool} = require("devtools/client/inspector/rules/rules"); const {ToolSidebar} = require("devtools/client/inspector/toolsidebar"); const {ViewHelpers} = require("devtools/client/shared/widgets/view-helpers"); const clipboardHelper = require("devtools/shared/platform/clipboard"); const {LocalizationHelper, localizeMarkup} = require("devtools/shared/l10n"); const INSPECTOR_L10N = new LocalizationHelper("devtools/locale/inspector.properties"); @@ -412,16 +413,20 @@ Inspector.prototype = { get React() { return this._toolbox.React; }, get ReactDOM() { return this._toolbox.ReactDOM; }, + get ReactRedux() { + return this._toolbox.ReactRedux; + }, + get browserRequire() { return this._toolbox.browserRequire; }, get InspectorTabPanel() { if (!this._InspectorTabPanel) { this._InspectorTabPanel = this.React.createFactory(this.browserRequire( @@ -554,16 +559,26 @@ Inspector.prototype = { Services.prefs.setCharPref("devtools.inspector.activeSidebar", toolId); }; this.sidebar.on("select", this._setDefaultSidebar); this.ruleview = new RuleViewTool(this, this.panelWin); this.computedview = new ComputedViewTool(this, this.panelWin); + if (Services.prefs.getBoolPref("devtools.layoutview.enabled")) { + this.sidebar.addExistingTab( + "layoutview", + INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle"), + defaultTab == "layoutview" + ); + + this.layoutview = new LayoutViewTool(this, this.panelWin); + } + if (this.target.form.animationsActor) { this.sidebar.addFrameTab( "animationinspector", INSPECTOR_L10N.getStr("inspector.sidebar.animationInspectorTitle"), "chrome://devtools/content/animationinspector/animation-inspector.xhtml", defaultTab == "animationinspector"); } @@ -580,16 +595,30 @@ Inspector.prototype = { // Setup the splitter before the sidebar is displayed so, // we don't miss any events. this.setupSplitter(); this.sidebar.show(defaultTab); }, + /** + * Register a side-panel tab. This API can be used outside of + * DevTools (e.g. from an extension) as well as by DevTools + * code base. + * + * @param {string} tab uniq id + * @param {string} title tab title + * @param {React.Component} panel component. See `InspectorPanelTab` as an example. + * @param {boolean} selected true if the panel should be selected + */ + addSidebarTab: function (id, title, panel, selected) { + this.sidebar.addTab(id, title, panel, selected); + }, + setupToolbar: function () { this.teardownToolbar(); // Setup the sidebar toggle button. let SidebarToggle = this.React.createFactory(this.browserRequire( "devtools/client/shared/components/sidebar-toggle")); let sidebarToggle = SidebarToggle({ @@ -861,16 +890,20 @@ Inspector.prototype = { if (this.ruleview) { this.ruleview.destroy(); } if (this.computedview) { this.computedview.destroy(); } + if (this.layoutview) { + this.layoutview.destroy(); + } + if (this.fontInspector) { this.fontInspector.destroy(); } let cssPropertiesDestroyer = this._cssPropertiesLoaded.then(({front}) => { if (front) { front.destroy(); }
--- a/devtools/client/inspector/inspector.xhtml +++ b/devtools/client/inspector/inspector.xhtml @@ -179,16 +179,21 @@ <div id="propertyContainer" class="theme-separator" tabindex="0"> </div> <div id="computedview-no-results" hidden="" data-localization="content=inspector.noProperties"/> </div> </div> </div> + <div id="sidebar-panel-layoutview" class="devtools-monospace theme-sidebar inspector-tabpanel" + data-localization-bundle="devtools/locale/inspector.properties"> + <div id="layout-root"></div> + </div> + <div id="sidebar-panel-fontinspector" class="devtools-monospace theme-sidebar inspector-tabpanel" data-localization-bundle="devtools/locale/font-inspector.properties"> <div class="devtools-toolbar"> <div class="devtools-searchbox"> <input id="font-preview-text-input" class="devtools-textinput" type="search" data-localization="placeholder=fontinspector.previewText"/> </div> <label id="font-showall" class="theme-link"
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/layout/actions/index.js @@ -0,0 +1,5 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +"use strict";
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/layout/actions/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# 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/.
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/layout/components/App.js @@ -0,0 +1,24 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +"use strict"; + +const { createClass, DOM: dom } = require("devtools/client/shared/vendor/react"); +const { connect } = require("devtools/client/shared/vendor/react-redux"); + +let App = createClass({ + + displayName: "App", + + render() { + return dom.div( + { + id: "app", + } + ); + }, + +}); + +module.exports = connect(state => state)(App);
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/layout/components/moz.build @@ -0,0 +1,9 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# 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/. + +DevToolsModules( + 'App.js', +)
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/layout/layout.js @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +"use strict"; + +function LayoutViewTool(inspector, window) { + this.inspector = inspector; + this.document = window.document; + this.store = null; + + this.init(); +} + +LayoutViewTool.prototype = { + + init() { + const { React, ReactDOM, ReactRedux, browserRequire } = this.inspector; + + const Store = browserRequire("devtools/client/inspector/layout/store"); + const App = React.createFactory( + browserRequire("devtools/client/inspector/layout/components/App")); + + let store = this.store = Store(); + let provider = React.createElement(ReactRedux.Provider, { store }, App()); + ReactDOM.render(provider, this.document.querySelector("#layout-root")); + }, + + destroy() { + this.inspector = null; + this.document = null; + this.store = null; + }, + +}; + +exports.LayoutViewTool = LayoutViewTool;
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/layout/moz.build @@ -0,0 +1,17 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# 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/. + +DIRS += [ + 'actions', + 'components', + 'reducers', +] + +DevToolsModules( + 'layout.js', + 'store.js', + 'types.js', +)
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/layout/reducers/grids.js @@ -0,0 +1,21 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +"use strict"; + +const INITIAL_GRIDS = { + +}; + +let reducers = { + +}; + +module.exports = function (grids = INITIAL_GRIDS, action) { + let reducer = reducers[action.type]; + if (!reducer) { + return grids; + } + return reducer(grids, action); +};
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/layout/reducers/index.js @@ -0,0 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +"use strict"; + +exports.grids = require("./grids");
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/layout/reducers/moz.build @@ -0,0 +1,10 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# 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/. + +DevToolsModules( + 'grids.js', + 'index.js', +)
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/layout/store.js @@ -0,0 +1,33 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +"use strict"; + +const { combineReducers } = require("devtools/client/shared/vendor/redux"); +const createStore = require("devtools/client/shared/redux/create-store"); +const reducers = require("./reducers/index"); +const flags = require("devtools/shared/flags"); + +module.exports = function () { + let shouldLog = false; + let history; + + // If testing, store the action history in an array + // we'll later attach to the store + if (flags.testing) { + history = []; + shouldLog = true; + } + + let store = createStore({ + log: shouldLog, + history + })(combineReducers(reducers), {}); + + if (history) { + store.history = history; + } + + return store; +};
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/layout/types.js @@ -0,0 +1,5 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +"use strict";
--- a/devtools/client/inspector/moz.build +++ b/devtools/client/inspector/moz.build @@ -1,16 +1,17 @@ # This Source Code Form is subject to the terms of the Mozilla Public # 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/. DIRS += [ 'components', 'computed', 'fonts', + 'layout', 'markup', 'rules', 'shared' ] DevToolsModules( 'breadcrumbs.js', 'inspector-commands.js',
--- a/devtools/client/inspector/test/browser.ini +++ b/devtools/client/inspector/test/browser.ini @@ -40,16 +40,17 @@ support-files = !/devtools/client/commandline/test/helpers.js !/devtools/client/framework/test/shared-head.js !/devtools/client/shared/test/test-actor.js !/devtools/client/shared/test/test-actor-registry.js [browser_inspector_addNode_01.js] [browser_inspector_addNode_02.js] [browser_inspector_addNode_03.js] +[browser_inspector_addSidebarTab.js] [browser_inspector_breadcrumbs.js] [browser_inspector_breadcrumbs_highlight_hover.js] [browser_inspector_breadcrumbs_keybinding.js] [browser_inspector_breadcrumbs_keyboard_trap.js] skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences [browser_inspector_breadcrumbs_mutations.js] [browser_inspector_breadcrumbs_namespaced.js] [browser_inspector_breadcrumbs_visibility.js]
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_addSidebarTab.js @@ -0,0 +1,62 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +const TEST_URI = "data:text/html;charset=UTF-8," + + "<h1>browser_inspector_addtabbar.js</h1>"; + +const CONTENT_TEXT = "Hello World!"; + +/** + * Verify InspectorPanel.addSidebarTab() API that can be consumed + * by DevTools extensions as well as DevTools code base. + */ +add_task(function* () { + let { inspector } = yield openInspectorForURL(TEST_URI); + + const React = inspector.React; + const { div } = React.DOM; + + info("Adding custom panel."); + + // Define custom side-panel. + let tabPanel = React.createFactory(React.createClass({ + displayName: "myTabPanel", + render: function () { + return ( + div({className: "my-tab-panel"}, + CONTENT_TEXT + ) + ); + } + })); + + // Append custom panel (tab) into the Inspector panel and + // make sure it's selected by default (the last arg = true). + inspector.addSidebarTab("myPanel", "My Panel", tabPanel, true); + is(inspector.sidebar.getCurrentTabID(), "myPanel", + "My Panel is selected by default"); + + // Define another custom side-panel. + tabPanel = React.createFactory(React.createClass({ + displayName: "myTabPanel2", + render: function () { + return ( + div({className: "my-tab-panel2"}, + "Another Content" + ) + ); + } + })); + + // Append second panel, but don't select it by default. + inspector.addSidebarTab("myPanel", "My Panel", tabPanel, false); + is(inspector.sidebar.getCurrentTabID(), "myPanel", + "My Panel is selected by default"); + + // Check the the panel content is properly rendered. + let tabPanelNode = inspector.panelDoc.querySelector(".my-tab-panel"); + is(tabPanelNode.textContent, CONTENT_TEXT, + "Side panel content has been rendered."); +});
--- a/devtools/client/inspector/toolsidebar.js +++ b/devtools/client/inspector/toolsidebar.js @@ -79,49 +79,69 @@ ToolSidebar.prototype = { toolbox: this._toolPanel._toolbox, showAllTabsMenu: true, onSelect: this.handleSelectionChange.bind(this), }); this._tabbar = this.ReactDOM.render(sidebar, this._tabbox); }, + /** + * Register a side-panel tab. + * + * @param {string} tab uniq id + * @param {string} title tab title + * @param {React.Component} panel component. See `InspectorPanelTab` as an example. + * @param {boolean} selected true if the panel should be selected + */ + addTab: function (id, title, panel, selected) { + this._tabbar.addTab(id, title, selected, panel); + this.emit("new-tab-registered", id); + }, + + /** + * Helper API for adding side-panels that use existing DOM nodes + * (defined within inspector.xhtml) as the content. + * + * @param {string} tab uniq id + * @param {string} title tab title + * @param {boolean} selected true if the panel should be selected + */ addExistingTab: function (id, title, selected) { let panel = this.InspectorTabPanel({ id: id, idPrefix: this.TABPANEL_ID_PREFIX, key: id, title: title, }); - this._tabbar.addTab(id, title, selected, panel); - - this.emit("new-tab-registered", id); + this.addTab(id, title, panel, selected); }, /** - * Register a tab. A tab is a document. + * Helper API for adding side-panels that use existing <iframe> nodes + * (defined within inspector.xhtml) as the content. * The document must have a title, which will be used as the name of the tab. * * @param {string} tab uniq id + * @param {string} title tab title * @param {string} url + * @param {boolean} selected true if the panel should be selected */ addFrameTab: function (id, title, url, selected) { let panel = this.InspectorTabPanel({ id: id, idPrefix: this.TABPANEL_ID_PREFIX, key: id, title: title, url: url, onMount: this.onSidePanelMounted.bind(this), }); - this._tabbar.addTab(id, title, selected, panel); - - this.emit("new-tab-registered", id); + this.addTab(id, title, panel, selected); }, onSidePanelMounted: function (content, props) { let iframe = content.querySelector("iframe"); if (!iframe || iframe.getAttribute("src")) { return; }
--- a/devtools/client/locales/en-US/inspector.properties +++ b/devtools/client/locales/en-US/inspector.properties @@ -308,16 +308,21 @@ inspector.sidebar.fontInspectorTitle=Fon inspector.sidebar.ruleViewTitle=Rules # LOCALIZATION NOTE (inspector.sidebar.computedViewTitle): # This is the title shown in a tab in the side panel of the Inspector panel # that corresponds to the tool displaying the list of computed CSS values # used in the page. inspector.sidebar.computedViewTitle=Computed +# LOCALIZATION NOTE (inspector.sidebar.computedViewTitle): +# This is the title shown in a tab in the side panel of the Inspector panel +# that corresponds to the tool displaying layout information defined in the page. +inspector.sidebar.layoutViewTitle=Layout + # LOCALIZATION NOTE (inspector.sidebar.animationInspectorTitle): # This is the title shown in a tab in the side panel of the Inspector panel # that corresponds to the tool displaying animations defined in the page. inspector.sidebar.animationInspectorTitle=Animations # LOCALIZATION NOTE (inspector.eyedropper.label): A string displayed as the tooltip of # a button in the inspector which toggles the Eyedropper tool inspector.eyedropper.label=Grab a color from the page
--- a/devtools/client/preferences/devtools.js +++ b/devtools/client/preferences/devtools.js @@ -60,16 +60,25 @@ pref("devtools.inspector.show_pseudo_ele pref("devtools.inspector.imagePreviewTooltipSize", 300); // Enable user agent style inspection in rule-view pref("devtools.inspector.showUserAgentStyles", false); // Show all native anonymous content (like controls in <video> tags) pref("devtools.inspector.showAllAnonymousContent", false); // Enable the MDN docs tooltip pref("devtools.inspector.mdnDocsTooltip.enabled", true); +// Enable the Font Inspector +pref("devtools.fontinspector.enabled", true); + +// Enable the Layout View +pref("devtools.layoutview.enabled", false); + +// By how many times eyedropper will magnify pixels +pref("devtools.eyedropper.zoom", 6); + // Enable to collapse attributes that are too long. pref("devtools.markup.collapseAttributes", true); // Length to collapse attributes pref("devtools.markup.collapseAttributeLength", 120); // DevTools default color unit pref("devtools.defaultColorUnit", "authored"); @@ -227,31 +236,34 @@ pref("devtools.webaudioeditor.inspectorW // Default theme ("dark" or "light") #ifdef MOZ_DEV_EDITION sticky_pref("devtools.theme", "dark"); #else sticky_pref("devtools.theme", "light"); #endif -// Remember the Web Console filters +// Web console filters +pref("devtools.webconsole.filter.error", true); +pref("devtools.webconsole.filter.warn", true); +pref("devtools.webconsole.filter.info", true); +pref("devtools.webconsole.filter.log", true); +pref("devtools.webconsole.filter.debug", true); +pref("devtools.webconsole.filter.net", false); +pref("devtools.webconsole.filter.netxhr", false); +// Deprecated - old console frontend pref("devtools.webconsole.filter.network", true); pref("devtools.webconsole.filter.networkinfo", false); pref("devtools.webconsole.filter.netwarn", true); -pref("devtools.webconsole.filter.netxhr", false); pref("devtools.webconsole.filter.csserror", true); pref("devtools.webconsole.filter.cssparser", false); pref("devtools.webconsole.filter.csslog", false); pref("devtools.webconsole.filter.exception", true); pref("devtools.webconsole.filter.jswarn", true); pref("devtools.webconsole.filter.jslog", false); -pref("devtools.webconsole.filter.error", true); -pref("devtools.webconsole.filter.warn", true); -pref("devtools.webconsole.filter.info", true); -pref("devtools.webconsole.filter.log", true); pref("devtools.webconsole.filter.secerror", true); pref("devtools.webconsole.filter.secwarn", true); pref("devtools.webconsole.filter.serviceworkers", true); pref("devtools.webconsole.filter.sharedworkers", false); pref("devtools.webconsole.filter.windowlessworkers", false); pref("devtools.webconsole.filter.servererror", false); pref("devtools.webconsole.filter.serverwarn", false); pref("devtools.webconsole.filter.serverinfo", false); @@ -277,16 +289,19 @@ pref("devtools.browserconsole.filter.sec pref("devtools.browserconsole.filter.serviceworkers", true); pref("devtools.browserconsole.filter.sharedworkers", true); pref("devtools.browserconsole.filter.windowlessworkers", true); pref("devtools.browserconsole.filter.servererror", false); pref("devtools.browserconsole.filter.serverwarn", false); pref("devtools.browserconsole.filter.serverinfo", false); pref("devtools.browserconsole.filter.serverlog", false); +// Web console filter settings bar +pref("devtools.webconsole.ui.filterbar", false); + // Max number of inputs to store in web console history. pref("devtools.webconsole.inputHistoryCount", 50); // Persistent logging: |true| if you want the Web Console to keep all of the // logged messages after reloading the page, |false| if you want the output to // be cleared each time page navigation happens. pref("devtools.webconsole.persistlog", false); @@ -311,37 +326,31 @@ pref("devtools.hud.loglimit", 1000); // The number of lines that are displayed in the web console for the Net, // CSS, JS and Web Developer categories. These defaults should be kept in sync // with DEFAULT_LOG_LIMIT in the webconsole frontend. pref("devtools.hud.loglimit.network", 1000); pref("devtools.hud.loglimit.cssparser", 1000); pref("devtools.hud.loglimit.exception", 1000); pref("devtools.hud.loglimit.console", 1000); -// By how many times eyedropper will magnify pixels -pref("devtools.eyedropper.zoom", 6); - // The developer tools editor configuration: // - tabsize: how many spaces to use when a Tab character is displayed. // - expandtab: expand Tab characters to spaces. // - keymap: which keymap to use (can be 'default', 'emacs' or 'vim') // - autoclosebrackets: whether to permit automatic bracket/quote closing. // - detectindentation: whether to detect the indentation from the file // - enableCodeFolding: Whether to enable code folding or not. pref("devtools.editor.tabsize", 2); pref("devtools.editor.expandtab", true); pref("devtools.editor.keymap", "default"); pref("devtools.editor.autoclosebrackets", true); pref("devtools.editor.detectindentation", true); pref("devtools.editor.enableCodeFolding", true); pref("devtools.editor.autocomplete", true); -// Enable the Font Inspector -pref("devtools.fontinspector.enabled", true); - // Pref to store the browser version at the time of a telemetry ping for an // opened developer tool. This allows us to ping telemetry just once per browser // version for each user. pref("devtools.telemetry.tools.opened.version", "{}"); // Enable the JSON View tool (an inspector for application/json documents) on // Nightly and Dev. Edition. #ifdef RELEASE_BUILD
--- a/devtools/client/shared/components/reps/rep.js +++ b/devtools/client/shared/components/reps/rep.js @@ -97,17 +97,17 @@ define(function (require, exports, modul * * @param defaultObject {React.Component} The default template * that should be used to render given object if none is found. */ function getRep(object, defaultRep = Obj) { let type = typeof object; if (type == "object" && object instanceof String) { type = "string"; - } else if (type == "object" && object.type === "symbol") { + } else if (object && type == "object" && object.type === "symbol") { type = "symbol"; } if (isGrip(object)) { type = object.class; } for (let i = 0; i < reps.length; i++) {
--- a/devtools/client/shared/components/tabs/tabbar.js +++ b/devtools/client/shared/components/tabs/tabbar.js @@ -49,17 +49,21 @@ let Tabbar = createClass({ let newState = Object.assign({}, this.state, { tabs: tabs, }); if (selected) { newState.activeTab = tabs.length - 1; } - this.setState(newState); + this.setState(newState, () => { + if (this.props.onSelect && selected) { + this.props.onSelect(id); + } + }); }, toggleTab: function (tabId, isVisible) { let index = this.getTabIndex(tabId); if (index < 0) { return; }
--- a/devtools/client/themes/webconsole.css +++ b/devtools/client/themes/webconsole.css @@ -684,16 +684,17 @@ a.learn-more-link.webconsole-learn-more- #output-container { height: 100%; } .webconsole-output-wrapper { display: flex; flex-direction: column; height: 100%; + -moz-user-focus: normal; } .webconsole-filterbar-wrapper { flex-grow: 0; } .webconsole-output { flex: 1;
--- a/devtools/client/webconsole/jsterm.js +++ b/devtools/client/webconsole/jsterm.js @@ -372,17 +372,17 @@ JSTerm.prototype = { result.type == "undefined" && helperResult && !helperHasRawOutput) { callback && callback(); return; } if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) { this.hud.newConsoleOutput.dispatchMessageAdd(response); - callback && callback(this.hud.newConsoleOutput.getLastMessage()); + callback && callback(); return; } let msg = new Messages.JavaScriptEvalOutput(response, errorMessage, errorDocLink); this.hud.output.addMessage(msg); if (callback) { let oldFlushCallback = this.hud._flushCallback;
--- a/devtools/client/webconsole/new-console-output/actions/filters.js +++ b/devtools/client/webconsole/new-console-output/actions/filters.js @@ -1,39 +1,55 @@ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * 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/. */ "use strict"; +const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters"); +const Services = require("Services"); + const { FILTER_TEXT_SET, FILTER_TOGGLE, - FILTERS_CLEAR -} = require("../constants"); + FILTERS_CLEAR, + PREFS, +} = require("devtools/client/webconsole/new-console-output/constants"); function filterTextSet(text) { return { type: FILTER_TEXT_SET, text }; } function filterToggle(filter) { - return { - type: FILTER_TOGGLE, - filter, + return (dispatch, getState) => { + dispatch({ + type: FILTER_TOGGLE, + filter, + }); + const filterState = getAllFilters(getState()); + Services.prefs.setBoolPref(PREFS.FILTER[filter.toUpperCase()], + filterState.get(filter)); }; } function filtersClear() { - return { - type: FILTERS_CLEAR + return (dispatch, getState) => { + dispatch({ + type: FILTERS_CLEAR, + }); + + const filterState = getAllFilters(getState()); + for (let filter in filterState) { + Services.prefs.clearUserPref(PREFS.FILTER[filter.toUpperCase()]); + } }; } module.exports = { filterTextSet, filterToggle, filtersClear };
--- a/devtools/client/webconsole/new-console-output/actions/ui.js +++ b/devtools/client/webconsole/new-console-output/actions/ui.js @@ -1,19 +1,27 @@ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * 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/. */ "use strict"; +const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui"); +const Services = require("Services"); + const { FILTER_BAR_TOGGLE, -} = require("../constants"); + PREFS, +} = require("devtools/client/webconsole/new-console-output/constants"); function filterBarToggle(show) { - return { - type: FILTER_BAR_TOGGLE + return (dispatch, getState) => { + dispatch({ + type: FILTER_BAR_TOGGLE + }); + const uiState = getAllUi(getState()); + Services.prefs.setBoolPref(PREFS.UI.FILTER_BAR, uiState.get("filterBarVisible")); }; } exports.filterBarToggle = filterBarToggle;
--- a/devtools/client/webconsole/new-console-output/components/collapse-button.js +++ b/devtools/client/webconsole/new-console-output/components/collapse-button.js @@ -35,9 +35,9 @@ const CollapseButton = createClass({ return dom.a({ className: classes.join(" "), onClick, title: l10n.getStr("messageToggleDetails"), }); } }); -module.exports.CollapseButton = CollapseButton; +module.exports = CollapseButton;
--- a/devtools/client/webconsole/new-console-output/components/console-output.js +++ b/devtools/client/webconsole/new-console-output/components/console-output.js @@ -8,89 +8,117 @@ const { createFactory, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); const ReactDOM = require("devtools/client/shared/vendor/react-dom"); const { connect } = require("devtools/client/shared/vendor/react-redux"); const { getAllMessages, getAllMessagesUiById, getAllMessagesTableDataById } = require("devtools/client/webconsole/new-console-output/selectors/messages"); +const { getScrollSetting } = require("devtools/client/webconsole/new-console-output/selectors/ui"); const MessageContainer = createFactory(require("devtools/client/webconsole/new-console-output/components/message-container").MessageContainer); const ConsoleOutput = createClass({ + displayName: "ConsoleOutput", + propTypes: { hudProxyClient: PropTypes.object.isRequired, messages: PropTypes.object.isRequired, messagesUi: PropTypes.object.isRequired, sourceMapService: PropTypes.object, onViewSourceInDebugger: PropTypes.func.isRequired, openNetworkPanel: PropTypes.func.isRequired, openLink: PropTypes.func.isRequired, + emitNewMessage: PropTypes.func.isRequired, + autoscroll: PropTypes.bool.isRequired, + }, + + componentDidMount() { + scrollToBottom(this.outputNode); }, - displayName: "ConsoleOutput", + componentWillUpdate(nextProps, nextState) { + if (!this.outputNode) { + return; + } - componentWillUpdate() { - let node = ReactDOM.findDOMNode(this); - if (node.lastChild) { - this.shouldScrollBottom = isScrolledToBottom(node.lastChild, node); + const outputNode = this.outputNode; + + // Figure out if we are at the bottom. If so, then any new message should be scrolled + // into view. + if (this.props.autoscroll && outputNode.lastChild) { + this.shouldScrollBottom = isScrolledToBottom(outputNode.lastChild, outputNode); } }, componentDidUpdate() { if (this.shouldScrollBottom) { - let node = ReactDOM.findDOMNode(this); - node.scrollTop = node.scrollHeight; + scrollToBottom(this.outputNode); } }, render() { let { dispatch, + autoscroll, hudProxyClient, messages, messagesUi, messagesTableData, sourceMapService, onViewSourceInDebugger, openNetworkPanel, openLink, + emitNewMessage, } = this.props; let messageNodes = messages.map((message) => { return ( MessageContainer({ dispatch, hudProxyClient, message, key: message.id, sourceMapService, onViewSourceInDebugger, openNetworkPanel, openLink, + emitNewMessage, open: messagesUi.includes(message.id), tableData: messagesTableData.get(message.id), + autoscroll, }) ); }); return ( - dom.div({className: "webconsole-output"}, messageNodes) + dom.div({ + className: "webconsole-output", + ref: node => { + this.outputNode = node; + }, + }, messageNodes + ) ); } }); +function scrollToBottom(node) { + node.scrollTop = node.scrollHeight; +} + function isScrolledToBottom(outputNode, scrollNode) { let lastNodeHeight = outputNode.lastChild ? outputNode.lastChild.clientHeight : 0; return scrollNode.scrollTop + scrollNode.clientHeight >= scrollNode.scrollHeight - lastNodeHeight / 2; } function mapStateToProps(state) { return { messages: getAllMessages(state), messagesUi: getAllMessagesUiById(state), messagesTableData: getAllMessagesTableDataById(state), + autoscroll: getScrollSetting(state), }; } module.exports = connect(mapStateToProps)(ConsoleOutput);
--- a/devtools/client/webconsole/new-console-output/components/console-table.js +++ b/devtools/client/webconsole/new-console-output/components/console-table.js @@ -7,17 +7,17 @@ const { createClass, createFactory, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); const { ObjectClient } = require("devtools/shared/client/main"); const actions = require("devtools/client/webconsole/new-console-output/actions/messages"); const {l10n} = require("devtools/client/webconsole/new-console-output/utils/messages"); -const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body").GripMessageBody); +const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body")); const TABLE_ROW_MAX_ITEMS = 1000; const TABLE_COLUMN_MAX_ITEMS = 10; const ConsoleTable = createClass({ displayName: "ConsoleTable", @@ -192,9 +192,9 @@ function getTableItems(data = {}, type, } return { columns, items }; } -exports.ConsoleTable = ConsoleTable; +module.exports = ConsoleTable;
--- a/devtools/client/webconsole/new-console-output/components/filter-bar.js +++ b/devtools/client/webconsole/new-console-output/components/filter-bar.js @@ -13,17 +13,17 @@ const { connect } = require("devtools/cl const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters"); const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui"); const { filterTextSet, filtersClear } = require("devtools/client/webconsole/new-console-output/actions/index"); const { messagesClear } = require("devtools/client/webconsole/new-console-output/actions/index"); const uiActions = require("devtools/client/webconsole/new-console-output/actions/index"); const { MESSAGE_LEVEL } = require("../constants"); -const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button").FilterButton); +const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button")); const FilterBar = createClass({ displayName: "FilterBar", propTypes: { filter: PropTypes.object.isRequired, ui: PropTypes.object.isRequired @@ -68,17 +68,17 @@ const FilterBar = createClass({ value: filter.text, placeholder: "Filter output", onInput: this.onSearchInput }) )); if (filterBarVisible) { children.push( - dom.div({className: "devtools-toolbar"}, + dom.div({className: "devtools-toolbar webconsole-filterbar-secondary"}, FilterButton({ active: filter.error, label: "Errors", filterKey: MESSAGE_LEVEL.ERROR, dispatch }), FilterButton({ active: filter.warn, @@ -109,19 +109,19 @@ const FilterBar = createClass({ }), FilterButton({ active: filter.netxhr, label: "XHR", filterKey: "netxhr", dispatch }), FilterButton({ - active: filter.network, + active: filter.net, label: "Requests", - filterKey: "network", + filterKey: "net", dispatch }) ) ); } if (ui.filteredMessageVisible) { children.push(
--- a/devtools/client/webconsole/new-console-output/components/filter-button.js +++ b/devtools/client/webconsole/new-console-output/components/filter-button.js @@ -21,23 +21,26 @@ const FilterButton = createClass({ dispatch: PropTypes.func.isRequired, }, onClick: function () { this.props.dispatch(actions.filterToggle(this.props.filterKey)); }, render() { - const {label, active} = this.props; + const {active, label, filterKey} = this.props; - let classList = ["menu-filter-button"]; + let classList = [ + "menu-filter-button", + filterKey, + ]; if (active) { classList.push("checked"); } return dom.button({ className: classList.join(" "), onClick: this.onClick }, label); } }); -exports.FilterButton = FilterButton; +module.exports = FilterButton;
--- a/devtools/client/webconsole/new-console-output/components/grip-message-body.js +++ b/devtools/client/webconsole/new-console-output/components/grip-message-body.js @@ -15,17 +15,17 @@ if (typeof define === "undefined") { // React const { createFactory, PropTypes } = require("devtools/client/shared/vendor/react"); const { createFactories } = require("devtools/client/shared/components/reps/rep-utils"); const { Rep } = createFactories(require("devtools/client/shared/components/reps/rep")); const StringRep = createFactories(require("devtools/client/shared/components/reps/string").StringRep).rep; -const VariablesViewLink = createFactory(require("devtools/client/webconsole/new-console-output/components/variables-view-link").VariablesViewLink); +const VariablesViewLink = createFactory(require("devtools/client/webconsole/new-console-output/components/variables-view-link")); const { Grip } = require("devtools/client/shared/components/reps/grip"); GripMessageBody.displayName = "GripMessageBody"; GripMessageBody.propTypes = { grip: PropTypes.oneOfType([ PropTypes.string, PropTypes.number, @@ -48,9 +48,9 @@ function GripMessageBody(props) { object: grip, objectLink: VariablesViewLink, defaultRep: Grip, mode: props.mode, }) ); } -module.exports.GripMessageBody = GripMessageBody; +module.exports = GripMessageBody;
--- a/devtools/client/webconsole/new-console-output/components/message-container.js +++ b/devtools/client/webconsole/new-console-output/components/message-container.js @@ -14,75 +14,56 @@ const { } = require("devtools/client/shared/vendor/react"); const { MESSAGE_SOURCE, MESSAGE_TYPE } = require("devtools/client/webconsole/new-console-output/constants"); const componentMap = new Map([ - ["ConsoleApiCall", require("./message-types/console-api-call").ConsoleApiCall], - ["ConsoleCommand", require("./message-types/console-command").ConsoleCommand], - ["DefaultRenderer", require("./message-types/default-renderer").DefaultRenderer], - ["EvaluationResult", require("./message-types/evaluation-result").EvaluationResult], - ["NetworkEventMessage", require("./message-types/network-event-message").NetworkEventMessage], - ["PageError", require("./message-types/page-error").PageError] + ["ConsoleApiCall", require("./message-types/console-api-call")], + ["ConsoleCommand", require("./message-types/console-command")], + ["DefaultRenderer", require("./message-types/default-renderer")], + ["EvaluationResult", require("./message-types/evaluation-result")], + ["NetworkEventMessage", require("./message-types/network-event-message")], + ["PageError", require("./message-types/page-error")] ]); const MessageContainer = createClass({ displayName: "MessageContainer", propTypes: { message: PropTypes.object.isRequired, sourceMapService: PropTypes.object, onViewSourceInDebugger: PropTypes.func.isRequired, openNetworkPanel: PropTypes.func.isRequired, openLink: PropTypes.func.isRequired, open: PropTypes.bool.isRequired, hudProxyClient: PropTypes.object.isRequired, + autoscroll: PropTypes.bool.isRequired, }, getDefaultProps: function () { return { open: false }; }, shouldComponentUpdate(nextProps, nextState) { const repeatChanged = this.props.message.repeat !== nextProps.message.repeat; const openChanged = this.props.open !== nextProps.open; const tableDataChanged = this.props.tableData !== nextProps.tableData; return repeatChanged || openChanged || tableDataChanged; }, render() { - const { - dispatch, - message, - sourceMapService, - onViewSourceInDebugger, - openNetworkPanel, - openLink, - open, - tableData, - hudProxyClient, - } = this.props; + const { message } = this.props; let MessageComponent = createFactory(getMessageComponent(message)); - return MessageComponent({ - dispatch, - message, - sourceMapService, - onViewSourceInDebugger, - openNetworkPanel, - openLink, - open, - tableData, - hudProxyClient, - }); + return MessageComponent(this.props); } }); function getMessageComponent(message) { switch (message.source) { case MESSAGE_SOURCE.CONSOLE_API: return componentMap.get("ConsoleApiCall"); case MESSAGE_SOURCE.NETWORK:
--- a/devtools/client/webconsole/new-console-output/components/message-icon.js +++ b/devtools/client/webconsole/new-console-output/components/message-icon.js @@ -24,9 +24,9 @@ function MessageIcon(props) { const title = l10n.getStr("level." + level); return dom.div({ className: "icon", title }); } -module.exports.MessageIcon = MessageIcon; +module.exports = MessageIcon;
--- a/devtools/client/webconsole/new-console-output/components/message-repeat.js +++ b/devtools/client/webconsole/new-console-output/components/message-repeat.js @@ -7,22 +7,30 @@ "use strict"; // React & Redux const { DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); +const { PluralForm } = require("devtools/shared/plural-form"); +const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages"); MessageRepeat.displayName = "MessageRepeat"; MessageRepeat.propTypes = { repeat: PropTypes.number.isRequired }; function MessageRepeat(props) { const { repeat } = props; const visibility = repeat > 1 ? "visible" : "hidden"; - return dom.span({className: "message-repeats", style: {visibility}}, repeat); + + return dom.span({ + className: "message-repeats", + style: {visibility}, + title: PluralForm.get(repeat, l10n.getStr("messageRepeats.tooltip2")) + .replace("#1", repeat) + }, repeat); } -exports.MessageRepeat = MessageRepeat; +module.exports = MessageRepeat;
--- a/devtools/client/webconsole/new-console-output/components/message-types/console-api-call.js +++ b/devtools/client/webconsole/new-console-output/components/message-types/console-api-call.js @@ -7,24 +7,20 @@ "use strict"; // React & Redux const { createFactory, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); -const FrameView = createFactory(require("devtools/client/shared/components/frame")); -const StackTrace = createFactory(require("devtools/client/shared/components/stack-trace")); -const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body").GripMessageBody); -const MessageRepeat = createFactory(require("devtools/client/webconsole/new-console-output/components/message-repeat").MessageRepeat); -const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon); -const CollapseButton = createFactory(require("devtools/client/webconsole/new-console-output/components/collapse-button").CollapseButton); -const ConsoleTable = createFactory(require("devtools/client/webconsole/new-console-output/components/console-table").ConsoleTable); -const actions = require("devtools/client/webconsole/new-console-output/actions/index"); +const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body")); +const ConsoleTable = createFactory(require("devtools/client/webconsole/new-console-output/components/console-table")); + +const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message")); ConsoleApiCall.displayName = "ConsoleApiCall"; ConsoleApiCall.propTypes = { message: PropTypes.object.isRequired, sourceMapService: PropTypes.object, onViewSourceInDebugger: PropTypes.func.isRequired, open: PropTypes.bool, @@ -38,114 +34,83 @@ ConsoleApiCall.defaultProps = { function ConsoleApiCall(props) { const { dispatch, message, sourceMapService, onViewSourceInDebugger, open, hudProxyClient, - tableData + tableData, + emitNewMessage, } = props; - const {source, level, stacktrace, type, frame, parameters } = message; + const { + id: messageId, + source, type, + level, + repeat, + stacktrace, + frame, + parameters + } = message; let messageBody; if (type === "trace") { messageBody = dom.span({className: "cm-variable"}, "console.trace()"); } else if (type === "assert") { let reps = formatReps(parameters); messageBody = dom.span({ className: "cm-variable" }, "Assertion failed: ", reps); } else if (type === "table") { // TODO: Chrome does not output anything, see if we want to keep this messageBody = dom.span({className: "cm-variable"}, "console.table()"); } else if (parameters) { messageBody = formatReps(parameters); } else { messageBody = message.messageText; } - const icon = MessageIcon({ level }); - const repeat = MessageRepeat({ repeat: message.repeat }); - const shouldRenderFrame = frame && frame.source !== "debugger eval code"; - const location = dom.span({ className: "message-location devtools-monospace" }, - shouldRenderFrame ? FrameView({ - frame, - onClick: onViewSourceInDebugger, - showEmptyPathAsHost: true, - sourceMapService - }) : null - ); - - let collapse = ""; - let attachment = ""; - if (stacktrace) { - if (open) { - attachment = dom.div({ className: "stacktrace devtools-monospace" }, - StackTrace({ - stacktrace: stacktrace, - onViewSourceInDebugger: onViewSourceInDebugger - }) - ); - } - - collapse = CollapseButton({ - open, - onClick: function () { - if (open) { - dispatch(actions.messageClose(message.id)); - } else { - dispatch(actions.messageOpen(message.id)); - } - }, - }); - } else if (type === "table") { + let attachment = null; + if (type === "table") { attachment = ConsoleTable({ dispatch, id: message.id, hudProxyClient, parameters: message.parameters, tableData }); } - const classes = ["message", "cm-s-mozilla"]; - - classes.push(source); - classes.push(type); - classes.push(level); - - if (open === true) { - classes.push("open"); - } + const topLevelClasses = ["cm-s-mozilla"]; - return dom.div({ className: classes.join(" ") }, - // @TODO add timestamp - // @TODO add indent if necessary - icon, - collapse, - dom.span({ className: "message-body-wrapper" }, - dom.span({ className: "message-flex-body" }, - dom.span({ className: "message-body devtools-monospace" }, - messageBody - ), - repeat, - location - ), - attachment - ) - ); + return Message({ + messageId, + open, + source, + type, + level, + topLevelClasses, + messageBody, + repeat, + frame, + stacktrace, + attachment, + onViewSourceInDebugger, + sourceMapService, + emitNewMessage, + dispatch, + }); } function formatReps(parameters) { return ( parameters // Get all the grips. .map((grip, key) => GripMessageBody({ grip, key })) // Interleave spaces. .reduce((arr, v, i) => { return i + 1 < parameters.length ? arr.concat(v, dom.span({}, " ")) : arr.concat(v); }, []) ); } -module.exports.ConsoleApiCall = ConsoleApiCall; +module.exports = ConsoleApiCall;
--- a/devtools/client/webconsole/new-console-output/components/message-types/console-command.js +++ b/devtools/client/webconsole/new-console-output/components/message-types/console-command.js @@ -4,52 +4,42 @@ * 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/. */ "use strict"; // React & Redux const { createFactory, - DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); -const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon); +const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message")); ConsoleCommand.displayName = "ConsoleCommand"; ConsoleCommand.propTypes = { message: PropTypes.object.isRequired, + autoscroll: PropTypes.bool.isRequired, }; /** * Displays input from the console. */ function ConsoleCommand(props) { - const { message } = props; - const {source, type, level} = message; - - const icon = MessageIcon({level}); - - const classes = ["message"]; - - classes.push(source); - classes.push(type); - classes.push(level); + const { + source, + type, + level, + messageText: messageBody, + } = props.message; - return dom.div({ - className: classes.join(" "), - ariaLive: "off", - }, - // @TODO add timestamp - // @TODO add indent if necessary - icon, - dom.span({ className: "message-body-wrapper" }, - dom.span({ className: "message-flex-body" }, - dom.span({ className: "message-body devtools-monospace" }, - message.messageText - ) - ) - ) - ); + const childProps = { + source, + type, + level, + topLevelClasses: [], + messageBody, + scrollToMessage: props.autoscroll, + }; + return Message(childProps); } -module.exports.ConsoleCommand = ConsoleCommand; +module.exports = ConsoleCommand;
--- a/devtools/client/webconsole/new-console-output/components/message-types/default-renderer.js +++ b/devtools/client/webconsole/new-console-output/components/message-types/default-renderer.js @@ -14,9 +14,9 @@ const { DefaultRenderer.displayName = "DefaultRenderer"; function DefaultRenderer(props) { return dom.div({}, "This message type is not supported yet." ); } -module.exports.DefaultRenderer = DefaultRenderer; +module.exports = DefaultRenderer;
--- a/devtools/client/webconsole/new-console-output/components/message-types/evaluation-result.js +++ b/devtools/client/webconsole/new-console-output/components/message-types/evaluation-result.js @@ -4,56 +4,50 @@ * 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/. */ "use strict"; // React & Redux const { createFactory, - DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); -const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body").GripMessageBody); -const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon); +const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message")); +const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body")); EvaluationResult.displayName = "EvaluationResult"; EvaluationResult.propTypes = { message: PropTypes.object.isRequired, }; function EvaluationResult(props) { const { message } = props; - const {source, type, level} = message; - const icon = MessageIcon({level}); + const { + source, + type, + level, + emitNewMessage, + } = message; let messageBody; if (message.messageText) { messageBody = message.messageText; } else { messageBody = GripMessageBody({grip: message.parameters}); } - - const classes = ["message", "cm-s-mozilla"]; - - classes.push(source); - classes.push(type); - classes.push(level); + const topLevelClasses = ["cm-s-mozilla"]; - return dom.div({ - className: classes.join(" ") - }, - // @TODO add timestamp - // @TODO add indent if needed with console.group - icon, - dom.span({ className: "message-body-wrapper" }, - dom.span({ className: "message-flex-body" }, - dom.span({ className: "message-body devtools-monospace" }, - messageBody - ) - ) - ) - ); + const childProps = { + source, + type, + level, + topLevelClasses, + messageBody, + scrollToMessage: props.autoscroll, + emitNewMessage, + }; + return Message(childProps); } -module.exports.EvaluationResult = EvaluationResult; +module.exports = EvaluationResult;
--- a/devtools/client/webconsole/new-console-output/components/message-types/network-event-message.js +++ b/devtools/client/webconsole/new-console-output/components/message-types/network-event-message.js @@ -7,69 +7,49 @@ "use strict"; // React & Redux const { createFactory, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); -const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon); -const CollapseButton = createFactory(require("devtools/client/webconsole/new-console-output/components/collapse-button").CollapseButton); +const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message")); const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages"); -const actions = require("devtools/client/webconsole/new-console-output/actions/index"); NetworkEventMessage.displayName = "NetworkEventMessage"; NetworkEventMessage.propTypes = { message: PropTypes.object.isRequired, openNetworkPanel: PropTypes.func.isRequired, - // @TODO: openLink will be used for mixed-content handling - openLink: PropTypes.func.isRequired, - open: PropTypes.bool.isRequired, }; function NetworkEventMessage(props) { - const { dispatch, message, openNetworkPanel, open } = props; - const { actor, source, type, level, request, response, isXHR, totalTime } = message; - let { method, url } = request; - let { httpVersion, status, statusText } = response; - - let classes = ["message", "cm-s-mozilla"]; - - classes.push(source); - classes.push(type); - classes.push(level); + const { message, openNetworkPanel, emitNewMessage } = props; + const { actor, source, type, level, request, isXHR } = message; - if (open) { - classes.push("open"); - } - - let statusInfo = "[]"; - - // @TODO: Status will be enabled after NetworkUpdateEvent packet arrives - if (httpVersion && status && statusText && totalTime) { - statusInfo = `[${httpVersion} ${status} ${statusText} ${totalTime}ms]`; - } - - let xhr = l10n.getStr("webConsoleXhrIndicator"); + const topLevelClasses = [ "cm-s-mozilla" ]; function onUrlClick() { openNetworkPanel(actor); } - return dom.div({ className: classes.join(" ") }, - // @TODO add timestamp - // @TODO add indent if necessary - MessageIcon({ level }), - dom.span({ - className: "message-body-wrapper message-body devtools-monospace", - "aria-haspopup": "true" - }, - dom.span({ className: "method" }, method), - isXHR ? dom.span({ className: "xhr" }, xhr) : null, - dom.a({ className: "url", title: url, onClick: onUrlClick }, - url.replace(/\?.+/, "")) - ) - ); + const method = dom.span({className: "method" }, request.method); + const xhr = isXHR + ? dom.span({ className: "xhr" }, l10n.getStr("webConsoleXhrIndicator")) + : null; + const url = dom.a({ className: "url", title: request.url, onClick: onUrlClick }, + request.url.replace(/\?.+/, "")); + + const messageBody = dom.span({}, method, xhr, url); + + const childProps = { + source, + type, + level, + topLevelClasses, + messageBody, + emitNewMessage, + }; + return Message(childProps); } -module.exports.NetworkEventMessage = NetworkEventMessage; +module.exports = NetworkEventMessage;
--- a/devtools/client/webconsole/new-console-output/components/message-types/page-error.js +++ b/devtools/client/webconsole/new-console-output/components/message-types/page-error.js @@ -4,97 +4,61 @@ * 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/. */ "use strict"; // React & Redux const { createFactory, - DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); -const FrameView = createFactory(require("devtools/client/shared/components/frame")); -const StackTrace = createFactory(require("devtools/client/shared/components/stack-trace")); -const CollapseButton = createFactory(require("devtools/client/webconsole/new-console-output/components/collapse-button").CollapseButton); -const MessageRepeat = createFactory(require("devtools/client/webconsole/new-console-output/components/message-repeat").MessageRepeat); -const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon); - -const actions = require("devtools/client/webconsole/new-console-output/actions/index"); +const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message")); PageError.displayName = "PageError"; PageError.propTypes = { message: PropTypes.object.isRequired, open: PropTypes.bool, }; PageError.defaultProps = { open: false }; function PageError(props) { - const { dispatch, message, open, sourceMapService, onViewSourceInDebugger } = props; - const { source, type, level, stacktrace, frame } = message; - - const repeat = MessageRepeat({repeat: message.repeat}); - const icon = MessageIcon({level}); - const shouldRenderFrame = frame && frame.source !== "debugger eval code"; - const location = dom.span({ className: "message-location devtools-monospace" }, - shouldRenderFrame ? FrameView({ - frame, - onClick: onViewSourceInDebugger, - showEmptyPathAsHost: true, - sourceMapService - }) : null - ); - - let collapse = ""; - let attachment = ""; - if (stacktrace) { - if (open) { - attachment = dom.div({ className: "stacktrace devtools-monospace" }, - StackTrace({ - stacktrace: stacktrace, - onViewSourceInDebugger: onViewSourceInDebugger - }) - ); - } + const { + message, + open, + sourceMapService, + onViewSourceInDebugger, + emitNewMessage, + } = props; + const { + id: messageId, + source, + type, + level, + messageText: messageBody, + repeat, + stacktrace, + frame + } = message; - collapse = CollapseButton({ - open, - onClick: function () { - if (open) { - dispatch(actions.messageClose(message.id)); - } else { - dispatch(actions.messageOpen(message.id)); - } - }, - }); - } - - const classes = ["message"]; - classes.push(source); - classes.push(type); - classes.push(level); - if (open === true) { - classes.push("open"); - } - - return dom.div({ - className: classes.join(" ") - }, - icon, - collapse, - dom.span({ className: "message-body-wrapper" }, - dom.span({ className: "message-flex-body" }, - dom.span({ className: "message-body devtools-monospace" }, - message.messageText - ), - repeat, - location - ), - attachment - ) - ); + const childProps = { + messageId, + open, + source, + type, + level, + topLevelClasses: [], + messageBody, + repeat, + frame, + stacktrace, + onViewSourceInDebugger, + sourceMapService, + emitNewMessage, + }; + return Message(childProps); } -module.exports.PageError = PageError; +module.exports = PageError;
new file mode 100644 --- /dev/null +++ b/devtools/client/webconsole/new-console-output/components/message.js @@ -0,0 +1,139 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +"use strict"; + +// React & Redux +const { + createClass, + createFactory, + DOM: dom, + PropTypes +} = require("devtools/client/shared/vendor/react"); +const actions = require("devtools/client/webconsole/new-console-output/actions/index"); +const CollapseButton = createFactory(require("devtools/client/webconsole/new-console-output/components/collapse-button")); +const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon")); +const MessageRepeat = createFactory(require("devtools/client/webconsole/new-console-output/components/message-repeat")); +const FrameView = createFactory(require("devtools/client/shared/components/frame")); +const StackTrace = createFactory(require("devtools/client/shared/components/stack-trace")); + +const Message = createClass({ + displayName: "Message", + + propTypes: { + open: PropTypes.bool, + source: PropTypes.string.isRequired, + type: PropTypes.string.isRequired, + level: PropTypes.string.isRequired, + topLevelClasses: PropTypes.array.isRequired, + messageBody: PropTypes.any.isRequired, + repeat: PropTypes.any, + frame: PropTypes.any, + attachment: PropTypes.any, + stacktrace: PropTypes.any, + messageId: PropTypes.string, + scrollToMessage: PropTypes.bool, + onViewSourceInDebugger: PropTypes.func, + sourceMapService: PropTypes.any, + }, + + componentDidMount() { + if (this.messageNode && this.props.emitNewMessage) { + this.props.emitNewMessage(this.messageNode); + if (this.props.scrollToMessage) { + this.messageNode.scrollIntoView(); + } + } + }, + + render() { + const { + messageId, + open, + source, + type, + level, + topLevelClasses, + messageBody, + frame, + stacktrace, + onViewSourceInDebugger, + sourceMapService, + dispatch, + } = this.props; + + topLevelClasses.push("message", source, type, level); + if (open) { + topLevelClasses.push("open"); + } + + const icon = MessageIcon({level}); + + // Figure out if there is an expandable part to the message. + let attachment = null; + if (this.props.attachment) { + attachment = this.props.attachment; + } else if (stacktrace) { + const child = open ? StackTrace({ + stacktrace: stacktrace, + onViewSourceInDebugger: onViewSourceInDebugger + }) : null; + attachment = dom.div({ className: "stacktrace devtools-monospace" }, child); + } + + // If there is an expandable part, make it collapsible. + let collapse = null; + if (attachment) { + collapse = CollapseButton({ + open, + onClick: function () { + if (open) { + dispatch(actions.messageClose(messageId)); + } else { + dispatch(actions.messageOpen(messageId)); + } + }, + }); + } + + const repeat = this.props.repeat ? MessageRepeat({repeat: this.props.repeat}) : null; + + // Configure the location. + const shouldRenderFrame = frame && frame.source !== "debugger eval code"; + const location = dom.span({ className: "message-location devtools-monospace" }, + shouldRenderFrame ? FrameView({ + frame, + onClick: onViewSourceInDebugger, + showEmptyPathAsHost: true, + sourceMapService + }) : null + ); + + return dom.div({ + className: topLevelClasses.join(" "), + ref: node => { + this.messageNode = node; + } + }, + // @TODO add timestamp + // @TODO add indent if necessary + icon, + collapse, + dom.span({ className: "message-body-wrapper" }, + dom.span({ className: "message-flex-body" }, + dom.span({ className: "message-body devtools-monospace" }, + messageBody + ), + repeat, + location + ), + attachment + ) + ); + } +}); + +module.exports = Message;
--- a/devtools/client/webconsole/new-console-output/components/moz.build +++ b/devtools/client/webconsole/new-console-output/components/moz.build @@ -12,10 +12,11 @@ DevToolsModules( 'console-output.js', 'console-table.js', 'filter-bar.js', 'filter-button.js', 'grip-message-body.js', 'message-container.js', 'message-icon.js', 'message-repeat.js', + 'message.js', 'variables-view-link.js' )
--- a/devtools/client/webconsole/new-console-output/components/variables-view-link.js +++ b/devtools/client/webconsole/new-console-output/components/variables-view-link.js @@ -26,9 +26,9 @@ function VariablesViewLink(props) { dom.a({ onClick: openVariablesView.bind(null, object), className: "cm-variable", draggable: false, }, children) ); } -module.exports.VariablesViewLink = VariablesViewLink; +module.exports = VariablesViewLink;
--- a/devtools/client/webconsole/new-console-output/constants.js +++ b/devtools/client/webconsole/new-console-output/constants.js @@ -13,16 +13,33 @@ const actionTypes = { MESSAGE_CLOSE: "MESSAGE_CLOSE", MESSAGE_TABLE_RECEIVE: "MESSAGE_TABLE_RECEIVE", FILTER_TOGGLE: "FILTER_TOGGLE", FILTER_TEXT_SET: "FILTER_TEXT_SET", FILTERS_CLEAR: "FILTERS_CLEAR", FILTER_BAR_TOGGLE: "FILTER_BAR_TOGGLE", }; +const prefs = { + PREFS: { + FILTER: { + ERROR: "devtools.webconsole.filter.error", + WARN: "devtools.webconsole.filter.warn", + INFO: "devtools.webconsole.filter.info", + LOG: "devtools.webconsole.filter.log", + DEBUG: "devtools.webconsole.filter.debug", + NET: "devtools.webconsole.filter.net", + NETXHR: "devtools.webconsole.filter.netxhr", + }, + UI: { + FILTER_BAR: "devtools.webconsole.ui.filterbar" + } + } +}; + const chromeRDPEnums = { MESSAGE_SOURCE: { XML: "xml", JAVASCRIPT: "javascript", NETWORK: "network", CONSOLE_API: "console-api", STORAGE: "storage", APPCACHE: "appcache", @@ -56,9 +73,9 @@ const chromeRDPEnums = { ERROR: "error", WARN: "warn", DEBUG: "debug", INFO: "info" } }; // Combine into a single constants object -module.exports = Object.assign({}, actionTypes, chromeRDPEnums); +module.exports = Object.assign({}, actionTypes, prefs, chromeRDPEnums);
--- a/devtools/client/webconsole/new-console-output/main.js +++ b/devtools/client/webconsole/new-console-output/main.js @@ -13,12 +13,12 @@ const { BrowserLoader } = Cu.import("res // Initialize module loader and load all modules of the new inline // preview feature. The entire code-base doesn't need any extra // privileges and runs entirely in content scope. const NewConsoleOutputWrapper = BrowserLoader({ baseURI: "resource://devtools/client/webconsole/new-console-output/", window}).require("./new-console-output-wrapper"); -this.NewConsoleOutput = function (parentNode, jsterm, toolbox, owner) { +this.NewConsoleOutput = function (parentNode, jsterm, toolbox, owner, emitNewMessage) { console.log("Creating NewConsoleOutput", parentNode, NewConsoleOutputWrapper); - return new NewConsoleOutputWrapper(parentNode, jsterm, toolbox, owner); + return new NewConsoleOutputWrapper(parentNode, jsterm, toolbox, owner, emitNewMessage); };
--- a/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js +++ b/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js @@ -10,20 +10,21 @@ const { Provider } = require("devtools/c const actions = require("devtools/client/webconsole/new-console-output/actions/index"); const { configureStore } = require("devtools/client/webconsole/new-console-output/store"); const ConsoleOutput = React.createFactory(require("devtools/client/webconsole/new-console-output/components/console-output")); const FilterBar = React.createFactory(require("devtools/client/webconsole/new-console-output/components/filter-bar")); const store = configureStore(); +let queuedActions = []; +let throttledDispatchTimeout = false; function NewConsoleOutputWrapper(parentNode, jsterm, toolbox, owner) { this.parentNode = parentNode; - this.parentNode = parentNode; this.jsterm = jsterm; this.toolbox = toolbox; this.owner = owner; this.init = this.init.bind(this); } NewConsoleOutputWrapper.prototype = { @@ -41,41 +42,51 @@ NewConsoleOutputWrapper.prototype = { openNetworkPanel: (requestId) => { return this.toolbox.selectTool("netmonitor").then(panel => { return panel.panelWin.NetMonitorController.inspectRequest(requestId); }); }, openLink: (url) => { this.owner.openLink(url); }, + emitNewMessage: (node) => { + this.jsterm.hud.emit("new-messages", new Set([{ + node + }])); + }, }); let filterBar = FilterBar({}); let provider = React.createElement( Provider, { store }, React.DOM.div( {className: "webconsole-output-wrapper"}, filterBar, childComponent )); this.body = ReactDOM.render(provider, this.parentNode); }, dispatchMessageAdd: (message) => { - store.dispatch(actions.messageAdd(message)); + batchedMessageAdd(actions.messageAdd(message)); }, dispatchMessagesAdd: (messages) => { const batchedActions = messages.map(message => actions.messageAdd(message)); store.dispatch(actions.batchActions(batchedActions)); }, dispatchMessagesClear: () => { store.dispatch(actions.messagesClear()); }, - getLastMessage: function() { - // Return the last message in the DOM as the message that was just dispatched. This may not - // always be correct in the case of filtered messages, but it's close enough for our tests. - let messageNodes = this.parentNode.querySelectorAll(".message"); - return messageNodes[messageNodes.length - 1] - }, }; +function batchedMessageAdd(action) { + queuedActions.push(action); + if (!throttledDispatchTimeout) { + throttledDispatchTimeout = setTimeout(() => { + store.dispatch(actions.batchActions(queuedActions)); + queuedActions = []; + throttledDispatchTimeout = null; + }, 50); + } +} + // Exports from this module module.exports = NewConsoleOutputWrapper;
--- a/devtools/client/webconsole/new-console-output/reducers/filters.js +++ b/devtools/client/webconsole/new-console-output/reducers/filters.js @@ -8,18 +8,18 @@ const Immutable = require("devtools/client/shared/vendor/immutable"); const constants = require("devtools/client/webconsole/new-console-output/constants"); const FilterState = Immutable.Record({ debug: true, error: true, info: true, log: true, - network: true, - netxhr: true, + net: false, + netxhr: false, text: "", warn: true, }); function filters(state = new FilterState(), action) { switch (action.type) { case constants.FILTER_TOGGLE: const {filter} = action;
--- a/devtools/client/webconsole/new-console-output/reducers/ui.js +++ b/devtools/client/webconsole/new-console-output/reducers/ui.js @@ -1,25 +1,39 @@ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * 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/. */ "use strict"; -const constants = require("devtools/client/webconsole/new-console-output/constants"); +const { + FILTER_BAR_TOGGLE, + MESSAGE_ADD, +} = require("devtools/client/webconsole/new-console-output/constants"); const Immutable = require("devtools/client/shared/vendor/immutable"); -const Ui = Immutable.Record({ +const UiState = Immutable.Record({ filterBarVisible: false, filteredMessageVisible: false, + autoscroll: true, }); -function ui(state = new Ui(), action) { +function ui(state = new UiState(), action) { + // Autoscroll should be set for all action types. If the last action was not message + // add, then turn it off. This prevents us from scrolling after someone toggles a + // filter, or to the bottom of the attachement when an expandable message at the bottom + // of the list is expanded. It does depend on the MESSAGE_ADD action being the last in + // its batch, though. + state = state.set("autoscroll", action.type == MESSAGE_ADD); + switch (action.type) { - case constants.FILTER_BAR_TOGGLE: + case FILTER_BAR_TOGGLE: return state.set("filterBarVisible", !state.filterBarVisible); } return state; } -exports.ui = ui; +module.exports = { + UiState, + ui, +};
--- a/devtools/client/webconsole/new-console-output/selectors/messages.js +++ b/devtools/client/webconsole/new-console-output/selectors/messages.js @@ -44,17 +44,17 @@ function filterLevel(messages, filters) || [MESSAGE_TYPE.COMMAND, MESSAGE_TYPE.RESULT].includes(message.type); }); } function filterNetwork(messages, filters) { return messages.filter((message) => { return ( message.source !== MESSAGE_SOURCE.NETWORK - || (filters.get("network") === true && message.isXHR === false) + || (filters.get("net") === true && message.isXHR === false) || (filters.get("netxhr") === true && message.isXHR === true) || [MESSAGE_TYPE.COMMAND, MESSAGE_TYPE.RESULT].includes(message.type) ); }); } function search(messages, text = "") { if (text === "") { @@ -94,16 +94,19 @@ function search(messages, text = "") { || (message.parameters !== null && message.parameters.join("").toLocaleLowerCase() .includes(text.toLocaleLowerCase())) ); }); } function isTextInFrame(text, frame) { + if (!frame) { + return false; + } // @TODO Change this to Object.values once it's supported in Node's version of V8 return Object.keys(frame) .map(key => frame[key]) .join(":") .toLocaleLowerCase() .includes(text.toLocaleLowerCase()); }
--- a/devtools/client/webconsole/new-console-output/selectors/ui.js +++ b/devtools/client/webconsole/new-console-output/selectors/ui.js @@ -1,12 +1,20 @@ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * 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/. */ + "use strict"; function getAllUi(state) { return state.ui; } -exports.getAllUi = getAllUi; +function getScrollSetting(state) { + return getAllUi(state).autoscroll; +} + +module.exports = { + getAllUi, + getScrollSetting, +};
--- a/devtools/client/webconsole/new-console-output/store.js +++ b/devtools/client/webconsole/new-console-output/store.js @@ -1,38 +1,45 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * 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/. */ "use strict"; const {FilterState} = require("devtools/client/webconsole/new-console-output/reducers/filters"); const {PrefState} = require("devtools/client/webconsole/new-console-output/reducers/prefs"); +const {UiState} = require("devtools/client/webconsole/new-console-output/reducers/ui"); const { applyMiddleware, combineReducers, compose, createStore } = require("devtools/client/shared/vendor/redux"); const { thunk } = require("devtools/client/shared/redux/middleware/thunk"); -const constants = require("devtools/client/webconsole/new-console-output/constants"); +const { + BATCH_ACTIONS, + PREFS, +} = require("devtools/client/webconsole/new-console-output/constants"); const { reducers } = require("./reducers/index"); const Services = require("Services"); function configureStore() { const initialState = { prefs: new PrefState({ logLimit: Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1), }), filters: new FilterState({ - error: Services.prefs.getBoolPref("devtools.webconsole.filter.error"), - warn: Services.prefs.getBoolPref("devtools.webconsole.filter.warn"), - info: Services.prefs.getBoolPref("devtools.webconsole.filter.info"), - log: Services.prefs.getBoolPref("devtools.webconsole.filter.log"), - network: Services.prefs.getBoolPref("devtools.webconsole.filter.network"), - netxhr: Services.prefs.getBoolPref("devtools.webconsole.filter.netxhr"), + error: Services.prefs.getBoolPref(PREFS.FILTER.ERROR), + warn: Services.prefs.getBoolPref(PREFS.FILTER.WARN), + info: Services.prefs.getBoolPref(PREFS.FILTER.INFO), + log: Services.prefs.getBoolPref(PREFS.FILTER.LOG), + net: Services.prefs.getBoolPref(PREFS.FILTER.NET), + netxhr: Services.prefs.getBoolPref(PREFS.FILTER.NETXHR), + }), + ui: new UiState({ + filterBarVisible: Services.prefs.getBoolPref(PREFS.UI.FILTER_BAR), }) }; return createStore( combineReducers(reducers), initialState, compose(applyMiddleware(thunk), enableBatching()) ); @@ -40,17 +47,17 @@ function configureStore() { /** * A enhancer for the store to handle batched actions. */ function enableBatching() { return next => (reducer, initialState, enhancer) => { function batchingReducer(state, action) { switch (action.type) { - case constants.BATCH_ACTIONS: + case BATCH_ACTIONS: return action.actions.reduce(batchingReducer, state); default: return reducer(state, action); } } if (typeof initialState === "function" && typeof enhancer === "undefined") { enhancer = initialState;
deleted file mode 100644 --- a/devtools/client/webconsole/new-console-output/test/actions/filters.test.js +++ /dev/null @@ -1,54 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ -"use strict"; - -const actions = require("devtools/client/webconsole/new-console-output/actions/index"); -const { - FILTER_TEXT_SET, - FILTER_TOGGLE, - FILTERS_CLEAR, - MESSAGE_LEVEL -} = require("devtools/client/webconsole/new-console-output/constants"); - -const expect = require("expect"); - -describe("Filter actions:", () => { - describe("filterTextSet", () => { - it("creates expected action", () => { - const action = actions.filterTextSet("test"); - const expected = { - type: FILTER_TEXT_SET, - text: "test" - }; - - expect(action).toEqual(expected); - }); - }); - - describe("filterToggle", () => { - it("creates expected action", () => { - const action = actions.filterToggle(MESSAGE_LEVEL.ERROR); - const expected = { - type: FILTER_TOGGLE, - filter: "error" - }; - - expect(action).toEqual(expected); - }); - }); - - describe("filterTextApply", () => { - - }); - - describe("filtersClear", () => { - it("creates expected action", () => { - const action = actions.filtersClear(); - const expected = { - type: FILTERS_CLEAR - }; - - expect(action).toEqual(expected); - }); - }); -});
deleted file mode 100644 --- a/devtools/client/webconsole/new-console-output/test/actions/messages.test.js +++ /dev/null @@ -1,88 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ -"use strict"; - -const { thunk } = require("devtools/client/shared/redux/middleware/thunk"); -const configureStore = require("redux-mock-store").default; -const { getRepeatId } = require("devtools/client/webconsole/new-console-output/utils/messages"); -const { stubPackets, stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index"); -const { setupActions } = require("devtools/client/webconsole/new-console-output/test/helpers"); -const constants = require("devtools/client/webconsole/new-console-output/constants"); - -const mockStore = configureStore([ thunk ]); - -const expect = require("expect"); - -let actions; - -describe("Message actions:", () => { - beforeEach(()=>{ - actions = setupActions(); - }); - - describe("messageAdd", () => { - it("dispatches expected action given a packet", () => { - const packet = stubPackets.get("console.log('foobar', 'test')"); - const store = mockStore({}); - store.dispatch(actions.messageAdd(packet)); - - const actualActions = store.getActions(); - expect(actualActions.length).toEqual(1); - - const addAction = actualActions[0]; - const {message} = addAction; - const expectedAction = { - type: constants.MESSAGE_ADD, - message: stubPreparedMessages.get("console.log('foobar', 'test')") - }; - expect(message.toJS()).toEqual(expectedAction.message.toJS()); - }); - - it("dispatches expected actions given a console.clear packet", () => { - const packet = stubPackets.get("console.clear()"); - const store = mockStore({}); - store.dispatch(actions.messageAdd(packet)); - - const actualActions = store.getActions(); - expect(actualActions.length).toEqual(1); - - const [clearAction, addAction] = actualActions[0].actions; - expect(clearAction.type).toEqual(constants.MESSAGES_CLEAR); - - const {message} = addAction; - const expectedAction = { - type: constants.MESSAGE_ADD, - message: stubPreparedMessages.get("console.clear()") - }; - expect(addAction.type).toEqual(constants.MESSAGE_ADD); - expect(message.toJS()).toEqual(expectedAction.message.toJS()); - }); - - it("dispatches expected action given a console.table packet", () => { - const packet = stubPackets.get("console.table(['a', 'b', 'c'])"); - const store = mockStore({}); - store.dispatch(actions.messageAdd(packet)); - - const expectedActions = store.getActions(); - expect(expectedActions.length).toEqual(1); - - const addAction = expectedActions[0]; - const {message} = addAction; - const expected = { - type: constants.MESSAGE_ADD, - message: stubPreparedMessages.get("console.table(['a', 'b', 'c'])") - }; - expect(message.toJS()).toEqual(expected.message.toJS()); - }); - }); - - describe("messagesClear", () => { - it("creates expected action", () => { - const action = actions.messagesClear(); - const expected = { - type: constants.MESSAGES_CLEAR, - }; - expect(action).toEqual(expected); - }); - }); -});
deleted file mode 100644 --- a/devtools/client/webconsole/new-console-output/test/actions/ui.test.js +++ /dev/null @@ -1,23 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ -"use strict"; - -const actions = require("devtools/client/webconsole/new-console-output/actions/index"); -const { - FILTER_BAR_TOGGLE -} = require("devtools/client/webconsole/new-console-output/constants"); - -const expect = require("expect"); - -describe("UI actions:", () => { - describe("filterBarToggle", () => { - it("creates expected action", () => { - const action = actions.filterBarToggle(); - const expected = { - type: FILTER_BAR_TOGGLE - }; - - expect(action).toEqual(expected); - }); - }); -});
--- a/devtools/client/webconsole/new-console-output/test/chrome/chrome.ini +++ b/devtools/client/webconsole/new-console-output/test/chrome/chrome.ini @@ -1,7 +1,7 @@ [DEFAULT] support-files = head.js [test_render_perf.html] -skip-if = debug +skip-if = true # Bug 1306783
--- a/devtools/client/webconsole/new-console-output/test/components/console-api-call.test.js +++ b/devtools/client/webconsole/new-console-output/test/components/console-api-call.test.js @@ -5,42 +5,48 @@ // Test utils. const expect = require("expect"); const { render } = require("enzyme"); // React const { createFactory } = require("devtools/client/shared/vendor/react"); // Components under test. -const ConsoleApiCall = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call").ConsoleApiCall); +const ConsoleApiCall = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call")); // Test fakes. const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index"); const onViewSourceInDebugger = () => {}; const tempfilePath = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js"; describe("ConsoleAPICall component:", () => { describe("console.log", () => { it("renders string grips", () => { const message = stubPreparedMessages.get("console.log('foobar', 'test')"); const wrapper = render(ConsoleApiCall({ message, onViewSourceInDebugger })); expect(wrapper.find(".message-body").text()).toBe("foobar test"); expect(wrapper.find(".objectBox-string").length).toBe(2); expect(wrapper.find("div.message.cm-s-mozilla span span.message-flex-body span.message-body.devtools-monospace").length).toBe(1); + + // There should be the location + const locationLink = wrapper.find(`.message-location`); + expect(locationLink.length).toBe(1); + expect(locationLink.text()).toBe("test-tempfile.js:1:27"); }); it("renders repeat node", () => { const message = stubPreparedMessages.get("console.log('foobar', 'test')") .set("repeat", 107); const wrapper = render(ConsoleApiCall({ message, onViewSourceInDebugger })); expect(wrapper.find(".message-repeats").text()).toBe("107"); + expect(wrapper.find(".message-repeats").prop("title")).toBe("107 repeats"); expect(wrapper.find("span > span.message-flex-body > span.message-body.devtools-monospace + span.message-repeats").length).toBe(1); }); }); describe("console.count", () => { it("renders", () => { const message = stubPreparedMessages.get("console.count('bar')");
--- a/devtools/client/webconsole/new-console-output/test/components/evaluation-result.test.js +++ b/devtools/client/webconsole/new-console-output/test/components/evaluation-result.test.js @@ -5,17 +5,17 @@ // Test utils. const expect = require("expect"); const { render } = require("enzyme"); // React const { createFactory } = require("devtools/client/shared/vendor/react"); // Components under test. -const EvaluationResult = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result").EvaluationResult); +const EvaluationResult = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result")); // Test fakes. const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index"); describe("EvaluationResult component:", () => { it("renders a grip result", () => { const message = stubPreparedMessages.get("new Date(0)"); const wrapper = render(EvaluationResult({ message }));
--- a/devtools/client/webconsole/new-console-output/test/components/filter-bar.test.js +++ b/devtools/client/webconsole/new-console-output/test/components/filter-bar.test.js @@ -4,17 +4,17 @@ const expect = require("expect"); const sinon = require("sinon"); const { render, mount } = require("enzyme"); const { createFactory } = require("devtools/client/shared/vendor/react"); const Provider = createFactory(require("react-redux").Provider); -const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button").FilterButton); +const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button")); const FilterBar = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-bar")); const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui"); const { MESSAGES_CLEAR, MESSAGE_LEVEL } = require("devtools/client/webconsole/new-console-output/constants"); const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
--- a/devtools/client/webconsole/new-console-output/test/components/filter-button.test.js +++ b/devtools/client/webconsole/new-console-output/test/components/filter-button.test.js @@ -1,49 +1,34 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const expect = require("expect"); -const sinon = require("sinon"); -const { render, shallow } = require("enzyme"); +const { render } = require("enzyme"); const { createFactory } = require("devtools/client/shared/vendor/react"); -const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button").FilterButton); -const { - FILTER_TOGGLE, - MESSAGE_LEVEL -} = require("devtools/client/webconsole/new-console-output/constants"); +const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button")); +const { MESSAGE_LEVEL } = require("devtools/client/webconsole/new-console-output/constants"); describe("FilterButton component:", () => { const props = { active: true, label: "Error", filterKey: MESSAGE_LEVEL.ERROR, - dispatch: sinon.spy() }; it("displays as active when turned on", () => { const wrapper = render(FilterButton(props)); expect(wrapper.html()).toBe( - "<button class=\"menu-filter-button checked\">Error</button>" + "<button class=\"menu-filter-button error checked\">Error</button>" ); }); it("displays as inactive when turned off", () => { const inactiveProps = Object.assign({}, props, { active: false }); const wrapper = render(FilterButton(inactiveProps)); expect(wrapper.html()).toBe( - "<button class=\"menu-filter-button\">Error</button>" + "<button class=\"menu-filter-button error\">Error</button>" ); }); - - it("fires FILTER_TOGGLE action when clicked", () => { - const wrapper = shallow(FilterButton(props)); - wrapper.find("button").simulate("click"); - const call = props.dispatch.getCall(0); - expect(call.args[0]).toEqual({ - type: FILTER_TOGGLE, - filter: MESSAGE_LEVEL.ERROR - }); - }); });
--- a/devtools/client/webconsole/new-console-output/test/components/message-container.test.js +++ b/devtools/client/webconsole/new-console-output/test/components/message-container.test.js @@ -6,19 +6,19 @@ const expect = require("expect"); const { renderComponent, shallowRenderComponent } = require("devtools/client/webconsole/new-console-output/test/helpers"); // Components under test. const { MessageContainer } = require("devtools/client/webconsole/new-console-output/components/message-container"); -const { ConsoleApiCall } = require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call"); -const { EvaluationResult } = require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result"); -const { PageError } = require("devtools/client/webconsole/new-console-output/components/message-types/page-error"); +const ConsoleApiCall = require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call"); +const EvaluationResult = require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result"); +const PageError = require("devtools/client/webconsole/new-console-output/components/message-types/page-error"); // Test fakes. const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index"); const onViewSourceInDebugger = () => {}; describe("MessageContainer component:", () => { it("pipes data to children as expected", () => { const message = stubPreparedMessages.get("console.log('foobar', 'test')");
--- a/devtools/client/webconsole/new-console-output/test/components/message-icon.test.js +++ b/devtools/client/webconsole/new-console-output/test/components/message-icon.test.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const { MESSAGE_LEVEL, } = require("devtools/client/webconsole/new-console-output/constants"); -const { MessageIcon } = require("devtools/client/webconsole/new-console-output/components/message-icon"); +const MessageIcon = require("devtools/client/webconsole/new-console-output/components/message-icon"); const expect = require("expect"); const { renderComponent } = require("devtools/client/webconsole/new-console-output/test/helpers"); describe("MessageIcon component:", () => {
rename from devtools/client/webconsole/new-console-output/test/components/repeat.test.js rename to devtools/client/webconsole/new-console-output/test/components/message-repeat.test.js --- a/devtools/client/webconsole/new-console-output/test/components/repeat.test.js +++ b/devtools/client/webconsole/new-console-output/test/components/message-repeat.test.js @@ -1,13 +1,13 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -const { MessageRepeat } = require("devtools/client/webconsole/new-console-output/components/message-repeat"); +const MessageRepeat = require("devtools/client/webconsole/new-console-output/components/message-repeat"); const expect = require("expect"); const { renderComponent } = require("devtools/client/webconsole/new-console-output/test/helpers"); describe("MessageRepeat component:", () => {
--- a/devtools/client/webconsole/new-console-output/test/components/network-event-message.test.js +++ b/devtools/client/webconsole/new-console-output/test/components/network-event-message.test.js @@ -5,17 +5,17 @@ // Test utils. const expect = require("expect"); const { render } = require("enzyme"); // React const { createFactory } = require("devtools/client/shared/vendor/react"); // Components under test. -const NetworkEventMessage = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/network-event-message").NetworkEventMessage); +const NetworkEventMessage = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/network-event-message")); // Test fakes. const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index"); const onViewSourceInDebugger = () => {}; const openNetworkPanel = () => {}; const openLink = () => {}; const EXPECTED_URL = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html";
--- a/devtools/client/webconsole/new-console-output/test/components/page-error.test.js +++ b/devtools/client/webconsole/new-console-output/test/components/page-error.test.js @@ -2,17 +2,17 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; // Test utils. const expect = require("expect"); const { render } = require("enzyme"); // Components under test. -const { PageError } = require("devtools/client/webconsole/new-console-output/components/message-types/page-error"); +const PageError = require("devtools/client/webconsole/new-console-output/components/message-types/page-error"); // Test fakes. const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index"); describe("PageError component:", () => { it("renders", () => { const message = stubPreparedMessages.get("ReferenceError: asdf is not defined"); const wrapper = render(PageError({ message }));
new file mode 100644 --- /dev/null +++ b/devtools/client/webconsole/new-console-output/test/fixtures/PluralForm.js @@ -0,0 +1,18 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +module.exports = { + PluralForm: { + get: function (occurence, str) { + // @TODO Remove when loading the actual strings from webconsole.properties + // is done in the L10n fixture. + if (str === "messageRepeats.tooltip2") { + return `${occurence} repeats`; + } + + return str; + } + } +};
--- a/devtools/client/webconsole/new-console-output/test/fixtures/Services.js +++ b/devtools/client/webconsole/new-console-output/test/fixtures/Services.js @@ -1,21 +1,27 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; +const { PREFS } = require("devtools/client/webconsole/new-console-output/constants"); + module.exports = { prefs: { getIntPref: pref => { switch (pref) { case "devtools.hud.loglimit": return 1000; } }, getBoolPref: pref => { - switch (pref) { - default: - return true; - } - } + const falsey = [ + PREFS.FILTER.NET, + PREFS.FILTER.NETXHR, + PREFS.UI.FILTER_BAR, + ]; + return !falsey.includes(pref); + }, + setBoolPref: () => {}, + clearUserPref: () => {}, } };
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini +++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini @@ -1,15 +1,17 @@ [DEFAULT] tags = devtools subsuite = devtools support-files = head.js !/devtools/client/framework/test/shared-head.js test-console-table.html test-console.html + test-console-filters.html [browser_webconsole_console_table.js] +[browser_webconsole_filters.js] [browser_webconsole_init.js] [browser_webconsole_input_focus.js] [browser_webconsole_observer_notifications.js] [browser_webconsole_vview_close_on_esc_key.js]
new file mode 100644 --- /dev/null +++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_filters.js @@ -0,0 +1,72 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests filters. + +"use strict"; + +const { MESSAGE_LEVEL } = require("devtools/client/webconsole/new-console-output/constants"); + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/test-console-filters.html"; + +add_task(function* () { + let hud = yield openNewTabAndConsole(TEST_URI); + const outputNode = hud.ui.experimentalOutputNode; + + const toolbar = yield waitFor(() => { + return outputNode.querySelector(".webconsole-filterbar-primary"); + }); + ok(toolbar, "Toolbar found"); + + // Show the filter bar + toolbar.querySelector(".devtools-filter-icon").click(); + const filterBar = yield waitFor(() => { + return outputNode.querySelector(".webconsole-filterbar-secondary"); + }); + ok(filterBar, "Filter bar is shown when filter icon is clicked."); + + // Check defaults. + Object.values(MESSAGE_LEVEL).forEach(level => { + ok(filterIsEnabled(filterBar.querySelector(`.${level}`)), + `Filter button for ${level} is on by default`); + }); + ["net", "netxhr"].forEach(category => { + ok(!filterIsEnabled(filterBar.querySelector(`.${category}`)), + `Filter button for ${category} is off by default`); + }); + + // Check that messages are shown as expected. This depends on cached messages being + // shown. + ok(findMessages(hud, "").length == 5, + "Messages of all levels shown when filters are on."); + + // Check that messages are not shown when their filter is turned off. + filterBar.querySelector(".error").click(); + yield waitFor(() => findMessages(hud, "").length == 4); + ok(true, "When a filter is turned off, its messages are not shown."); + + // Check that the ui settings were persisted. + yield closeTabAndToolbox(); + yield testFilterPersistence(); +}); + +function filterIsEnabled(button) { + return button.classList.contains("checked"); +} + +function* testFilterPersistence() { + let hud = yield openNewTabAndConsole(TEST_URI); + const outputNode = hud.ui.experimentalOutputNode; + const filterBar = yield waitFor(() => { + return outputNode.querySelector(".webconsole-filterbar-secondary"); + }); + ok(filterBar, "Filter bar ui setting is persisted."); + + // Check that the filter settings were persisted. + ok(!filterIsEnabled(filterBar.querySelector(".error")), + "Filter button setting is persisted"); + ok(findMessages(hud, "").length == 4, + "Messages of all levels shown when filters are on."); +}
--- a/devtools/client/webconsole/new-console-output/test/mochitest/head.js +++ b/devtools/client/webconsole/new-console-output/test/mochitest/head.js @@ -104,14 +104,30 @@ function* waitFor(condition, message = " * @param object hud * The web console. * @param string text * A substring that can be found in the message. * @param selector [optional] * The selector to use in finding the message. */ function findMessage(hud, text, selector = ".message") { + const elements = findMessages(hud, text, selector); + return elements.pop(); +} + +/** + * Find multiple messages in the output. + * + * @param object hud + * The web console. + * @param string text + * A substring that can be found in the message. + * @param selector [optional] + * The selector to use in finding the message. + */ +function findMessages(hud, text, selector = ".message") { + const messages = hud.ui.experimentalOutputNode.querySelectorAll(selector); const elements = Array.prototype.filter.call( - hud.ui.experimentalOutputNode.querySelectorAll(selector), + messages, (el) => el.textContent.includes(text) ); - return elements.length > 0 ? elements.pop() : false; + return elements; }
new file mode 100644 --- /dev/null +++ b/devtools/client/webconsole/new-console-output/test/mochitest/test-console-filters.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Webconsole filters test page</title> + </head> + <body> + <p>Webconsole filters test page</p> + <script> + console.log("console log"); + console.warn("console warn"); + console.error("console error"); + console.info("console info"); + console.count("console debug"); + </script> + </body> +</html>
--- a/devtools/client/webconsole/new-console-output/test/requireHelper.js +++ b/devtools/client/webconsole/new-console-output/test/requireHelper.js @@ -22,15 +22,17 @@ requireHacker.global_hook("default", pat // Some modules depend on Chrome APIs which don't work in mocha. When such a module // is required, replace it with a mock version. switch (path) { case "devtools/client/webconsole/utils": return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/WebConsoleUtils")`; case "devtools/shared/l10n": return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/LocalizationHelper")`; + case "devtools/shared/plural-form": + return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/PluralForm")`; case "Services": case "Services.default": return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/Services")`; case "devtools/shared/client/main": return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/ObjectClient")`; } });
--- a/devtools/client/webconsole/new-console-output/test/store/filters.test.js +++ b/devtools/client/webconsole/new-console-output/test/store/filters.test.js @@ -60,33 +60,33 @@ describe("Filtering", () => { expect(messages.size).toEqual(numMessages - 1); }); it("filters xhr messages", () => { let message = stubPreparedMessages.get("XHR GET request"); store.dispatch(messageAdd(message)); let messages = getAllMessages(store.getState()); - expect(messages.size).toEqual(numMessages + 1); + expect(messages.size).toEqual(numMessages); store.dispatch(actions.filterToggle("netxhr")); messages = getAllMessages(store.getState()); - expect(messages.size).toEqual(numMessages); + expect(messages.size).toEqual(numMessages + 1); }); it("filters network messages", () => { let message = stubPreparedMessages.get("GET request"); store.dispatch(messageAdd(message)); let messages = getAllMessages(store.getState()); - expect(messages.size).toEqual(numMessages + 1); + expect(messages.size).toEqual(numMessages); - store.dispatch(actions.filterToggle("network")); + store.dispatch(actions.filterToggle("net")); messages = getAllMessages(store.getState()); - expect(messages.size).toEqual(numMessages); + expect(messages.size).toEqual(numMessages + 1); }); }); describe("Text filter", () => { it("matches on value grips", () => { store.dispatch(actions.filterTextSet("danger")); let messages = getAllMessages(store.getState()); @@ -166,32 +166,32 @@ describe("Clear filters", () => { store.dispatch(actions.filterTextSet("foobar")); let filters = getAllFilters(store.getState()); expect(filters.toJS()).toEqual({ "debug": true, "error": false, "info": true, "log": true, - "network": true, - "netxhr": false, + "net": false, + "netxhr": true, "warn": true, "text": "foobar" }); store.dispatch(actions.filtersClear()); filters = getAllFilters(store.getState()); expect(filters.toJS()).toEqual({ "debug": true, "error": true, "info": true, "log": true, - "network": true, - "netxhr": true, + "net": false, + "netxhr": false, "warn": true, "text": "" }); }); }); function prepareBaseStore() { const store = setupStore([
--- a/devtools/client/webconsole/package.json +++ b/devtools/client/webconsole/package.json @@ -5,17 +5,16 @@ "amd-loader": "0.0.5", "babel-preset-es2015": "^6.6.0", "babel-register": "^6.7.2", "enzyme": "^2.4.1", "expect": "^1.16.0", "jsdom": "^9.4.1", "jsdom-global": "^2.0.0", "mocha": "^2.5.3", - "redux-mock-store": "^1.1.4", "require-hacker": "^2.1.4", "sinon": "^1.17.5" }, "scripts": { "postinstall": "cd ../ && npm install && cd webconsole", "test": "NODE_PATH=`pwd`/../../../:`pwd`/../../../devtools/client/shared/vendor/ mocha new-console-output/test/**/*.test.js --compilers js:babel-register -r jsdom-global/register -r ./new-console-output/test/requireHelper.js" } }
--- a/devtools/client/webconsole/test/browser_webconsole_bug_737873_mixedcontent.js +++ b/devtools/client/webconsole/test/browser_webconsole_bug_737873_mixedcontent.js @@ -8,30 +8,32 @@ "use strict"; const TEST_URI = "data:text/html;charset=utf8,Web Console mixed content test"; const TEST_HTTPS_URI = "https://example.com/browser/devtools/client/" + "webconsole/test/test-bug-737873-mixedcontent.html"; const LEARN_MORE_URI = "https://developer.mozilla.org/docs/Web/Security/" + "Mixed_content"; +registerCleanupFunction(function*() { + Services.prefs.clearUserPref("security.mixed_content.block_display_content"); + Services.prefs.clearUserPref("security.mixed_content.block_active_content"); +}); + add_task(function* () { Services.prefs.setBoolPref("security.mixed_content.block_display_content", false); Services.prefs.setBoolPref("security.mixed_content.block_active_content", false); yield loadTab(TEST_URI); let hud = yield openConsole(); yield testMixedContent(hud); - - Services.prefs.clearUserPref("security.mixed_content.block_display_content"); - Services.prefs.clearUserPref("security.mixed_content.block_active_content"); }); var testMixedContent = Task.async(function* (hud) { BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_HTTPS_URI); let results = yield waitForMessages({ webconsole: hud, messages: [{
--- a/devtools/client/webconsole/webconsole.js +++ b/devtools/client/webconsole/webconsole.js @@ -586,16 +586,17 @@ WebConsoleFrame.prototype = { // XXX: We should actually stop output from happening on old output // panel, but for now let's just hide it. this.experimentalOutputNode = this.outputNode.cloneNode(); this.experimentalOutputNode.removeAttribute("tabindex"); this.outputNode.hidden = true; this.outputNode.parentNode.appendChild(this.experimentalOutputNode); // @TODO Once the toolbox has been converted to React, see if passing // in JSTerm is still necessary. + this.newConsoleOutput = new this.window.NewConsoleOutput( this.experimentalOutputNode, this.jsterm, toolbox, this.owner); console.log("Created newConsoleOutput", this.newConsoleOutput); let filterToolbar = doc.querySelector(".hud-console-filter-toolbar"); filterToolbar.hidden = true; } @@ -3261,20 +3262,16 @@ WebConsoleConnectionProxy.prototype = { this.webConsoleFrame._onUpdateListeners(); }, /** * Dispatch a message add on the new frontend and emit an event for tests. */ dispatchMessageAdd: function(packet) { this.webConsoleFrame.newConsoleOutput.dispatchMessageAdd(packet); - this.webConsoleFrame.emit("new-messages", new Set([{ - response: packet, - node: this.webConsoleFrame.newConsoleOutput.getLastMessage(), - }])); }, /** * Batched dispatch of messages. */ dispatchMessagesAdd: function(packets) { this.webConsoleFrame.newConsoleOutput.dispatchMessagesAdd(packets); },
--- a/devtools/server/actors/script.js +++ b/devtools/server/actors/script.js @@ -1109,17 +1109,17 @@ const ThreadActor = ActorClassWithSpec(t * @param EventTarget eventTarget * The target the event was dispatched on. * @returns Array */ _getAllEventListeners: function (eventTarget) { let els = Cc["@mozilla.org/eventlistenerservice;1"] .getService(Ci.nsIEventListenerService); - let targets = els.getEventTargetChainFor(eventTarget); + let targets = els.getEventTargetChainFor(eventTarget, true); let listeners = []; for (let target of targets) { let handlers = els.getListenerInfoFor(target); for (let handler of handlers) { // Null is returned for all-events handlers, and native event listeners // don't provide any listenerObject, which makes them not that useful to // a JS debugger.
--- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -801,60 +801,17 @@ nsIContent::PreHandleEvent(EventChainPre parent = aVisitor.mDestInsertionPoints.LastElement(); aVisitor.mDestInsertionPoints.SetLength( aVisitor.mDestInsertionPoints.Length() - 1); } } ShadowRoot* thisShadowRoot = ShadowRoot::FromNode(this); if (thisShadowRoot) { - // The following events must always be stopped at the root node of the node tree: - // abort - // error - // select - // change - // load - // reset - // resize - // scroll - // selectstart - bool stopEvent = false; - switch (aVisitor.mEvent->mMessage) { - case eImageAbort: - case eLoadError: - case eFormSelect: - case eFormChange: - case eLoad: - case eFormReset: - case eResize: - case eScroll: - case eSelectStart: - stopEvent = true; - break; - case eUnidentifiedEvent: - if (aVisitor.mDOMEvent) { - nsAutoString eventType; - aVisitor.mDOMEvent->GetType(eventType); - if (eventType.EqualsLiteral("abort") || - eventType.EqualsLiteral("error") || - eventType.EqualsLiteral("select") || - eventType.EqualsLiteral("change") || - eventType.EqualsLiteral("load") || - eventType.EqualsLiteral("reset") || - eventType.EqualsLiteral("resize") || - eventType.EqualsLiteral("scroll")) { - stopEvent = true; - } - } - break; - default: - break; - } - - if (stopEvent) { + if (!aVisitor.mEvent->mFlags.mComposed) { // If we do stop propagation, we still want to propagate // the event to chrome (nsPIDOMWindow::GetParentTarget()). // The load event is special in that we don't ever propagate it // to chrome. nsCOMPtr<nsPIDOMWindowOuter> win = OwnerDoc()->GetWindow(); EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != eLoad ? win->GetParentTarget() : nullptr; @@ -902,17 +859,21 @@ nsIContent::PreHandleEvent(EventChainPre NS_ASSERTION(!(aVisitor.mEventTargetAtParent && insertionParent && aVisitor.mEventTargetAtParent != insertionParent), "Retargeting and having insertion parent!"); if (insertionParent) { parent = insertionParent; } } - if (parent) { + if (!aVisitor.mEvent->mFlags.mComposedInNativeAnonymousContent && + IsRootOfNativeAnonymousSubtree() && OwnerDoc() && + OwnerDoc()->GetWindow()) { + aVisitor.mParentTarget = OwnerDoc()->GetWindow()->GetParentTarget(); + } else if (parent) { aVisitor.mParentTarget = parent; } else { aVisitor.mParentTarget = GetComposedDoc(); } return NS_OK; } bool
--- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -16702,16 +16702,17 @@ class CGEventMethod(CGNativeMember): self.body = fill( """ RefPtr<${nativeType}> e = new ${nativeType}(aOwner); bool trusted = e->Init(aOwner); e->InitEvent(${eventType}, ${eventInit}.mBubbles, ${eventInit}.mCancelable); $*{members} e->SetTrusted(trusted); + e->SetComposed(${eventInit}.mComposed); $*{holdJS} return e.forget(); """, nativeType=self.descriptorProvider.nativeType.split('::')[-1], eventType=self.args[0].name, eventInit=self.args[1].name, members=members, holdJS=holdJS)
--- a/dom/bluetooth/common/webapi/BluetoothLeDeviceEvent.cpp +++ b/dom/bluetooth/common/webapi/BluetoothLeDeviceEvent.cpp @@ -102,16 +102,17 @@ BluetoothLeDeviceEvent::Constructor( scanRecord.Data()); if (!e->mScanRecord) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return nullptr; } } e->SetTrusted(trusted); + e->SetComposed(aEventInitDict.mComposed); return e.forget(); } BluetoothDevice* BluetoothLeDeviceEvent::GetDevice() const { return mDevice; }
--- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -2266,18 +2266,20 @@ CanvasRenderingContext2D::SetMozCurrentT if (ObjectToMatrix(aCx, aCurrentTransform, newCTM, aError) && newCTM.IsFinite()) { mTarget->SetTransform(newCTM); } } void CanvasRenderingContext2D::GetMozCurrentTransform(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, - ErrorResult& aError) const -{ + ErrorResult& aError) +{ + EnsureTarget(); + MatrixToJSObject(aCx, mTarget ? mTarget->GetTransform() : Matrix(), aResult, aError); } void CanvasRenderingContext2D::SetMozCurrentTransformInverse(JSContext* aCx, JS::Handle<JSObject*> aCurrentTransform, ErrorResult& aError) @@ -2295,18 +2297,20 @@ CanvasRenderingContext2D::SetMozCurrentT mTarget->SetTransform(newCTMInverse); } } } void CanvasRenderingContext2D::GetMozCurrentTransformInverse(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, - ErrorResult& aError) const -{ + ErrorResult& aError) +{ + EnsureTarget(); + if (!mTarget) { MatrixToJSObject(aCx, Matrix(), aResult, aError); return; } Matrix ctm = mTarget->GetTransform(); if (!ctm.Invert()) {
--- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -348,23 +348,23 @@ public: void Arc(double aX, double aY, double aRadius, double aStartAngle, double aEndAngle, bool aAnticlockwise, mozilla::ErrorResult& aError); void Ellipse(double aX, double aY, double aRadiusX, double aRadiusY, double aRotation, double aStartAngle, double aEndAngle, bool aAnticlockwise, ErrorResult& aError); void GetMozCurrentTransform(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, - mozilla::ErrorResult& aError) const; + mozilla::ErrorResult& aError); void SetMozCurrentTransform(JSContext* aCx, JS::Handle<JSObject*> aCurrentTransform, mozilla::ErrorResult& aError); void GetMozCurrentTransformInverse(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, - mozilla::ErrorResult& aError) const; + mozilla::ErrorResult& aError); void SetMozCurrentTransformInverse(JSContext* aCx, JS::Handle<JSObject*> aCurrentTransform, mozilla::ErrorResult& aError); void GetFillRule(nsAString& aFillRule); void SetFillRule(const nsAString& aFillRule); void GetMozDash(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval, mozilla::ErrorResult& aError); void SetMozDash(JSContext* aCx, const JS::Value& aMozDash,
--- a/dom/events/AnimationEvent.cpp +++ b/dom/events/AnimationEvent.cpp @@ -47,16 +47,17 @@ AnimationEvent::Constructor(const Global e->InitEvent(aType, aParam.mBubbles, aParam.mCancelable); InternalAnimationEvent* internalEvent = e->mEvent->AsAnimationEvent(); internalEvent->mAnimationName = aParam.mAnimationName; internalEvent->mElapsedTime = aParam.mElapsedTime; internalEvent->mPseudoElement = aParam.mPseudoElement; e->SetTrusted(trusted); + e->SetComposed(aParam.mComposed); return e.forget(); } NS_IMETHODIMP AnimationEvent::GetAnimationName(nsAString& aAnimationName) { aAnimationName = mEvent->AsAnimationEvent()->mAnimationName; return NS_OK;
--- a/dom/events/ClipboardEvent.cpp +++ b/dom/events/ClipboardEvent.cpp @@ -79,16 +79,17 @@ ClipboardEvent::Constructor(const Global Some(aGlobal.GetSubjectPrincipal()), aRv); NS_ENSURE_TRUE(!aRv.Failed(), nullptr); } } e->InitClipboardEvent(aType, aParam.mBubbles, aParam.mCancelable, clipboardData); e->SetTrusted(trusted); + e->SetComposed(aParam.mComposed); return e.forget(); } NS_IMETHODIMP ClipboardEvent::GetClipboardData(nsIDOMDataTransfer** aClipboardData) { NS_IF_ADDREF(*aClipboardData = GetClipboardData()); return NS_OK;
--- a/dom/events/CustomEvent.cpp +++ b/dom/events/CustomEvent.cpp @@ -55,16 +55,17 @@ CustomEvent::Constructor(const GlobalObj ErrorResult& aRv) { nsCOMPtr<mozilla::dom::EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports()); RefPtr<CustomEvent> e = new CustomEvent(t, nullptr, nullptr); bool trusted = e->Init(t); JS::Rooted<JS::Value> detail(aGlobal.Context(), aParam.mDetail); e->InitCustomEvent(aGlobal.Context(), aType, aParam.mBubbles, aParam.mCancelable, detail, aRv); e->SetTrusted(trusted); + e->SetComposed(aParam.mComposed); return e.forget(); } JSObject* CustomEvent::WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return mozilla::dom::CustomEventBinding::Wrap(aCx, this, aGivenProto); }
--- a/dom/events/DeviceMotionEvent.cpp +++ b/dom/events/DeviceMotionEvent.cpp @@ -94,17 +94,17 @@ DeviceMotionEvent::Constructor(const Glo e->mRotationRate = new DeviceRotationRate(e, aEventInitDict.mRotationRate.mAlpha, aEventInitDict.mRotationRate.mBeta, aEventInitDict.mRotationRate.mGamma); e->mInterval = aEventInitDict.mInterval; e->SetTrusted(trusted); - + e->SetComposed(aEventInitDict.mComposed); return e.forget(); } /****************************************************************************** * DeviceAcceleration *****************************************************************************/ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DeviceAcceleration, mOwner)
--- a/dom/events/DragEvent.cpp +++ b/dom/events/DragEvent.cpp @@ -106,16 +106,17 @@ DragEvent::Constructor(const GlobalObjec e->InitDragEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView, aParam.mDetail, aParam.mScreenX, aParam.mScreenY, aParam.mClientX, aParam.mClientY, aParam.mCtrlKey, aParam.mAltKey, aParam.mShiftKey, aParam.mMetaKey, aParam.mButton, aParam.mRelatedTarget, aParam.mDataTransfer); e->InitializeExtraMouseEventDictionaryMembers(aParam); e->SetTrusted(trusted); + e->SetComposed(aParam.mComposed); return e.forget(); } } // namespace dom } // namespace mozilla using namespace mozilla; using namespace mozilla::dom;
--- a/dom/events/Event.cpp +++ b/dom/events/Event.cpp @@ -410,16 +410,17 @@ Event::Constructor(const GlobalObject& a const EventInit& aParam, ErrorResult& aRv) { nsCOMPtr<mozilla::dom::EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports()); RefPtr<Event> e = new Event(t, nullptr, nullptr); bool trusted = e->Init(t); e->InitEvent(aType, aParam.mBubbles, aParam.mCancelable); e->SetTrusted(trusted); + e->SetComposed(aParam.mComposed); return e.forget(); } uint16_t Event::EventPhase() const { // Note, remember to check that this works also // if or when Bug 235441 is fixed. @@ -562,21 +563,24 @@ Event::PreventDefaultInternal(bool aCall void Event::SetEventType(const nsAString& aEventTypeArg) { if (mIsMainThreadEvent) { mEvent->mSpecifiedEventTypeString.Truncate(); mEvent->mSpecifiedEventType = nsContentUtils::GetEventMessageAndAtom(aEventTypeArg, mEvent->mClass, &(mEvent->mMessage)); + mEvent->SetDefaultComposed(); } else { mEvent->mSpecifiedEventType = nullptr; mEvent->mMessage = eUnidentifiedEvent; mEvent->mSpecifiedEventTypeString = aEventTypeArg; + mEvent->SetComposed(aEventTypeArg); } + mEvent->SetDefaultComposedInNativeAnonymousContent(); } void Event::InitEvent(const nsAString& aEventTypeArg, bool aCanBubbleArg, bool aCancelableArg) { // Make sure this event isn't already being dispatched. @@ -1162,16 +1166,17 @@ Event::Serialize(IPC::Message* aMsg, boo nsString type; GetType(type); IPC::WriteParam(aMsg, type); IPC::WriteParam(aMsg, Bubbles()); IPC::WriteParam(aMsg, Cancelable()); IPC::WriteParam(aMsg, IsTrusted()); + IPC::WriteParam(aMsg, Composed()); // No timestamp serialization for now! } NS_IMETHODIMP_(bool) Event::Deserialize(const IPC::Message* aMsg, PickleIterator* aIter) { nsString type; @@ -1181,18 +1186,22 @@ Event::Deserialize(const IPC::Message* a NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &bubbles), false); bool cancelable = false; NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &cancelable), false); bool trusted = false; NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &trusted), false); + bool composed = false; + NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &composed), false); + InitEvent(type, bubbles, cancelable); SetTrusted(trusted); + SetComposed(composed); return true; } NS_IMETHODIMP_(void) Event::SetOwner(mozilla::dom::EventTarget* aOwner) { mOwner = nullptr;
--- a/dom/events/Event.h +++ b/dom/events/Event.h @@ -165,16 +165,21 @@ public: return mEvent->mFlags.mBubbles; } bool Cancelable() const { return mEvent->mFlags.mCancelable; } + bool Composed() const + { + return mEvent->mFlags.mComposed; + } + // xpidl implementation // void PreventDefault(); // You MUST NOT call PreventDefaultJ(JSContext*) from C++ code. A call of // this method always sets Event.defaultPrevented true for web contents. // If default action handler calls this, web applications meet wrong // defaultPrevented value. virtual void PreventDefault(JSContext* aCx); @@ -269,16 +274,21 @@ protected: } /** * IsChrome() returns true if aCx is chrome context or the event is created * in chrome's thread. Otherwise, false. */ bool IsChrome(JSContext* aCx) const; + void SetComposed(bool aComposed) + { + mEvent->SetComposed(aComposed); + } + mozilla::WidgetEvent* mEvent; RefPtr<nsPresContext> mPresContext; nsCOMPtr<EventTarget> mExplicitOriginalTarget; nsCOMPtr<nsIGlobalObject> mOwner; bool mEventIsInternal; bool mPrivateDataDuplicated; bool mIsMainThreadEvent; // True when popup control check should rely on event.type, not
--- a/dom/events/EventListenerService.cpp +++ b/dom/events/EventListenerService.cpp @@ -215,23 +215,25 @@ EventListenerService::GetListenerInfoFor NS_ADDREF((*aOutArray)[i] = listenerInfos[i]); } *aCount = count; return NS_OK; } NS_IMETHODIMP EventListenerService::GetEventTargetChainFor(nsIDOMEventTarget* aEventTarget, + bool aComposed, uint32_t* aCount, nsIDOMEventTarget*** aOutArray) { *aCount = 0; *aOutArray = nullptr; NS_ENSURE_ARG(aEventTarget); WidgetEvent event(true, eVoidEvent); + event.SetComposed(aComposed); nsTArray<EventTarget*> targets; nsresult rv = EventDispatcher::Dispatch(aEventTarget, nullptr, &event, nullptr, nullptr, nullptr, &targets); NS_ENSURE_SUCCESS(rv, rv); int32_t count = targets.Length(); if (count == 0) { return NS_OK; }
--- a/dom/events/FocusEvent.cpp +++ b/dom/events/FocusEvent.cpp @@ -60,16 +60,17 @@ FocusEvent::Constructor(const GlobalObje ErrorResult& aRv) { nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports()); RefPtr<FocusEvent> e = new FocusEvent(t, nullptr, nullptr); bool trusted = e->Init(t); e->InitFocusEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView, aParam.mDetail, aParam.mRelatedTarget); e->SetTrusted(trusted); + e->SetComposed(aParam.mComposed); return e.forget(); } } // namespace dom } // namespace mozilla using namespace mozilla; using namespace mozilla::dom;
--- a/dom/events/InputEvent.cpp +++ b/dom/events/InputEvent.cpp @@ -51,16 +51,17 @@ InputEvent::Constructor(const GlobalObje RefPtr<InputEvent> e = new InputEvent(t, nullptr, nullptr); bool trusted = e->Init(t); auto* view = aParam.mView ? aParam.mView->AsInner() : nullptr; e->InitUIEvent(aType, aParam.mBubbles, aParam.mCancelable, view, aParam.mDetail); InternalEditorInputEvent* internalEvent = e->mEvent->AsEditorInputEvent(); internalEvent->mIsComposing = aParam.mIsComposing; e->SetTrusted(trusted); + e->SetComposed(aParam.mComposed); return e.forget(); } } // namespace dom } // namespace mozilla using namespace mozilla; using namespace mozilla::dom;
--- a/dom/events/MouseEvent.cpp +++ b/dom/events/MouseEvent.cpp @@ -182,17 +182,17 @@ MouseEvent::Constructor(const GlobalObje bool trusted = e->Init(t); e->InitMouseEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView, aParam.mDetail, aParam.mScreenX, aParam.mScreenY, aParam.mClientX, aParam.mClientY, aParam.mCtrlKey, aParam.mAltKey, aParam.mShiftKey, aParam.mMetaKey, aParam.mButton, aParam.mRelatedTarget); e->InitializeExtraMouseEventDictionaryMembers(aParam); e->SetTrusted(trusted); - + e->SetComposed(aParam.mComposed); return e.forget(); } void MouseEvent::InitNSMouseEvent(const nsAString& aType, bool aCanBubble, bool aCancelable, nsGlobalWindow* aView,
--- a/dom/events/PointerEvent.cpp +++ b/dom/events/PointerEvent.cpp @@ -95,16 +95,17 @@ PointerEvent::Constructor(EventTarget* a widgetEvent->pressure = aParam.mPressure; widgetEvent->tiltX = aParam.mTiltX; widgetEvent->tiltY = aParam.mTiltY; widgetEvent->inputSource = ConvertStringToPointerType(aParam.mPointerType); widgetEvent->mIsPrimary = aParam.mIsPrimary; widgetEvent->buttons = aParam.mButtons; e->SetTrusted(trusted); + e->SetComposed(aParam.mComposed); return e.forget(); } // static already_AddRefed<PointerEvent> PointerEvent::Constructor(const GlobalObject& aGlobal, const nsAString& aType, const PointerEventInit& aParam,
--- a/dom/events/SpeechRecognitionError.cpp +++ b/dom/events/SpeechRecognitionError.cpp @@ -26,16 +26,17 @@ SpeechRecognitionError::Constructor(cons const SpeechRecognitionErrorInit& aParam, ErrorResult& aRv) { nsCOMPtr<mozilla::dom::EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports()); RefPtr<SpeechRecognitionError> e = new SpeechRecognitionError(t, nullptr, nullptr); bool trusted = e->Init(t); e->InitSpeechRecognitionError(aType, aParam.mBubbles, aParam.mCancelable, aParam.mError, aParam.mMessage); e->SetTrusted(trusted); + e->SetComposed(aParam.mComposed); return e.forget(); } void SpeechRecognitionError::InitSpeechRecognitionError(const nsAString& aType, bool aCanBubble, bool aCancelable, SpeechRecognitionErrorCode aError,
--- a/dom/events/StorageEvent.cpp +++ b/dom/events/StorageEvent.cpp @@ -60,17 +60,17 @@ StorageEvent::Constructor(EventTarget* a bool trusted = e->Init(aOwner); e->InitEvent(aType, aEventInitDict.mBubbles, aEventInitDict.mCancelable); e->mKey = aEventInitDict.mKey; e->mOldValue = aEventInitDict.mOldValue; e->mNewValue = aEventInitDict.mNewValue; e->mUrl = aEventInitDict.mUrl; e->mStorageArea = aEventInitDict.mStorageArea; e->SetTrusted(trusted); - + e->SetComposed(aEventInitDict.mComposed); return e.forget(); } already_AddRefed<StorageEvent> StorageEvent::Constructor(const GlobalObject& aGlobal, const nsAString& aType, const StorageEventInit& aEventInitDict, ErrorResult& aRv)
--- a/dom/events/TouchEvent.cpp +++ b/dom/events/TouchEvent.cpp @@ -240,16 +240,17 @@ TouchEvent::Constructor(const GlobalObje RefPtr<TouchList> touches = e->CopyTouches(aParam.mTouches); RefPtr<TouchList> targetTouches = e->CopyTouches(aParam.mTargetTouches); RefPtr<TouchList> changedTouches = e->CopyTouches(aParam.mChangedTouches); e->InitTouchEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView, aParam.mDetail, aParam.mCtrlKey, aParam.mAltKey, aParam.mShiftKey, aParam.mMetaKey, touches, targetTouches, changedTouches); e->SetTrusted(trusted); + e->SetComposed(aParam.mComposed); return e.forget(); } already_AddRefed<TouchList> TouchEvent::CopyTouches(const Sequence<OwningNonNull<Touch>>& aTouches) { RefPtr<TouchList> list = new TouchList(GetParentObject());
--- a/dom/events/TransitionEvent.cpp +++ b/dom/events/TransitionEvent.cpp @@ -47,16 +47,17 @@ TransitionEvent::Constructor(const Globa e->InitEvent(aType, aParam.mBubbles, aParam.mCancelable); InternalTransitionEvent* internalEvent = e->mEvent->AsTransitionEvent(); internalEvent->mPropertyName = aParam.mPropertyName; internalEvent->mElapsedTime = aParam.mElapsedTime; internalEvent->mPseudoElement = aParam.mPseudoElement; e->SetTrusted(trusted); + e->SetComposed(aParam.mComposed); return e.forget(); } NS_IMETHODIMP TransitionEvent::GetPropertyName(nsAString& aPropertyName) { aPropertyName = mEvent->AsTransitionEvent()->mPropertyName; return NS_OK;
--- a/dom/events/UIEvent.cpp +++ b/dom/events/UIEvent.cpp @@ -85,16 +85,17 @@ UIEvent::Constructor(const GlobalObject& ErrorResult& aRv) { nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports()); RefPtr<UIEvent> e = new UIEvent(t, nullptr, nullptr); bool trusted = e->Init(t); e->InitUIEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView, aParam.mDetail); e->SetTrusted(trusted); + e->SetComposed(aParam.mComposed); return e.forget(); } NS_IMPL_CYCLE_COLLECTION_INHERITED(UIEvent, Event, mView) NS_IMPL_ADDREF_INHERITED(UIEvent, Event) NS_IMPL_RELEASE_INHERITED(UIEvent, Event)
--- a/dom/events/WheelEvent.cpp +++ b/dom/events/WheelEvent.cpp @@ -120,16 +120,17 @@ WheelEvent::Constructor(const GlobalObje aParam.mView, aParam.mDetail, aParam.mScreenX, aParam.mScreenY, aParam.mClientX, aParam.mClientY, aParam.mButton, aParam.mRelatedTarget, EmptyString(), aParam.mDeltaX, aParam.mDeltaY, aParam.mDeltaZ, aParam.mDeltaMode); e->InitializeExtraMouseEventDictionaryMembers(aParam); e->SetTrusted(trusted); + e->SetComposed(aParam.mComposed); return e.forget(); } } // namespace dom } // namespace mozilla using namespace mozilla; using namespace mozilla::dom;
--- a/dom/events/nsIEventListenerService.idl +++ b/dom/events/nsIEventListenerService.idl @@ -73,16 +73,17 @@ interface nsIEventListenerService : nsIS * Returns an array of event targets. * aEventTarget will be at index 0. * The objects are the ones that would be used as DOMEvent.currentTarget while * dispatching an event to aEventTarget * @note Some events, especially 'load', may actually have a shorter * event target chain than what this methods returns. */ void getEventTargetChainFor(in nsIDOMEventTarget aEventTarget, + in boolean composed, [optional] out unsigned long aCount, [retval, array, size_is(aCount)] out nsIDOMEventTarget aOutArray); /** * Returns true if a event target has any listener for the given type. */ boolean hasListenersFor(in nsIDOMEventTarget aEventTarget,
--- a/dom/events/test/pointerevents/mochitest.ini +++ b/dom/events/test/pointerevents/mochitest.ini @@ -30,17 +30,16 @@ support-files = support-files = pointerevent_lostpointercapture_is_first-manual.html [test_pointerevent_multiple_primary_pointers_boundary_events-manual.html] support-files = pointerevent_multiple_primary_pointers_boundary_events-manual.html disabled = should be investigated [test_pointerevent_pointercancel_touch-manual.html] support-files = pointerevent_pointercancel_touch-manual.html [test_pointerevent_pointerdown-manual.html] support-files = pointerevent_pointerdown-manual.html - disabled = should be investigated [test_pointerevent_pointerenter_does_not_bubble-manual.html] support-files = pointerevent_pointerenter_does_not_bubble-manual.html [test_pointerevent_pointerenter_nohover-manual.html] support-files = pointerevent_pointerenter_nohover-manual.html [test_pointerevent_pointerId_scope-manual.html] support-files = test_pointerevent_pointerId_scope-manual.html ./resources/pointerevent_pointerId_scope-iframe.html @@ -86,17 +85,16 @@ support-files = [test_pointerevent_pointertype_mouse-manual.html] support-files = pointerevent_pointertype_mouse-manual.html [test_pointerevent_pointertype_pen-manual.html] support-files = pointerevent_pointertype_pen-manual.html [test_pointerevent_pointertype_touch-manual.html] support-files = pointerevent_pointertype_touch-manual.html [test_pointerevent_pointerup-manual.html] support-files = pointerevent_pointerup-manual.html - disabled = should be investigated [test_pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html] support-files = pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html [test_pointerevent_pointerup_pointertype-manual.html] support-files = pointerevent_pointerup_pointertype-manual.html [test_pointerevent_releasepointercapture_events_to_original_target-manual.html] support-files = pointerevent_releasepointercapture_events_to_original_target-manual.html [test_pointerevent_releasepointercapture_invalid_pointerid-manual.html] support-files = pointerevent_releasepointercapture_invalid_pointerid-manual.html
--- a/dom/events/test/test_bug448602.html +++ b/dom/events/test/test_bug448602.html @@ -89,17 +89,17 @@ function runTests() { is(infos[0].allowsUntrusted, true, "Should allow untrusted events (3)"); is(SpecialPowers.unwrap(infos[0].listenerObject), l, "Should have the right listener object (4)"); // Event target chain tests l2 = document.getElementById("testlevel2"); l3 = document.getElementById("testlevel3"); var textnode = l3.firstChild; - var chain = els.getEventTargetChainFor(textnode, {}); + var chain = els.getEventTargetChainFor(textnode, true, {}); ok(chain.length > 3, "Too short event target chain."); ok(SpecialPowers.compare(chain[0], textnode), "Wrong chain item (1)"); ok(SpecialPowers.compare(chain[1], l3), "Wrong chain item (2)"); ok(SpecialPowers.compare(chain[2], l2), "Wrong chain item (3)"); ok(SpecialPowers.compare(chain[3], root), "Wrong chain item (4)"); var hasDocumentInChain = false; var hasWindowInChain = false; @@ -162,24 +162,24 @@ function testAllListener() { if (infos[i].type == null) { ++nullTypes; } } is(nullTypes, 1, "Should have one all-event-listener!"); els.addListenerForAllEvents(root, allListener, false, true, true); els.addListenerForAllEvents(root, allListenerTrustedOnly, false, false, true); - l3.dispatchEvent(new Event("testevent", { bubbles: true })); - dispatchTrusted(l3, { bubbles: true }); + l3.dispatchEvent(new Event("testevent", { bubbles: true, composed: true })); + dispatchTrusted(l3, { bubbles: true, composed: true }); els.removeListenerForAllEvents(root, allListener, false); els.removeListenerForAllEvents(root, allListener, false, true); els.removeListenerForAllEvents(root, allListenerTrustedOnly, false, true); // make sure removeListenerForAllEvents works. - l3.dispatchEvent(new Event("testevent", { bubbles: true })); - dispatchTrusted(l3, { bubbles: true }); + l3.dispatchEvent(new Event("testevent", { bubbles: true, composed : true })); + dispatchTrusted(l3, { bubbles: true, composed: true }); // Test the order of event listeners. var clickListenerCalled = false; var allListenerCalled = false; function clickListener() { clickListenerCalled = true; ok(allListenerCalled, "Should have called '*' listener before normal listener!"); }
--- a/dom/media/eme/MediaKeyMessageEvent.cpp +++ b/dom/media/eme/MediaKeyMessageEvent.cpp @@ -95,16 +95,17 @@ MediaKeyMessageEvent::Constructor(const } e->mMessage = ArrayBuffer::Create(aGlobal.Context(), length, data); if (!e->mMessage) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return nullptr; } e->mMessageType = aEventInitDict.mMessageType; e->SetTrusted(trusted); + e->SetComposed(aEventInitDict.mComposed); return e.forget(); } void MediaKeyMessageEvent::GetMessage(JSContext* cx, JS::MutableHandle<JSObject*> aMessage, ErrorResult& aRv) {
--- a/dom/notification/NotificationEvent.h +++ b/dom/notification/NotificationEvent.h @@ -38,16 +38,17 @@ public: const nsAString& aType, const NotificationEventInit& aOptions, ErrorResult& aRv) { RefPtr<NotificationEvent> e = new NotificationEvent(aOwner); bool trusted = e->Init(aOwner); e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable); e->SetTrusted(trusted); + e->SetComposed(aOptions.mComposed); e->mNotification = aOptions.mNotification; e->SetWantsPopupControlCheck(e->IsTrusted()); return e.forget(); } static already_AddRefed<NotificationEvent> Constructor(const GlobalObject& aGlobal, const nsAString& aType,
--- a/dom/tests/mochitest/webcomponents/test_event_dispatch.html +++ b/dom/tests/mochitest/webcomponents/test_event_dispatch.html @@ -22,17 +22,17 @@ function eventListener(e) { function isEventChain(actual, expected, msg) { is(actual.length, expected.length, msg); for (var i = 0; i < expected.length; i++) { is(actual[i], expected[i], msg + " at " + i); } // Check to make sure the event chain matches what we get back from nsIEventListenerService.getEventTargetChainFor if (0 < actual.length) { - var chain = els.getEventTargetChainFor(actual[0]); // Events should be dispatched on actual[0]. + var chain = els.getEventTargetChainFor(actual[0], true); // Events should be dispatched on actual[0]. for (var i = 0; i < expected.length; i++) { ok(SpecialPowers.compare(chain[i], expected[i]), msg + " at " + i + " for nsIEventListenerService"); } } } /* * Test 1: Test of event dispatch through a basic ShadowRoot with content a insertion point. @@ -59,17 +59,17 @@ elemFour.addEventListener("custom", even var shadowOne = elemOne.createShadowRoot(); shadowOne.addEventListener("custom", eventListener); elemThree.appendChild(elemFour); shadowOne.appendChild(elemThree); elemOne.appendChild(elemTwo); var eventChain = []; -var customEvent = new CustomEvent("custom", { "bubbles" : true }); +var customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); elemTwo.dispatchEvent(customEvent); isEventChain(eventChain, [elemTwo, elemFour, elemThree, shadowOne, elemOne], "Event path for test 1 for event dispatched on elemTwo."); /* * Test 2: Test of event dispatch through a nested ShadowRoots with content insertion points. * * <div elemFive> --- <shadow-root shadowTwo> * | | @@ -106,17 +106,17 @@ shadowTwo.addEventListener("custom", eve elemFive.appendChild(elemOne); shadowTwo.appendChild(elemFour); elemFour.appendChild(elemTwo); shadowOne.appendChild(elemSix); elemSix.appendChild(elemThree); eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true }); +customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); elemOne.dispatchEvent(customEvent); is(elemOne.getDestinationInsertionPoints().length, 2, "yes"); isEventChain(eventChain, [elemOne, elemThree, elemSix, shadowOne, elemTwo, elemFour, shadowTwo, elemFive], "Event path for test 2 for event dispatched on elemOne."); /* * Test 3: Test of event dispatch through nested ShadowRoot with content insertion points. * * <div elemOne> ------- <shadow-root shadowOne> @@ -154,17 +154,17 @@ shadowTwo.addEventListener("custom", eve elemOne.appendChild(elemTwo); shadowOne.appendChild(elemThree); elemThree.appendChild(elemFour); elemFour.appendChild(elemFive); shadowTwo.appendChild(elemSix); eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true }); +customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); elemTwo.dispatchEvent(customEvent); isEventChain(eventChain, [elemTwo, elemFive, elemFour, elemSix, shadowTwo, elemThree, shadowOne, elemOne], "Event path for test 3 for event dispatched on elemTwo."); /* * Test 4: Test of event dispatch through host with multiple ShadowRoots with shadow insertion point. * * <div elemSeven> --- <shadow-root shadowTwo> (younger ShadowRoot) * | | | @@ -218,22 +218,22 @@ elemSeven.appendChild(elemOne); shadowTwo.appendChild(elemSix); elemSix.appendChild(elemFour); elemFour.appendChild(elemTwo); shadowThree.appendChild(elemEight); shadowThree.appendChild(elemThree); shadowOne.appendChild(elemFive); eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true }); +customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); elemOne.dispatchEvent(customEvent); isEventChain(eventChain, [elemOne, elemFive, shadowOne, elemThree, shadowThree, elemTwo, elemFour, elemSix, shadowTwo, elemSeven], "Event path for test 4 for event dispatched on elemOne."); eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true }); +customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); elemEight.dispatchEvent(customEvent); isEventChain(eventChain, [elemEight, elemFive, shadowOne, elemSix, shadowTwo, elemSeven], "Event path for test 4 for event dispatched on elemEight."); /* * Test 5: Test of event dispatch through nested shadowroot with insertion points that match specific tags. * * <div elemOne> --------- <shadow-root shadowOne> * | | | @@ -276,22 +276,22 @@ shadowOne.addEventListener("custom", eve elemOne.appendChild(elemTwo); elemOne.appendChild(elemThree); shadowOne.appendChild(elemFour); elemFour.appendChild(elemSix); elemFour.appendChild(elemFive); shadowTwo.appendChild(elemSeven); eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true }); +customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); elemTwo.dispatchEvent(customEvent); isEventChain(eventChain, [elemTwo, elemSeven, shadowTwo, elemSix, elemFour, shadowOne, elemOne], "Event path for test 5 for event dispatched on elemTwo."); eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true }); +customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); elemThree.dispatchEvent(customEvent); isEventChain(eventChain, [elemThree, elemSeven, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 5 for event dispatched on elemThree."); /* * Test 6: Test of event dispatch through nested shadowroot with insertion points that match specific tags. * * <div elemOne> --------- <shadow-root shadowOne>; * | | | @@ -334,22 +334,22 @@ shadowOne.addEventListener("custom", eve elemOne.appendChild(elemTwo); elemOne.appendChild(elemThree); shadowOne.appendChild(elemFour); elemFour.appendChild(elemFive); shadowTwo.appendChild(elemSix); shadowTwo.appendChild(elemSeven); eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true }); +customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); elemTwo.dispatchEvent(customEvent); isEventChain(eventChain, [elemTwo, elemSix, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 6 for event dispatched on elemTwo."); eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true }); +customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); elemThree.dispatchEvent(customEvent); isEventChain(eventChain, [elemThree, elemSeven, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 6 for event dispatched on elemThree."); /* * Test 7: Test of event dispatch through nested shadowroot with insertion points that match specific tags. * * <div elemOne> --------- <shadow-root shadowOne> * | | | @@ -398,22 +398,22 @@ elemOne.appendChild(elemTwo); elemOne.appendChild(elemThree); shadowOne.appendChild(elemFour); elemFour.appendChild(elemFive); shadowTwo.appendChild(elemEight); elemEight.appendChild(elemSix); elemEight.appendChild(elemSeven); eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true }); +customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); elemTwo.dispatchEvent(customEvent); isEventChain(eventChain, [elemTwo, elemSix, elemEight, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 7 for event dispatched on elemTwo."); eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true }); +customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); elemThree.dispatchEvent(customEvent); isEventChain(eventChain, [elemThree, elemSeven, elemEight, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 7 for event dispatched on elemThree."); /* * Test 8: Test of event dispatch through host with multiple ShadowRoots with shadow insertion point. * * <div elemOne> --- <shadow-root shadowOne> (younger ShadowRoot) * | | @@ -444,15 +444,15 @@ shadowTwo.addEventListener("custom", eve shadowOne = elemOne.createShadowRoot(); shadowOne.addEventListener("custom", eventListener); shadowOne.appendChild(elemFour); elemFour.appendChild(elemTwo); shadowTwo.appendChild(elemThree); eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true }); +customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); elemThree.dispatchEvent(customEvent); isEventChain(eventChain, [elemThree, shadowTwo, elemTwo, elemFour, shadowOne, elemOne], "Event path for test 8 for event dispatched on elemThree."); </script> </body> </html>
--- a/dom/tests/mochitest/webcomponents/test_event_stopping.html +++ b/dom/tests/mochitest/webcomponents/test_event_stopping.html @@ -21,17 +21,17 @@ function eventListener(e) { function isEventChain(actual, expected, msg) { is(actual.length, expected.length, msg); for (var i = 0; i < expected.length; i++) { is(actual[i], expected[i], msg + " at " + i); } if (0 < actual.length) { - var chain = els.getEventTargetChainFor(actual[0]); // Events should be dispatched on actual[0]. + var chain = els.getEventTargetChainFor(actual[0], false); // Events should be dispatched on actual[0]. ok(expected.length < chain.length, "There should be additional chrome event targets."); } } /* * <div elemOne> ------ <shadow-root shadowOne> * | * <span elemTwo>
--- a/dom/webidl/Event.webidl +++ b/dom/webidl/Event.webidl @@ -36,16 +36,18 @@ interface Event { readonly attribute boolean cancelable; void preventDefault(); [Pure] readonly attribute boolean defaultPrevented; [ChromeOnly, Pure] readonly attribute boolean defaultPreventedByChrome; [ChromeOnly, Pure] readonly attribute boolean defaultPreventedByContent; + [Pure] + readonly attribute boolean composed; [Unforgeable, Pure] readonly attribute boolean isTrusted; [Pure] readonly attribute DOMHighResTimeStamp timeStamp; void initEvent(DOMString type, boolean bubbles, boolean cancelable); }; @@ -64,9 +66,10 @@ partial interface Event { [ChromeOnly] readonly attribute boolean isSynthesized; boolean getPreventDefault(); }; dictionary EventInit { boolean bubbles = false; boolean cancelable = false; + boolean composed = false; };
--- a/dom/workers/ServiceWorkerEvents.cpp +++ b/dom/workers/ServiceWorkerEvents.cpp @@ -144,16 +144,17 @@ FetchEvent::Constructor(const GlobalObje ErrorResult& aRv) { RefPtr<EventTarget> owner = do_QueryObject(aGlobal.GetAsSupports()); MOZ_ASSERT(owner); RefPtr<FetchEvent> e = new FetchEvent(owner); bool trusted = e->Init(owner); e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable); e->SetTrusted(trusted); + e->SetComposed(aOptions.mComposed); e->mRequest = aOptions.mRequest; e->mClientId = aOptions.mClientId; e->mIsReload = aOptions.mIsReload; return e.forget(); } namespace { @@ -1136,16 +1137,17 @@ PushEvent::Constructor(mozilla::dom::Eve const nsAString& aType, const PushEventInit& aOptions, ErrorResult& aRv) { RefPtr<PushEvent> e = new PushEvent(aOwner); bool trusted = e->Init(aOwner); e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable); e->SetTrusted(trusted); + e->SetComposed(aOptions.mComposed); if(aOptions.mData.WasPassed()){ nsTArray<uint8_t> bytes; nsresult rv = ExtractBytesFromData(aOptions.mData.Value(), bytes); if (NS_FAILED(rv)) { aRv.Throw(rv); return nullptr; } e->mData = new PushMessageData(aOwner, Move(bytes));
--- a/dom/workers/ServiceWorkerEvents.h +++ b/dom/workers/ServiceWorkerEvents.h @@ -72,16 +72,17 @@ public: Constructor(mozilla::dom::EventTarget* aOwner, const nsAString& aType, const EventInit& aOptions) { RefPtr<ExtendableEvent> e = new ExtendableEvent(aOwner); bool trusted = e->Init(aOwner); e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable); e->SetTrusted(trusted); + e->SetComposed(aOptions.mComposed); return e.forget(); } static already_AddRefed<ExtendableEvent> Constructor(const GlobalObject& aGlobal, const nsAString& aType, const EventInit& aOptions, ErrorResult& aRv)
--- a/gfx/harfbuzz/README-mozilla +++ b/gfx/harfbuzz/README-mozilla @@ -1,15 +1,16 @@ gfx/harfbuzz status as of 2016-08-21: This directory contains the harfbuzz source from the 'master' branch of https://github.com/behdad/harfbuzz. Current version: 1.3.0 + recent fixes from trunk, -up to 547ddb0721365dca985aef5b759d08718f7c5f82. +up to 547ddb0721365dca985aef5b759d08718f7c5f82 ++ the relevant part of https://github.com/behdad/harfbuzz/pull/334. UPDATING: Note that gfx/harfbuzz/src/hb-version.h is not present in the upstream Git repository. It is created at build time by the harfbuzz build system; but as we don't use that build system in mozilla, it is necessary to refresh this file when updating harfbuzz, and check it into the mozilla tree.
--- a/gfx/harfbuzz/src/hb-glib.cc +++ b/gfx/harfbuzz/src/hb-glib.cc @@ -377,24 +377,26 @@ hb_glib_get_unicode_funcs (void) HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS #undef HB_UNICODE_FUNC_IMPLEMENT } }; return const_cast<hb_unicode_funcs_t *> (&_hb_glib_unicode_funcs); } +#if GLIB_CHECK_VERSION(2,31,10) /** * hb_glib_blob_create: * * Since: 0.9.38 **/ hb_blob_t * hb_glib_blob_create (GBytes *gbytes) { gsize size = 0; gconstpointer data = g_bytes_get_data (gbytes, &size); return hb_blob_create ((const char *) data, size, HB_MEMORY_MODE_READONLY, g_bytes_ref (gbytes), (hb_destroy_func_t) g_bytes_unref); } +#endif
--- a/gfx/harfbuzz/src/hb-glib.h +++ b/gfx/harfbuzz/src/hb-glib.h @@ -41,15 +41,16 @@ hb_glib_script_to_script (GUnicodeScript HB_EXTERN GUnicodeScript hb_glib_script_from_script (hb_script_t script); HB_EXTERN hb_unicode_funcs_t * hb_glib_get_unicode_funcs (void); +#if GLIB_CHECK_VERSION(2,31,10) HB_EXTERN hb_blob_t * hb_glib_blob_create (GBytes *gbytes); - +#endif HB_END_DECLS #endif /* HB_GLIB_H */
--- a/gfx/harfbuzz/src/moz.build +++ b/gfx/harfbuzz/src/moz.build @@ -54,16 +54,25 @@ UNIFIED_SOURCES += [ 'hb-ot-tag.cc', 'hb-set.cc', 'hb-shape.cc', 'hb-shaper.cc', 'hb-unicode.cc', 'hb-warning.cc', ] +if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']: + EXPORTS.harfbuzz += [ + 'hb-glib.h', + ] + UNIFIED_SOURCES += [ + 'hb-glib.cc', + ] + CXXFLAGS += CONFIG['GLIB_CFLAGS'] + # We allow warnings for third-party code that can be updated from upstream. ALLOW_COMPILER_WARNINGS = True FINAL_LIBRARY = 'gkmedias' DEFINES['PACKAGE_VERSION'] = '"moz"' DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"' DEFINES['HAVE_OT'] = 1
--- a/gfx/layers/RotatedBuffer.cpp +++ b/gfx/layers/RotatedBuffer.cpp @@ -649,17 +649,19 @@ RotatedContentBuffer::BeginPaint(Painted } if (!result.mDidSelfCopy) { destBufferRect = ComputeBufferRect(neededRegion.GetBounds()); CreateBuffer(result.mContentType, destBufferRect, bufferFlags, &destDTBuffer, &destDTBufferOnWhite); if (!destDTBuffer || (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) { - gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height)))) << "Failed 1 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height; + if (Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height))) { + gfxCriticalNote << "Failed 1 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height; + } return result; } } } } else { mBufferRect = destBufferRect; mBufferRotation = newRotation; } @@ -671,17 +673,19 @@ RotatedContentBuffer::BeginPaint(Painted mBufferRotation = IntPoint(0,0); } } else { // The buffer's not big enough, so allocate a new one CreateBuffer(result.mContentType, destBufferRect, bufferFlags, &destDTBuffer, &destDTBufferOnWhite); if (!destDTBuffer || (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) { - gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height)))) << "Failed 2 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height; + if (Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height))) { + gfxCriticalNote << "Failed 2 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height; + } return result; } } NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || destBufferRect == neededRegion.GetBounds(), "If we're resampling, we need to validate the entire buffer"); // If we have no buffered data already, then destBuffer will be a fresh buffer
new file mode 100644 --- /dev/null +++ b/gfx/layers/apz/test/mochitest/helper_bug1299195.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width; initial-scale=1.0"> + <title>Test pointer events are dispatched once for touch tap</title> + <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script> + <script type="application/javascript" src="apz_test_utils.js"></script> + <script type="application/javascript" src="apz_test_native_event_utils.js"></script> + <script type="application/javascript"> + /** Test for Bug 1299195 **/ + function runTests() { + let target0 = document.getElementById("target0"); + let mouseup_count = 0; + let mousedown_count = 0; + let pointerup_count = 0; + let pointerdown_count = 0; + + target0.addEventListener("mouseup", () => { + ++mouseup_count; + if (mouseup_count == 2) { + is(mousedown_count, 2, "Double tap with touch should fire 2 mousedown events"); + is(mouseup_count, 2, "Double tap with touch should fire 2 mouseup events"); + is(pointerdown_count, 2, "Double tap with touch should fire 2 pointerdown events"); + is(pointerup_count, 2, "Double tap with touch should fire 2 pointerup events"); + subtestDone(); + } + }); + target0.addEventListener("mousedown", () => { + ++mousedown_count; + }); + target0.addEventListener("pointerup", () => { + ++pointerup_count; + }); + target0.addEventListener("pointerdown", () => { + ++pointerdown_count; + }); + synthesizeNativeTap(document.getElementById('target0'), 100, 100); + synthesizeNativeTap(document.getElementById('target0'), 100, 100); + } + waitUntilApzStable().then(runTests); + </script> +</head> +<body> + <div id="target0" style="width: 200px; height: 200px; background: green"></div> +</body> +</html>
--- a/gfx/layers/apz/test/mochitest/mochitest.ini +++ b/gfx/layers/apz/test/mochitest/mochitest.ini @@ -24,16 +24,17 @@ support-files = helper_scroll_inactive_perspective.html helper_scroll_inactive_zindex.html helper_bug1280013.html helper_tall.html helper_drag_scroll.html helper_touch_action_complex.html helper_tap_fullzoom.html helper_bug1162771.html + helper_bug1299195.html tags = apz [test_bug982141.html] [test_bug1151663.html] [test_bug1277814.html] skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet [test_wheel_scroll.html] skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet [test_wheel_transactions.html] @@ -58,12 +59,12 @@ skip-if = (toolkit == 'android') # wheel [test_group_mouseevents.html] skip-if = (toolkit == 'android') # mouse events not supported on mobile [test_touch_listeners_impacting_wheel.html] skip-if = (toolkit == 'android') || (toolkit == 'cocoa') # wheel events not supported on mobile, and synthesized wheel smooth-scrolling not supported on OS X [test_bug1253683.html] skip-if = (os == 'android') || (os == 'b2g') # uses wheel events which are not supported on mobile [test_group_zoom.html] skip-if = (toolkit != 'android') # only android supports zoom -[test_bug1285070.html] +[test_group_pointerevents.html] # Windows touch injection doesn't work in automation, but this test can be run locally on a windows touch device. # On OS X we don't support touch events at all. skip-if = (toolkit == 'windows') || (toolkit == 'cocoa')
rename from gfx/layers/apz/test/mochitest/test_bug1285070.html rename to gfx/layers/apz/test/mochitest/test_group_pointerevents.html --- a/gfx/layers/apz/test/mochitest/test_bug1285070.html +++ b/gfx/layers/apz/test/mochitest/test_group_pointerevents.html @@ -7,17 +7,18 @@ https://bugzilla.mozilla.org/show_bug.cg <meta charset="utf-8"> <title>Test for Bug 1285070</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="apz_test_utils.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> <script type="application/javascript"> var subtests = [ - {'file': 'helper_bug1285070.html', 'prefs': [["dom.w3c_pointer_events.enabled", true]]} + {'file': 'helper_bug1285070.html', 'prefs': [["dom.w3c_pointer_events.enabled", true]]}, + {'file': 'helper_bug1299195.html', 'prefs': [["dom.w3c_pointer_events.enabled", true]]} ]; if (isApzEnabled()) { SimpleTest.waitForExplicitFinish(); window.onload = function() { runSubtestsSeriallyInFreshWindows(subtests) .then(SimpleTest.finish); };
--- a/gfx/layers/client/ClientLayerManager.cpp +++ b/gfx/layers/client/ClientLayerManager.cpp @@ -184,16 +184,22 @@ ClientLayerManager::CreateReadbackLayer( { RefPtr<ReadbackLayer> layer = new ClientReadbackLayer(this); return layer.forget(); } void ClientLayerManager::BeginTransactionWithTarget(gfxContext* aTarget) { + MOZ_ASSERT(mForwarder, "ClientLayerManager::BeginTransaction without forwarder"); + if (!mForwarder->IPCOpen()) { + gfxCriticalNote << "ClientLayerManager::BeginTransaction with IPC channel down. GPU process may have died."; + return; + } + mInTransaction = true; mTransactionStart = TimeStamp::Now(); #ifdef MOZ_LAYERS_HAVE_LOG MOZ_LAYERS_LOG(("[----- BeginTransaction")); Log(); #endif @@ -268,22 +274,16 @@ bool ClientLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback, void* aCallbackData, EndTransactionFlags) { PROFILER_LABEL("ClientLayerManager", "EndTransactionInternal", js::ProfileEntry::Category::GRAPHICS); if (!mForwarder || !mForwarder->IPCOpen()) { - gfxCriticalError() << "LayerManager::EndTransaction while IPC is dead."; - // Pointless to try to render since the content cannot be sent to the - // compositor. We should not get here in the first place but I suspect - // This is happening during shutdown, tab-switch or some other scenario - // where we already started tearing the resources down but something - // triggered painting anyway. return false; } #ifdef MOZ_LAYERS_HAVE_LOG MOZ_LAYERS_LOG((" ----- (beginning paint)")); Log(); #endif profiler_tracing("Paint", "Rasterize", TRACING_INTERVAL_START);
--- a/gfx/layers/client/ClientLayerManager.h +++ b/gfx/layers/client/ClientLayerManager.h @@ -397,18 +397,19 @@ public: // our layer tree to a parent process. Record the new layer creation // in the current open transaction as a side effect. template<typename CreatedMethod> void CreateShadowFor(ClientLayer* aLayer, ClientLayerManager* aMgr, CreatedMethod aMethod) { PLayerChild* shadow = aMgr->AsShadowForwarder()->ConstructShadowFor(aLayer); - // XXX error handling - MOZ_ASSERT(shadow, "failed to create shadow"); + if (!shadow) { + return; + } aLayer->SetShadow(shadow); (aMgr->AsShadowForwarder()->*aMethod)(aLayer); aMgr->Hold(aLayer->AsLayer()); } #define CREATE_SHADOW(_type) \ CreateShadowFor(layer, this, \
--- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -1066,17 +1066,16 @@ TextureClient::CreateForDrawing(TextureF int32_t aMaxTextureSize, BackendSelector aSelector, TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags) { gfx::BackendType moz2DBackend = BackendTypeForBackendSelector(aLayersBackend, aSelector); // also test the validity of aAllocator - MOZ_ASSERT(aAllocator && aAllocator->IPCOpen()); if (!aAllocator || !aAllocator->IPCOpen()) { return nullptr; } if (!gfx::Factory::AllowedSurfaceSize(aSize)) { return nullptr; } @@ -1163,17 +1162,16 @@ TextureClient::CreateForDrawing(TextureF already_AddRefed<TextureClient> TextureClient::CreateFromSurface(KnowsCompositor* aAllocator, gfx::SourceSurface* aSurface, BackendSelector aSelector, TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags) { // also test the validity of aAllocator - MOZ_ASSERT(aAllocator && aAllocator->GetTextureForwarder()->IPCOpen()); if (!aAllocator || !aAllocator->GetTextureForwarder()->IPCOpen()) { return nullptr; } gfx::IntSize size = aSurface->GetSize(); if (!gfx::Factory::AllowedSurfaceSize(size)) { return nullptr;
--- a/gfx/layers/ipc/ShadowLayers.cpp +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -687,17 +687,19 @@ ShadowLayerForwarder::EndTransaction(Inf MOZ_LAYERS_LOG(("[LayersForwarder] OpSetLayerAttributes(%p)\n", mutant)); mTxn->AddEdit(OpSetLayerAttributes(nullptr, Shadow(shadow), attrs)); } AutoTArray<Edit, 10> cset; size_t nCsets = mTxn->mCset.size() + mTxn->mPaints.size(); - MOZ_ASSERT(nCsets > 0 || mTxn->RotationChanged(), "should have bailed by now"); + if (nCsets == 0 && !mTxn->RotationChanged()) { + return true; + } cset.SetCapacity(nCsets); if (!mTxn->mCset.empty()) { cset.AppendElements(&mTxn->mCset.front(), mTxn->mCset.size()); } // Paints after non-paint ops, including attribute changes. See // above. if (!mTxn->mPaints.empty()) {
--- a/gfx/thebes/gfxFontconfigFonts.cpp +++ b/gfx/thebes/gfxFontconfigFonts.cpp @@ -11,16 +11,17 @@ #include "gfxContext.h" #ifdef MOZ_WIDGET_GTK #include "gfxPlatformGtk.h" #endif #include "gfxFontconfigFonts.h" #include "gfxFT2FontBase.h" #include "gfxFT2Utils.h" #include "harfbuzz/hb.h" +#include "harfbuzz/hb-glib.h" #include "harfbuzz/hb-ot.h" #include "nsUnicodeProperties.h" #include "nsUnicodeScriptCodes.h" #include "gfxFontconfigUtils.h" #include "gfxUserFontSet.h" #include "gfxFontConstants.h" #include "nsGkAtoms.h" #include "nsILanguageAtomService.h" @@ -1618,17 +1619,17 @@ gfxPangoFontGroup::FindFontForChar(uint3 } // Our MOZ_SCRIPT_* codes may not match the PangoScript enumeration values // (if we're using ICU's codes), so convert by mapping through ISO 15924 tag. // Note that PangoScript is defined to be compatible with GUnicodeScript: // https://developer.gnome.org/pango/stable/pango-Scripts-and-Languages.html#PangoScript const hb_tag_t scriptTag = GetScriptTagForCode(aRunScript); const PangoScript script = - (const PangoScript)g_unicode_script_from_iso15924(scriptTag); + (const PangoScript)hb_glib_script_from_script(hb_script_from_iso15924_tag(scriptTag)); // Might be nice to call pango_language_includes_script only once for the // run rather than for each character. PangoLanguage *scriptLang; if ((!basePattern || !pango_language_includes_script(mPangoLanguage, script)) && (scriptLang = pango_script_get_sample_language(script))) { fontSet = GetFontSet(scriptLang);
--- a/intl/locale/moz.build +++ b/intl/locale/moz.build @@ -27,16 +27,17 @@ XPIDL_MODULE = 'locale' EXPORTS += [ 'nsCollation.h', 'nsCollationCID.h', 'nsDateTimeFormatCID.h', 'nsIDateTimeFormat.h', 'nsILanguageAtomService.h', 'nsIPlatformCharset.h', 'nsPosixLocale.h', + 'nsUConvPropertySearch.h', 'nsWin32Locale.h', ] UNIFIED_SOURCES += [ 'nsCollation.cpp', 'nsIDateTimeFormat.cpp', 'nsLanguageAtomService.cpp', 'nsLocale.cpp',
--- a/js/src/asmjs/WasmStubs.cpp +++ b/js/src/asmjs/WasmStubs.cpp @@ -1042,17 +1042,17 @@ wasm::GenerateInterruptStub(MacroAssembl FloatRegisterSet(uint32_t(0)))); // Save both the APSR and FPSCR in non-volatile registers. masm.as_mrs(r4); masm.as_vmrs(r5); // Save the stack pointer in a non-volatile register. masm.mov(sp,r6); // Align the stack. - masm.ma_and(Imm32(~7), sp, sp); + masm.as_bic(sp, sp, Imm8(7)); // Store resumePC into the return PC stack slot. masm.loadWasmActivationFromSymbolicAddress(IntArgReg0); masm.loadPtr(Address(IntArgReg0, WasmActivation::offsetOfResumePC()), IntArgReg1); masm.storePtr(IntArgReg1, Address(r6, 14 * sizeof(uint32_t*))); // Save all FP registers static_assert(!SupportsSimd, "high lanes of SIMD registers need to be saved too.");
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -886,21 +886,16 @@ BytecodeEmitter::EmitterScope::enterLexi return false; if (!tdzCache->noteTDZCheck(bce, bi.name(), CheckTDZ)) return false; } updateFrameFixedSlots(bce, bi); - // Put frame slots in TDZ. Environment slots are poisoned during - // environment creation. - if (!deadZoneFrameSlotRange(bce, firstFrameSlot, frameSlotEnd())) - return false; - // Create and intern the VM scope. auto createScope = [kind, bindings, firstFrameSlot](ExclusiveContext* cx, HandleScope enclosing) { return LexicalScope::create(cx, kind, bindings, firstFrameSlot, enclosing); }; if (!internScope(bce, createScope)) return false; @@ -910,16 +905,24 @@ BytecodeEmitter::EmitterScope::enterLexi if (!bce->emitInternedScopeOp(index(), JSOP_PUSHLEXICALENV)) return false; } // Lexical scopes need notes to be mapped from a pc. if (!appendScopeNote(bce)) return false; + // Put frame slots in TDZ. Environment slots are poisoned during + // environment creation. + // + // This must be done after appendScopeNote to be considered in the extent + // of the scope. + if (!deadZoneFrameSlotRange(bce, firstFrameSlot, frameSlotEnd())) + return false; + return checkEnvironmentChainLength(bce); } bool BytecodeEmitter::EmitterScope::enterNamedLambda(BytecodeEmitter* bce, FunctionBox* funbox) { MOZ_ASSERT(this == bce->innermostEmitterScope); MOZ_ASSERT(funbox->namedLambdaBindings());
--- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2836,17 +2836,18 @@ Parser<ParseHandler>::checkFunctionDefin handler.setOp(pn, kind == Arrow ? JSOP_LAMBDA_ARROW : JSOP_LAMBDA); } return true; } template <> bool -Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, bool tryAnnexB) +Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, FunctionSyntaxKind kind, + bool tryAnnexB) { // When a lazily-parsed function is called, we only fully parse (and emit) // that function, not any of its nested children. The initial syntax-only // parse recorded the free variables of nested functions and their extents, // so we can skip over them after accounting for their free variables. RootedFunction fun(context, handler.nextLazyInnerFunction()); MOZ_ASSERT(!fun->isLegacyGenerator()); @@ -2862,22 +2863,31 @@ Parser<FullParseHandler>::skipLazyInnerF PropagateTransitiveParseFlags(lazy, pc->sc()); // The position passed to tokenStream.advance() is an offset of the sort // returned by userbuf.offset() and expected by userbuf.rawCharPtrAt(), // while LazyScript::{begin,end} offsets are relative to the outermost // script source. Rooted<LazyScript*> lazyOuter(context, handler.lazyOuterFunction()); uint32_t userbufBase = lazyOuter->begin() - lazyOuter->column(); - return tokenStream.advance(fun->lazyScript()->end() - userbufBase); + if (!tokenStream.advance(fun->lazyScript()->end() - userbufBase)) + return false; + + if (kind == Statement && fun->isExprBody()) { + if (!MatchOrInsertSemicolonAfterExpression(tokenStream)) + return false; + } + + return true; } template <> bool -Parser<SyntaxParseHandler>::skipLazyInnerFunction(Node pn, bool tryAnnexB) +Parser<SyntaxParseHandler>::skipLazyInnerFunction(Node pn, FunctionSyntaxKind kind, + bool tryAnnexB) { MOZ_CRASH("Cannot skip lazy inner functions when syntax parsing"); } template <typename ParseHandler> bool Parser<ParseHandler>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, Node nodeList, TokenKind* ttp) @@ -2965,17 +2975,17 @@ Parser<ParseHandler>::functionDefinition bool tryAnnexB = false; if (!checkFunctionDefinition(funName, pn, kind, generatorKind, &tryAnnexB)) return null(); // When fully parsing a LazyScript, we do not fully reparse its inner // functions, which are also lazy. Instead, their free variables and // source extents are recorded and may be skipped. if (handler.canSkipLazyInnerFunctions()) { - if (!skipLazyInnerFunction(pn, tryAnnexB)) + if (!skipLazyInnerFunction(pn, kind, tryAnnexB)) return null(); return pn; } RootedObject proto(context); if (generatorKind == StarGenerator) { // If we are off the main thread, the generator meta-objects have // already been created by js::StartOffThreadParseScript, so cx will not
--- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -1254,17 +1254,17 @@ class Parser final : private JS::AutoGCR bool declareFunctionThis(); Node newInternalDotName(HandlePropertyName name); Node newThisName(); Node newDotGeneratorName(); bool declareDotGeneratorName(); bool checkFunctionDefinition(HandleAtom funAtom, Node pn, FunctionSyntaxKind kind, GeneratorKind generatorKind, bool* tryAnnexB); - bool skipLazyInnerFunction(Node pn, bool tryAnnexB); + bool skipLazyInnerFunction(Node pn, FunctionSyntaxKind kind, bool tryAnnexB); bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun, InHandling inHandling, FunctionSyntaxKind kind, GeneratorKind generatorKind, bool tryAnnexB, Directives inheritedDirectives, Directives* newDirectives); bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, InHandling inHandling, FunctionSyntaxKind kind, GeneratorKind generatorKind, bool tryAnnexB, Directives inheritedDirectives, Directives* newDirectives);
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/parser/bug-1298809.js @@ -0,0 +1,6 @@ +function f() { + if (0) + function g() x; + else; +} +f();
--- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -2731,16 +2731,30 @@ MBinaryBitwiseInstruction::foldsTo(TempA return folded; return this; } MDefinition* MBinaryBitwiseInstruction::foldUnnecessaryBitop() { + // Fold unsigned shift right operator when the second operand is zero and + // the only use is an unsigned modulo. Thus, the expression + // |(x >>> 0) % y| becomes |x % y|. + if (isUrsh() && hasOneDefUse() && getOperand(1)->isConstant()) { + MConstant* constant = getOperand(1)->toConstant(); + if (constant->type() == MIRType::Int32 && constant->toInt32() == 0) { + for (MUseDefIterator use(this); use; use++) { + if (use.def()->isMod() && use.def()->toMod()->isUnsigned()) + return getOperand(0); + break; + } + } + } + if (specialization_ != MIRType::Int32) return this; // Eliminate bitwise operations that are no-ops when used on integer // inputs, such as (x | 0). MDefinition* lhs = getOperand(0); MDefinition* rhs = getOperand(1);
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -7134,33 +7134,61 @@ class MDiv : public MBinaryArithInstruct return unsigned_ == other->isUnsigned(); } ALLOW_CLONE(MDiv) }; class MMod : public MBinaryArithInstruction { + public: + enum class PossiblyUnsigned { + NotPossible, + LHSPossible, + RHSPossible, + BothPossible, + }; + + protected: bool unsigned_; bool canBeNegativeDividend_; bool canBePowerOfTwoDivisor_; bool canBeDivideByZero_; bool trapOnError_; + PossiblyUnsigned possiblyUnsigned_; + MMod(MDefinition* left, MDefinition* right, MIRType type) : MBinaryArithInstruction(left, right), unsigned_(false), canBeNegativeDividend_(true), canBePowerOfTwoDivisor_(true), canBeDivideByZero_(true), - trapOnError_(false) + trapOnError_(false), + possiblyUnsigned_(PossiblyUnsigned::NotPossible) { if (type != MIRType::Value) specialization_ = type; setResultType(type); + + if (left->isUrsh() && left->getOperand(1)->isConstant()) { + MConstant* constant = left->getOperand(1)->toConstant(); + if (constant->type() == MIRType::Int32 && constant->toInt32() == 0) + possiblyUnsigned_ = PossiblyUnsigned::LHSPossible; + } + + if (right->isUrsh() && right->getOperand(1)->isConstant()) { + MConstant* constant = right->getOperand(1)->toConstant(); + if (constant->type() == MIRType::Int32 && constant->toInt32() == 0) { + if (possiblyUnsigned_ == PossiblyUnsigned::NotPossible) + possiblyUnsigned_ = PossiblyUnsigned::RHSPossible; + else + possiblyUnsigned_ = PossiblyUnsigned::BothPossible; + } + } } public: INSTRUCTION_HEADER(Mod) static MMod* New(TempAllocator& alloc, MDefinition* left, MDefinition* right) { return new(alloc) MMod(left, right, MIRType::Value); } static MMod* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
--- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -1556,23 +1556,50 @@ MMod::computeRange(TempAllocator& alloc) { if (specialization() != MIRType::Int32 && specialization() != MIRType::Double) return; Range lhs(getOperand(0)); Range rhs(getOperand(1)); // If either operand is a NaN, the result is NaN. This also conservatively // handles Infinity cases. - if (!lhs.hasInt32Bounds() || !rhs.hasInt32Bounds()) + if ((possiblyUnsigned_ == PossiblyUnsigned::NotPossible && + !lhs.hasInt32Bounds()) || !rhs.hasInt32Bounds()) + { return; + } // If RHS can be zero, the result can be NaN. if (rhs.lower() <= 0 && rhs.upper() >= 0) return; + // (x >>> 0) % y is an unsigned modulo operation but the lhs' range is not + // always >= 0. The lhs range assumes a signed integer 32 bit while the + // value is unsigned 32 bit. That breaks the assumption that range >= 0. + if (specialization() == MIRType::Int32) { + switch (possiblyUnsigned_) { + case PossiblyUnsigned::NotPossible: + break; + case PossiblyUnsigned::LHSPossible: + if (rhs.lower() > 0 && !rhs.canHaveFractionalPart()) + unsigned_ = true; + break; + case PossiblyUnsigned::RHSPossible: + if (lhs.lower() >= 0 && !lhs.canHaveFractionalPart()) + unsigned_ = true; + break; + case PossiblyUnsigned::BothPossible: + if (lhs.lower() >= 0 && !lhs.canHaveFractionalPart()) + unsigned_ = true; + else if (rhs.lower() > 0 && !rhs.canHaveFractionalPart()) + unsigned_ = true; + break; + } + } + // If both operands are non-negative integers, we can optimize this to an // unsigned mod. if (specialization() == MIRType::Int32 && lhs.lower() >= 0 && rhs.lower() > 0 && !lhs.canHaveFractionalPart() && !rhs.canHaveFractionalPart()) { unsigned_ = true; }
--- a/js/src/jit/arm/Assembler-arm.cpp +++ b/js/src/jit/arm/Assembler-arm.cpp @@ -1112,17 +1112,17 @@ Imm8::EncodeTwoImms(uint32_t imm) imm2 = ((imm >> (32 - imm2shift)) | (imm << imm2shift)) & 0xff; MOZ_ASSERT((imm1shift & 0x1) == 0); MOZ_ASSERT((imm2shift & 0x1) == 0); return TwoImm8mData(datastore::Imm8mData(imm1, imm1shift >> 1), datastore::Imm8mData(imm2, imm2shift >> 1)); } ALUOp -jit::ALUNeg(ALUOp op, Register dest, Imm32* imm, Register* negDest) +jit::ALUNeg(ALUOp op, Register dest, Register scratch, Imm32* imm, Register* negDest) { // Find an alternate ALUOp to get the job done, and use a different imm. *negDest = dest; switch (op) { case OpMov: *imm = Imm32(~imm->value); return OpMvn; case OpMvn: @@ -1144,17 +1144,17 @@ jit::ALUNeg(ALUOp op, Register dest, Imm *imm = Imm32(-imm->value); return OpCmn; case OpCmn: *imm = Imm32(-imm->value); return OpCmp; case OpTst: MOZ_ASSERT(dest == InvalidReg); *imm = Imm32(~imm->value); - *negDest = ScratchRegister; + *negDest = scratch; return OpBic; // orr has orn on thumb2 only. default: return OpInvalid; } } bool @@ -3416,8 +3416,13 @@ Assembler::GetPoolMaxOffset() char* poolMaxOffsetStr = getenv("ASM_POOL_MAX_OFFSET"); uint32_t poolMaxOffset; if (poolMaxOffsetStr && sscanf(poolMaxOffsetStr, "%u", &poolMaxOffset) == 1) AsmPoolMaxOffset = poolMaxOffset; isSet = true; } return AsmPoolMaxOffset; } + +SecondScratchRegisterScope::SecondScratchRegisterScope(MacroAssembler &masm) + : AutoRegisterScope(masm, masm.getSecondScratchReg()) +{ +}
--- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -50,16 +50,21 @@ static constexpr Register ScratchRegiste // of code thinks it has exclusive ownership of the scratch register. struct ScratchRegisterScope : public AutoRegisterScope { explicit ScratchRegisterScope(MacroAssembler& masm) : AutoRegisterScope(masm, ScratchRegister) { } }; +struct SecondScratchRegisterScope : public AutoRegisterScope +{ + explicit SecondScratchRegisterScope(MacroAssembler& masm); +}; + static constexpr Register OsrFrameReg = r3; static constexpr Register ArgumentsRectifierReg = r8; static constexpr Register CallTempReg0 = r5; static constexpr Register CallTempReg1 = r6; static constexpr Register CallTempReg2 = r7; static constexpr Register CallTempReg3 = r8; static constexpr Register CallTempReg4 = r0; static constexpr Register CallTempReg5 = r1; @@ -404,17 +409,17 @@ enum VFPOp { OpvAbs = 0xB << 20 | 0x3 << 6, OpvNeg = 0xB << 20 | 0x1 << 6 | 0x1 << 16, OpvSqrt = 0xB << 20 | 0x3 << 6 | 0x1 << 16, OpvCmp = 0xB << 20 | 0x1 << 6 | 0x4 << 16, OpvCmpz = 0xB << 20 | 0x1 << 6 | 0x5 << 16 }; // Negate the operation, AND negate the immediate that we were passed in. -ALUOp ALUNeg(ALUOp op, Register dest, Imm32* imm, Register* negDest); +ALUOp ALUNeg(ALUOp op, Register dest, Register scratch, Imm32* imm, Register* negDest); bool can_dbl(ALUOp op); bool condsAreSafe(ALUOp op); // If there is a variant of op that has a dest (think cmp/sub) return that // variant of it. ALUOp getDestVariant(ALUOp op); static const ValueOperand JSReturnOperand = ValueOperand(JSReturnReg_Type, JSReturnReg_Data);
--- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -66,17 +66,17 @@ OutOfLineBailout::accept(CodeGeneratorAR void CodeGeneratorARM::visitTestIAndBranch(LTestIAndBranch* test) { const LAllocation* opd = test->getOperand(0); MBasicBlock* ifTrue = test->ifTrue(); MBasicBlock* ifFalse = test->ifFalse(); // Test the operand - masm.ma_cmp(ToRegister(opd), Imm32(0)); + masm.as_cmp(ToRegister(opd), Imm8(0)); if (isNextBlock(ifFalse->lir())) { jumpToBlock(ifTrue, Assembler::NonZero); } else if (isNextBlock(ifTrue->lir())) { jumpToBlock(ifFalse, Assembler::Zero); } else { jumpToBlock(ifFalse, Assembler::Zero); jumpToBlock(ifTrue); @@ -86,39 +86,47 @@ CodeGeneratorARM::visitTestIAndBranch(LT void CodeGeneratorARM::visitCompare(LCompare* comp) { Assembler::Condition cond = JSOpToCondition(comp->mir()->compareType(), comp->jsop()); const LAllocation* left = comp->getOperand(0); const LAllocation* right = comp->getOperand(1); const LDefinition* def = comp->getDef(0); - if (right->isConstant()) - masm.ma_cmp(ToRegister(left), Imm32(ToInt32(right))); - else if (right->isRegister()) + ScratchRegisterScope scratch(masm); + + if (right->isConstant()) { + masm.ma_cmp(ToRegister(left), Imm32(ToInt32(right)), scratch); + } else if (right->isRegister()) { masm.ma_cmp(ToRegister(left), ToRegister(right)); - else - masm.ma_cmp(ToRegister(left), Operand(ToAddress(right))); + } else { + SecondScratchRegisterScope scratch2(masm); + masm.ma_cmp(ToRegister(left), Operand(ToAddress(right)), scratch, scratch2); + } masm.ma_mov(Imm32(0), ToRegister(def)); masm.ma_mov(Imm32(1), ToRegister(def), cond); } void CodeGeneratorARM::visitCompareAndBranch(LCompareAndBranch* comp) { Assembler::Condition cond = JSOpToCondition(comp->cmpMir()->compareType(), comp->jsop()); const LAllocation* left = comp->left(); const LAllocation* right = comp->right(); - if (right->isConstant()) - masm.ma_cmp(ToRegister(left), Imm32(ToInt32(right))); - else if (right->isRegister()) + ScratchRegisterScope scratch(masm); + + if (right->isConstant()) { + masm.ma_cmp(ToRegister(left), Imm32(ToInt32(right)), scratch); + } else if (right->isRegister()) { masm.ma_cmp(ToRegister(left), ToRegister(right)); - else - masm.ma_cmp(ToRegister(left), Operand(ToAddress(right))); + } else { + SecondScratchRegisterScope scratch2(masm); + masm.ma_cmp(ToRegister(left), Operand(ToAddress(right)), scratch, scratch2); + } emitBranch(cond, comp->ifTrue(), comp->ifFalse()); } bool CodeGeneratorARM::generateOutOfLineCode() { if (!CodeGeneratorShared::generateOutOfLineCode()) return false; @@ -276,18 +284,20 @@ CodeGeneratorARM::visitSqrtF(LSqrtF* ins void CodeGeneratorARM::visitAddI(LAddI* ins) { const LAllocation* lhs = ins->getOperand(0); const LAllocation* rhs = ins->getOperand(1); const LDefinition* dest = ins->getDef(0); + ScratchRegisterScope scratch(masm); + if (rhs->isConstant()) - masm.ma_add(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), SetCC); + masm.ma_add(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), scratch, SetCC); else if (rhs->isRegister()) masm.ma_add(ToRegister(lhs), ToRegister(rhs), ToRegister(dest), SetCC); else masm.ma_add(ToRegister(lhs), Operand(ToAddress(rhs)), ToRegister(dest), SetCC); if (ins->snapshot()) bailoutIf(Assembler::Overflow, ins->snapshot()); } @@ -310,18 +320,20 @@ CodeGeneratorARM::visitAddI64(LAddI64* l void CodeGeneratorARM::visitSubI(LSubI* ins) { const LAllocation* lhs = ins->getOperand(0); const LAllocation* rhs = ins->getOperand(1); const LDefinition* dest = ins->getDef(0); + ScratchRegisterScope scratch(masm); + if (rhs->isConstant()) - masm.ma_sub(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), SetCC); + masm.ma_sub(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), scratch, SetCC); else if (rhs->isRegister()) masm.ma_sub(ToRegister(lhs), ToRegister(rhs), ToRegister(dest), SetCC); else masm.ma_sub(ToRegister(lhs), Operand(ToAddress(rhs)), ToRegister(dest), SetCC); if (ins->snapshot()) bailoutIf(Assembler::Overflow, ins->snapshot()); } @@ -353,23 +365,23 @@ CodeGeneratorARM::visitMulI(LMulI* ins) if (rhs->isConstant()) { // Bailout when this condition is met. Assembler::Condition c = Assembler::Overflow; // Bailout on -0.0 int32_t constant = ToInt32(rhs); if (mul->canBeNegativeZero() && constant <= 0) { Assembler::Condition bailoutCond = (constant == 0) ? Assembler::LessThan : Assembler::Equal; - masm.ma_cmp(ToRegister(lhs), Imm32(0)); + masm.as_cmp(ToRegister(lhs), Imm8(0)); bailoutIf(bailoutCond, ins->snapshot()); } // TODO: move these to ma_mul. switch (constant) { case -1: - masm.ma_rsb(ToRegister(lhs), Imm32(0), ToRegister(dest), SetCC); + masm.as_rsb(ToRegister(dest), ToRegister(lhs), Imm8(0), SetCC); break; case 0: masm.ma_mov(Imm32(0), ToRegister(dest)); return; // Escape overflow check; case 1: // Nop masm.ma_mov(ToRegister(lhs), ToRegister(dest)); return; // Escape overflow check; @@ -417,41 +429,44 @@ CodeGeneratorARM::visitMulI(LMulI* ins) masm.as_cmp(ToRegister(lhs), asr(ToRegister(dest), shift)); c = Assembler::NotEqual; handled = true; } } } if (!handled) { + ScratchRegisterScope scratch(masm); if (mul->canOverflow()) - c = masm.ma_check_mul(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), c); + c = masm.ma_check_mul(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), scratch, c); else - masm.ma_mul(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest)); + masm.ma_mul(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), scratch); } } } // Bailout on overflow. if (mul->canOverflow()) bailoutIf(c, ins->snapshot()); } else { Assembler::Condition c = Assembler::Overflow; - if (mul->canOverflow()) - c = masm.ma_check_mul(ToRegister(lhs), ToRegister(rhs), ToRegister(dest), c); - else + if (mul->canOverflow()) { + ScratchRegisterScope scratch(masm); + c = masm.ma_check_mul(ToRegister(lhs), ToRegister(rhs), ToRegister(dest), scratch, c); + } else { masm.ma_mul(ToRegister(lhs), ToRegister(rhs), ToRegister(dest)); + } // Bailout on overflow. if (mul->canOverflow()) bailoutIf(c, ins->snapshot()); if (mul->canBeNegativeZero()) { Label done; - masm.ma_cmp(ToRegister(dest), Imm32(0)); + masm.as_cmp(ToRegister(dest), Imm8(0)); masm.ma_b(&done, Assembler::NotEqual); // Result is -0 if lhs or rhs is negative. masm.ma_cmn(ToRegister(lhs), ToRegister(rhs)); bailoutIf(Assembler::Signed, ins->snapshot()); masm.bind(&done); } @@ -498,24 +513,26 @@ CodeGeneratorARM::visitMulI64(LMulI64* l masm.mul64(ToOperandOrRegister64(rhs), ToRegister64(lhs), temp); } } void CodeGeneratorARM::divICommon(MDiv* mir, Register lhs, Register rhs, Register output, LSnapshot* snapshot, Label& done) { + ScratchRegisterScope scratch(masm); + if (mir->canBeNegativeOverflow()) { // Handle INT32_MIN / -1; // The integer division will give INT32_MIN, but we want -(double)INT32_MIN. // Sets EQ if lhs == INT32_MIN. - masm.ma_cmp(lhs, Imm32(INT32_MIN)); + masm.ma_cmp(lhs, Imm32(INT32_MIN), scratch); // If EQ (LHS == INT32_MIN), sets EQ if rhs == -1. - masm.ma_cmp(rhs, Imm32(-1), Assembler::Equal); + masm.ma_cmp(rhs, Imm32(-1), scratch, Assembler::Equal); if (mir->canTruncateOverflow()) { if (mir->trapOnError()) { masm.ma_b(wasm::JumpTarget::IntegerOverflow, Assembler::Equal); } else { // (-INT32_MIN)|0 = INT32_MIN Label skip; masm.ma_b(&skip, Assembler::NotEqual); masm.ma_mov(Imm32(INT32_MIN), output); @@ -525,17 +542,17 @@ CodeGeneratorARM::divICommon(MDiv* mir, } else { MOZ_ASSERT(mir->fallible()); bailoutIf(Assembler::Equal, snapshot); } } // Handle divide by zero. if (mir->canBeDivideByZero()) { - masm.ma_cmp(rhs, Imm32(0)); + masm.as_cmp(rhs, Imm8(0)); if (mir->canTruncateInfinities()) { if (mir->trapOnError()) { masm.ma_b(wasm::JumpTarget::IntegerDivideByZero, Assembler::Equal); } else { // Infinity|0 == 0 Label skip; masm.ma_b(&skip, Assembler::NotEqual); masm.ma_mov(Imm32(0), output); @@ -546,19 +563,19 @@ CodeGeneratorARM::divICommon(MDiv* mir, MOZ_ASSERT(mir->fallible()); bailoutIf(Assembler::Equal, snapshot); } } // Handle negative 0. if (!mir->canTruncateNegativeZero() && mir->canBeNegativeZero()) { Label nonzero; - masm.ma_cmp(lhs, Imm32(0)); + masm.as_cmp(lhs, Imm8(0)); masm.ma_b(&nonzero, Assembler::NotEqual); - masm.ma_cmp(rhs, Imm32(0)); + masm.as_cmp(rhs, Imm8(0)); MOZ_ASSERT(mir->fallible()); bailoutIf(Assembler::LessThan, snapshot); masm.bind(&nonzero); } } void CodeGeneratorARM::visitDivI(LDivI* ins) @@ -610,17 +627,17 @@ CodeGeneratorARM::visitSoftDivI(LSoftDiv if (gen->compilingAsmJS()) masm.callWithABI(wasm::SymbolicAddress::aeabi_idivmod); else masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod)); // idivmod returns the quotient in r0, and the remainder in r1. if (!mir->canTruncateRemainder()) { MOZ_ASSERT(mir->fallible()); - masm.ma_cmp(r1, Imm32(0)); + masm.as_cmp(r1, Imm8(0)); bailoutIf(Assembler::NonZero, ins->snapshot()); } masm.bind(&done); } void CodeGeneratorARM::visitDivPowTwoI(LDivPowTwoI* ins) @@ -682,18 +699,18 @@ CodeGeneratorARM::modICommon(MMod* mir, // There are three cases: (Y < 0), (Y == 0) and (Y > 0). // If (Y < 0), then we compare X with 0, and bail if X == 0. // If (Y == 0), then we simply want to bail. Since this does not set the // flags necessary for LT to trigger, we don't test X, and take the bailout // because the EQ flag is set. // If (Y > 0), we don't set EQ, and we don't trigger LT, so we don't take // the bailout. if (mir->canBeDivideByZero() || mir->canBeNegativeDividend()) { - masm.ma_cmp(rhs, Imm32(0)); - masm.ma_cmp(lhs, Imm32(0), Assembler::LessThan); + masm.as_cmp(rhs, Imm8(0)); + masm.as_cmp(lhs, Imm8(0), Assembler::LessThan); if (mir->isTruncated()) { if (mir->trapOnError()) { masm.ma_b(wasm::JumpTarget::IntegerDivideByZero, Assembler::Equal); } else { // NaN|0 == 0 and (0 % -X)|0 == 0 Label skip; masm.ma_b(&skip, Assembler::NotEqual); masm.ma_mov(Imm32(0), output); @@ -717,28 +734,31 @@ CodeGeneratorARM::visitModI(LModI* ins) MMod* mir = ins->mir(); // Save the lhs in case we end up with a 0 that should be a -0.0 because lhs < 0. masm.ma_mov(lhs, callTemp); Label done; modICommon(mir, lhs, rhs, output, ins->snapshot(), done); - masm.ma_smod(lhs, rhs, output); + { + ScratchRegisterScope scratch(masm); + masm.ma_smod(lhs, rhs, output, scratch); + } // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0. if (mir->canBeNegativeDividend()) { if (mir->isTruncated()) { // -0.0|0 == 0 } else { MOZ_ASSERT(mir->fallible()); // See if X < 0 - masm.ma_cmp(output, Imm32(0)); + masm.as_cmp(output, Imm8(0)); masm.ma_b(&done, Assembler::NotEqual); - masm.ma_cmp(callTemp, Imm32(0)); + masm.as_cmp(callTemp, Imm8(0)); bailoutIf(Assembler::Signed, ins->snapshot()); } } masm.bind(&done); } void @@ -751,23 +771,27 @@ CodeGeneratorARM::visitSoftModI(LSoftMod Register callTemp = ToRegister(ins->callTemp()); MMod* mir = ins->mir(); Label done; // Save the lhs in case we end up with a 0 that should be a -0.0 because lhs < 0. 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()) { - // Sets EQ if lhs == INT_MIN - masm.ma_cmp(lhs, Imm32(INT_MIN)); - // If EQ (LHS == INT_MIN), sets EQ if rhs == -1 - masm.ma_cmp(rhs, Imm32(-1), Assembler::Equal); + { + ScratchRegisterScope scratch(masm); + // Sets EQ if lhs == INT_MIN + masm.ma_cmp(lhs, Imm32(INT_MIN), scratch); + // If EQ (LHS == INT_MIN), sets EQ if rhs == -1 + masm.ma_cmp(rhs, Imm32(-1), scratch, Assembler::Equal); + } if (mir->isTruncated()) { // (INT_MIN % -1)|0 == 0 Label skip; masm.ma_b(&skip, Assembler::NotEqual); masm.ma_mov(Imm32(0), output); masm.ma_b(&done); masm.bind(&skip); } else { @@ -788,39 +812,43 @@ CodeGeneratorARM::visitSoftModI(LSoftMod // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0 if (mir->canBeNegativeDividend()) { if (mir->isTruncated()) { // -0.0|0 == 0 } else { MOZ_ASSERT(mir->fallible()); // See if X < 0 - masm.ma_cmp(r1, Imm32(0)); + masm.as_cmp(r1, Imm8(0)); masm.ma_b(&done, Assembler::NotEqual); - masm.ma_cmp(callTemp, Imm32(0)); + masm.as_cmp(callTemp, Imm8(0)); bailoutIf(Assembler::Signed, ins->snapshot()); } } masm.bind(&done); } void CodeGeneratorARM::visitModPowTwoI(LModPowTwoI* ins) { Register in = ToRegister(ins->getOperand(0)); Register out = ToRegister(ins->getDef(0)); MMod* mir = ins->mir(); Label fin; // bug 739870, jbramley has a different sequence that may help with speed // here. + masm.ma_mov(in, out, SetCC); masm.ma_b(&fin, Assembler::Zero); - masm.ma_rsb(Imm32(0), out, LeaveCC, Assembler::Signed); - masm.ma_and(Imm32((1 << ins->shift()) - 1), out); - masm.ma_rsb(Imm32(0), out, SetCC, Assembler::Signed); + masm.as_rsb(out, out, Imm8(0), LeaveCC, Assembler::Signed); + { + ScratchRegisterScope scratch(masm); + masm.ma_and(Imm32((1 << ins->shift()) - 1), out, scratch); + } + masm.as_rsb(out, out, Imm8(0), SetCC, Assembler::Signed); if (mir->canBeNegativeDividend()) { if (!mir->isTruncated()) { MOZ_ASSERT(mir->fallible()); bailoutIf(Assembler::Zero, ins->snapshot()); } else { // -0|0 == 0 } } @@ -830,17 +858,22 @@ CodeGeneratorARM::visitModPowTwoI(LModPo void CodeGeneratorARM::visitModMaskI(LModMaskI* ins) { Register src = ToRegister(ins->getOperand(0)); Register dest = ToRegister(ins->getDef(0)); Register tmp1 = ToRegister(ins->getTemp(0)); Register tmp2 = ToRegister(ins->getTemp(1)); MMod* mir = ins->mir(); - masm.ma_mod_mask(src, dest, tmp1, tmp2, ins->shift()); + + ScratchRegisterScope scratch(masm); + SecondScratchRegisterScope scratch2(masm); + + masm.ma_mod_mask(src, dest, tmp1, tmp2, scratch, scratch2, ins->shift()); + if (mir->canBeNegativeDividend()) { if (!mir->isTruncated()) { MOZ_ASSERT(mir->fallible()); bailoutIf(Assembler::Zero, ins->snapshot()); } else { // -0|0 == 0 } } @@ -859,33 +892,36 @@ CodeGeneratorARM::visitBitNotI(LBitNotI* } void CodeGeneratorARM::visitBitOpI(LBitOpI* ins) { const LAllocation* lhs = ins->getOperand(0); const LAllocation* rhs = ins->getOperand(1); const LDefinition* dest = ins->getDef(0); + + ScratchRegisterScope scratch(masm); + // All of these bitops should be either imm32's, or integer registers. switch (ins->bitop()) { case JSOP_BITOR: if (rhs->isConstant()) - masm.ma_orr(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest)); + masm.ma_orr(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest), scratch); else masm.ma_orr(ToRegister(rhs), ToRegister(lhs), ToRegister(dest)); break; case JSOP_BITXOR: if (rhs->isConstant()) - masm.ma_eor(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest)); + masm.ma_eor(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest), scratch); else masm.ma_eor(ToRegister(rhs), ToRegister(lhs), ToRegister(dest)); break; case JSOP_BITAND: if (rhs->isConstant()) - masm.ma_and(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest)); + masm.ma_and(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest), scratch); else masm.ma_and(ToRegister(rhs), ToRegister(lhs), ToRegister(dest)); break; default: MOZ_CRASH("unexpected binary opcode"); } } @@ -913,42 +949,42 @@ CodeGeneratorARM::visitShiftI(LShiftI* i break; case JSOP_URSH: if (shift) { masm.ma_lsr(Imm32(shift), lhs, dest); } else { // x >>> 0 can overflow. masm.ma_mov(lhs, dest); if (ins->mir()->toUrsh()->fallible()) { - masm.ma_cmp(dest, Imm32(0)); + masm.as_cmp(dest, Imm8(0)); bailoutIf(Assembler::LessThan, ins->snapshot()); } } break; default: MOZ_CRASH("Unexpected shift op"); } } else { // The shift amounts should be AND'ed into the 0-31 range since arm // shifts by the lower byte of the register (it will attempt to shift by // 250 if you ask it to). - masm.ma_and(Imm32(0x1F), ToRegister(rhs), dest); + masm.as_and(dest, ToRegister(rhs), Imm8(0x1F)); switch (ins->bitop()) { case JSOP_LSH: masm.ma_lsl(dest, lhs, dest); break; case JSOP_RSH: masm.ma_asr(dest, lhs, dest); break; case JSOP_URSH: masm.ma_lsr(dest, lhs, dest); if (ins->mir()->toUrsh()->fallible()) { // x >>> 0 can overflow. - masm.ma_cmp(dest, Imm32(0)); + masm.as_cmp(dest, Imm8(0)); bailoutIf(Assembler::LessThan, ins->snapshot()); } break; default: MOZ_CRASH("Unexpected shift op"); } } } @@ -964,17 +1000,17 @@ CodeGeneratorARM::visitUrshD(LUrshD* ins if (rhs->isConstant()) { int32_t shift = ToInt32(rhs) & 0x1F; if (shift) masm.ma_lsr(Imm32(shift), lhs, temp); else masm.ma_mov(lhs, temp); } else { - masm.ma_and(Imm32(0x1F), ToRegister(rhs), temp); + masm.as_and(temp, ToRegister(rhs), Imm8(0x1F)); masm.ma_lsr(temp, lhs, temp); } masm.convertUInt32ToDouble(temp, out); } void CodeGeneratorARM::visitClzI(LClzI* ins) @@ -1114,20 +1150,22 @@ CodeGeneratorARM::emitTableSwitchDispatc // current instruction *PLUS 8*. This means that ldr foo, [pc, +0] reads // $pc+8. In other words, there is an empty word after the branch into the // switch table before the table actually starts. Since the only other // unhandled case is the default case (both out of range high and out of // range low) I then insert a branch to default case into the extra slot, // which ensures we don't attempt to execute the address table. Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label(); + ScratchRegisterScope scratch(masm); + int32_t cases = mir->numCases(); // Lower value with low value. - masm.ma_sub(index, Imm32(mir->low()), index, SetCC); - masm.ma_rsb(index, Imm32(cases - 1), index, SetCC, Assembler::NotSigned); + masm.ma_sub(index, Imm32(mir->low()), index, scratch, SetCC); + masm.ma_rsb(index, Imm32(cases - 1), index, scratch, SetCC, Assembler::NotSigned); // Inhibit pools within the following sequence because we are indexing into // a pc relative table. The region will have one instruction for ma_ldr, one // for ma_b, and each table case takes one word. AutoForbidPools afp(&masm, 1 + 1 + cases); masm.ma_ldr(DTRAddr(pc, DtrRegImmShift(index, LSL, 2)), pc, Offset, Assembler::NotSigned); masm.ma_b(defaultcase); // To fill in the CodeLabels for the case entries, we need to first generate @@ -1257,21 +1295,22 @@ CodeGeneratorARM::visitRoundF(LRoundF* l masm.roundf(input, output, &bail, tmp); bailoutFrom(&bail, lir->snapshot()); } void CodeGeneratorARM::emitRoundDouble(FloatRegister src, Register dest, Label* fail) { ScratchDoubleScope scratch(masm); + ScratchRegisterScope scratchReg(masm); masm.ma_vcvt_F64_I32(src, scratch); masm.ma_vxfer(scratch, dest); - masm.ma_cmp(dest, Imm32(0x7fffffff)); - masm.ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual); + masm.ma_cmp(dest, Imm32(0x7fffffff), scratchReg); + masm.ma_cmp(dest, Imm32(0x80000000), scratchReg, Assembler::NotEqual); masm.ma_b(fail, Assembler::Equal); } void CodeGeneratorARM::visitTruncateDToInt32(LTruncateDToInt32* ins) { emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output()), ins->mir()); } @@ -1375,18 +1414,20 @@ CodeGeneratorARM::visitBoxFloatingPoint( void CodeGeneratorARM::visitUnbox(LUnbox* unbox) { // Note that for unbox, the type and payload indexes are switched on the // inputs. MUnbox* mir = unbox->mir(); Register type = ToRegister(unbox->type()); + ScratchRegisterScope scratch(masm); + if (mir->fallible()) { - masm.ma_cmp(type, Imm32(MIRTypeToTag(mir->type()))); + masm.ma_cmp(type, Imm32(MIRTypeToTag(mir->type())), scratch); bailoutIf(Assembler::NotEqual, unbox->snapshot()); } } void CodeGeneratorARM::visitDouble(LDouble* ins) { const LDefinition* out = ins->getDef(0); @@ -1578,18 +1619,19 @@ CodeGeneratorARM::visitCompareBitwiseAnd jumpToBlock(notEqual, Assembler::NotEqual); masm.cmp32(lhs.payloadReg(), rhs.payloadReg()); emitBranch(cond, lir->ifTrue(), lir->ifFalse()); } void CodeGeneratorARM::visitBitAndAndBranch(LBitAndAndBranch* baab) { + ScratchRegisterScope scratch(masm); if (baab->right()->isConstant()) - masm.ma_tst(ToRegister(baab->left()), Imm32(ToInt32(baab->right()))); + masm.ma_tst(ToRegister(baab->left()), Imm32(ToInt32(baab->right())), scratch); else masm.ma_tst(ToRegister(baab->left()), ToRegister(baab->right())); emitBranch(Assembler::NonZero, baab->ifTrue(), baab->ifFalse()); } void CodeGeneratorARM::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir) { @@ -1601,28 +1643,28 @@ CodeGeneratorARM::visitAsmJSUInt32ToFloa { masm.convertUInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output())); } void CodeGeneratorARM::visitNotI(LNotI* ins) { // It is hard to optimize !x, so just do it the basic way for now. - masm.ma_cmp(ToRegister(ins->input()), Imm32(0)); + masm.as_cmp(ToRegister(ins->input()), Imm8(0)); masm.emitSet(Assembler::Equal, ToRegister(ins->output())); } void CodeGeneratorARM::visitNotI64(LNotI64* lir) { Register64 input = ToRegister64(lir->getInt64Operand(0)); Register output = ToRegister(lir->output()); masm.ma_orr(input.low, input.high, output); - masm.ma_cmp(output, Imm32(0)); + masm.as_cmp(output, Imm8(0)); masm.emitSet(Assembler::Equal, output); } void CodeGeneratorARM::visitNotD(LNotD* ins) { // Since this operation is not, we want to set a bit if the double is // falsey, which means 0.0, -0.0 or NaN. When comparing with 0, an input of @@ -1635,17 +1677,17 @@ CodeGeneratorARM::visitNotD(LNotD* ins) // TODO There are three variations here to compare performance-wise. bool nocond = true; if (nocond) { // Load the value into the dest register. masm.as_vmrs(dest); masm.ma_lsr(Imm32(28), dest, dest); // 28 + 2 = 30 masm.ma_alu(dest, lsr(dest, 2), dest, OpOrr); - masm.ma_and(Imm32(1), dest); + masm.as_and(dest, dest, Imm8(1)); } else { masm.as_vmrs(pc); masm.ma_mov(Imm32(0), dest); masm.ma_mov(Imm32(1), dest, Assembler::Equal); masm.ma_mov(Imm32(1), dest, Assembler::Overflow); } } @@ -1663,60 +1705,64 @@ CodeGeneratorARM::visitNotF(LNotF* ins) // TODO There are three variations here to compare performance-wise. bool nocond = true; if (nocond) { // Load the value into the dest register. masm.as_vmrs(dest); masm.ma_lsr(Imm32(28), dest, dest); // 28 + 2 = 30 masm.ma_alu(dest, lsr(dest, 2), dest, OpOrr); - masm.ma_and(Imm32(1), dest); + masm.as_and(dest, dest, Imm8(1)); } else { masm.as_vmrs(pc); masm.ma_mov(Imm32(0), dest); masm.ma_mov(Imm32(1), dest, Assembler::Equal); masm.ma_mov(Imm32(1), dest, Assembler::Overflow); } } void CodeGeneratorARM::visitGuardShape(LGuardShape* guard) { Register obj = ToRegister(guard->input()); Register tmp = ToRegister(guard->tempInt()); + ScratchRegisterScope scratch(masm); masm.ma_ldr(DTRAddr(obj, DtrOffImm(ShapedObject::offsetOfShape())), tmp); - masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->shape())); + masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->shape()), scratch); bailoutIf(Assembler::NotEqual, guard->snapshot()); } void CodeGeneratorARM::visitGuardObjectGroup(LGuardObjectGroup* guard) { Register obj = ToRegister(guard->input()); Register tmp = ToRegister(guard->tempInt()); MOZ_ASSERT(obj != tmp); + ScratchRegisterScope scratch(masm); masm.ma_ldr(DTRAddr(obj, DtrOffImm(JSObject::offsetOfGroup())), tmp); - masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->group())); + masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->group()), scratch); Assembler::Condition cond = guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual; bailoutIf(cond, guard->snapshot()); } void CodeGeneratorARM::visitGuardClass(LGuardClass* guard) { Register obj = ToRegister(guard->input()); Register tmp = ToRegister(guard->tempInt()); + ScratchRegisterScope scratch(masm); + masm.loadObjClass(obj, tmp); - masm.ma_cmp(tmp, Imm32((uint32_t)guard->mir()->getClass())); + masm.ma_cmp(tmp, Imm32((uint32_t)guard->mir()->getClass()), scratch); bailoutIf(Assembler::NotEqual, guard->snapshot()); } void CodeGeneratorARM::generateInvalidateEpilogue() { // Ensure that there is enough space in the buffer for the OsiPoint patching // to occur. Otherwise, we could overwrite the invalidation epilogue. @@ -2125,17 +2171,17 @@ CodeGeneratorARM::visitAtomicTypedArrayE } void CodeGeneratorARM::visitAsmSelect(LAsmSelect* ins) { MIRType mirType = ins->mir()->type(); Register cond = ToRegister(ins->condExpr()); - masm.ma_cmp(cond, Imm32(0)); + masm.as_cmp(cond, Imm8(0)); if (mirType == MIRType::Int32) { Register falseExpr = ToRegister(ins->falseExpr()); Register out = ToRegister(ins->output()); MOZ_ASSERT(ToRegister(ins->trueExpr()) == out, "true expr input is reused for output"); masm.ma_mov(falseExpr, out, LeaveCC, Assembler::Zero); return; } @@ -2258,57 +2304,60 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAs default: MOZ_CRASH("unexpected array type"); } if (ptr->isConstant()) { MOZ_ASSERT(!mir->needsBoundsCheck()); int32_t ptrImm = ptr->toConstant()->toInt32(); MOZ_ASSERT(ptrImm >= 0); if (isFloat) { + ScratchRegisterScope scratch(masm); VFPRegister vd(ToFloatRegister(ins->output())); if (size == 32) - masm.ma_vldr(Address(HeapReg, ptrImm), vd.singleOverlay(), Assembler::Always); + masm.ma_vldr(Address(HeapReg, ptrImm), vd.singleOverlay(), scratch, Assembler::Always); else - masm.ma_vldr(Address(HeapReg, ptrImm), vd, Assembler::Always); + masm.ma_vldr(Address(HeapReg, ptrImm), vd, scratch, Assembler::Always); } else { + ScratchRegisterScope scratch(masm); masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, Imm32(ptrImm), - ToRegister(ins->output()), Offset, Assembler::Always); + ToRegister(ins->output()), scratch, Offset, Assembler::Always); } } else { + ScratchRegisterScope scratch(masm); Register ptrReg = ToRegister(ptr); if (isFloat) { FloatRegister output = ToFloatRegister(ins->output()); if (size == 32) output = output.singleOverlay(); Assembler::Condition cond = Assembler::Always; if (mir->needsBoundsCheck()) { BufferOffset cmp = masm.as_cmp(ptrReg, Imm8(0)); masm.append(wasm::BoundsCheck(cmp.getOffset())); size_t nanOffset = size == 32 ? wasm::NaN32GlobalDataOffset : wasm::NaN64GlobalDataOffset; - masm.ma_vldr(Address(GlobalReg, nanOffset - AsmJSGlobalRegBias), output, + masm.ma_vldr(Address(GlobalReg, nanOffset - AsmJSGlobalRegBias), output, scratch, Assembler::AboveOrEqual); cond = Assembler::Below; } - masm.ma_vldr(output, HeapReg, ptrReg, 0, cond); + masm.ma_vldr(output, HeapReg, ptrReg, scratch, 0, cond); } else { Register output = ToRegister(ins->output()); Assembler::Condition cond = Assembler::Always; if (mir->needsBoundsCheck()) { uint32_t cmpOffset = masm.as_cmp(ptrReg, Imm8(0)).getOffset(); masm.append(wasm::BoundsCheck(cmpOffset)); masm.ma_mov(Imm32(0), output, Assembler::AboveOrEqual); cond = Assembler::Below; } - masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg, output, Offset, cond); + masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg, output, scratch, Offset, cond); } } } template <typename T> void CodeGeneratorARM::emitWasmLoad(T* lir) { @@ -2317,52 +2366,53 @@ CodeGeneratorARM::emitWasmLoad(T* lir) uint32_t offset = mir->offset(); MOZ_ASSERT(offset < wasm::OffsetGuardLimit); Register ptr = ToRegister(lir->ptr()); Scalar::Type type = mir->accessType(); // Maybe add the offset. if (offset || type == Scalar::Int64) { + ScratchRegisterScope scratch(masm); Register ptrPlusOffset = ToRegister(lir->ptrCopy()); if (offset) - masm.ma_add(Imm32(offset), ptrPlusOffset); + masm.ma_add(Imm32(offset), ptrPlusOffset, scratch); ptr = ptrPlusOffset; } else { MOZ_ASSERT(lir->ptrCopy()->isBogusTemp()); } bool isSigned = type == Scalar::Int8 || type == Scalar::Int16 || type == Scalar::Int32 || type == Scalar::Int64; unsigned byteSize = mir->byteSize(); memoryBarrier(mir->barrierBefore()); if (mir->type() == MIRType::Int64) { Register64 output = ToOutRegister64(lir); if (type == Scalar::Int64) { MOZ_ASSERT(INT64LOW_OFFSET == 0); masm.ma_dataTransferN(IsLoad, 32, /* signed = */ false, HeapReg, ptr, output.low); - masm.ma_add(Imm32(INT64HIGH_OFFSET), ptr); + masm.as_add(ptr, ptr, Imm8(INT64HIGH_OFFSET)); masm.ma_dataTransferN(IsLoad, 32, isSigned, HeapReg, ptr, output.high); } else { masm.ma_dataTransferN(IsLoad, byteSize * 8, isSigned, HeapReg, ptr, output.low); if (isSigned) masm.ma_asr(Imm32(31), output.low, output.high); else masm.ma_mov(Imm32(0), output.high); } } else { AnyRegister output = ToAnyRegister(lir->output()); bool isFloat = output.isFloat(); if (isFloat) { MOZ_ASSERT((byteSize == 4) == output.fpu().isSingle()); ScratchRegisterScope scratch(masm); masm.ma_add(HeapReg, ptr, scratch); - masm.ma_vldr(Address(scratch, 0), output.fpu()); + masm.ma_vldr(Operand(Address(scratch, 0)).toVFPAddr(), output.fpu()); } else { masm.ma_dataTransferN(IsLoad, byteSize * 8, isSigned, HeapReg, ptr, output.gpr()); } } memoryBarrier(mir->barrierAfter()); } @@ -2383,18 +2433,20 @@ void CodeGeneratorARM::emitWasmUnalignedLoad(T* lir) { const MWasmLoad* mir = lir->mir(); uint32_t offset = mir->offset(); MOZ_ASSERT(offset < wasm::OffsetGuardLimit); Register ptr = ToRegister(lir->ptrCopy()); - if (offset) - masm.ma_add(Imm32(offset), ptr); + if (offset) { + ScratchRegisterScope scratch(masm); + masm.ma_add(Imm32(offset), ptr, scratch); + } // Add HeapReg to ptr, so we can use base+index addressing in the byte loads. masm.ma_add(HeapReg, ptr); unsigned byteSize = mir->byteSize(); Scalar::Type type = mir->accessType(); bool isSigned = type == Scalar::Int8 || type == Scalar::Int16 || type == Scalar::Int32 || type == Scalar::Int64; @@ -2462,17 +2514,18 @@ CodeGeneratorARM::visitWasmUnalignedLoad void CodeGeneratorARM::visitWasmAddOffset(LWasmAddOffset* lir) { MWasmAddOffset* mir = lir->mir(); Register base = ToRegister(lir->base()); Register out = ToRegister(lir->output()); - masm.ma_add(base, Imm32(mir->offset()), out, SetCC); + ScratchRegisterScope scratch(masm); + masm.ma_add(base, Imm32(mir->offset()), out, scratch, SetCC); masm.ma_b(wasm::JumpTarget::OutOfBounds, Assembler::CarrySet); } template <typename T> void CodeGeneratorARM::emitWasmStore(T* lir) { const MWasmStore* mir = lir->mir(); @@ -2481,41 +2534,42 @@ CodeGeneratorARM::emitWasmStore(T* lir) MOZ_ASSERT(offset < wasm::OffsetGuardLimit); Register ptr = ToRegister(lir->ptr()); unsigned byteSize = mir->byteSize(); Scalar::Type type = mir->accessType(); // Maybe add the offset. if (offset || type == Scalar::Int64) { + ScratchRegisterScope scratch(masm); Register ptrPlusOffset = ToRegister(lir->ptrCopy()); if (offset) - masm.ma_add(Imm32(offset), ptrPlusOffset); + masm.ma_add(Imm32(offset), ptrPlusOffset, scratch); ptr = ptrPlusOffset; } else { MOZ_ASSERT(lir->ptrCopy()->isBogusTemp()); } memoryBarrier(mir->barrierBefore()); if (type == Scalar::Int64) { MOZ_ASSERT(INT64LOW_OFFSET == 0); Register64 value = ToRegister64(lir->getInt64Operand(lir->ValueIndex)); masm.ma_dataTransferN(IsStore, 32 /* bits */, /* signed */ false, HeapReg, ptr, value.low); - masm.ma_add(Imm32(INT64HIGH_OFFSET), ptr); + masm.as_add(ptr, ptr, Imm8(INT64HIGH_OFFSET)); masm.ma_dataTransferN(IsStore, 32 /* bits */, /* signed */ true, HeapReg, ptr, value.high); } else { AnyRegister value = ToAnyRegister(lir->getOperand(lir->ValueIndex)); if (value.isFloat()) { + ScratchRegisterScope scratch(masm); FloatRegister val = value.fpu(); MOZ_ASSERT((byteSize == 4) == val.isSingle()); - ScratchRegisterScope scratch(masm); masm.ma_add(HeapReg, ptr, scratch); - masm.ma_vstr(val, Address(scratch, 0)); + masm.ma_vstr(val, Operand(Address(scratch, 0)).toVFPAddr()); } else { bool isSigned = type == Scalar::Uint32 || type == Scalar::Int32; // see AsmJSStoreHeap; Register val = value.gpr(); masm.ma_dataTransferN(IsStore, 8 * byteSize /* bits */, isSigned, HeapReg, ptr, val); } } memoryBarrier(mir->barrierAfter()); @@ -2538,18 +2592,20 @@ void CodeGeneratorARM::emitWasmUnalignedStore(T* lir) { const MWasmStore* mir = lir->mir(); uint32_t offset = mir->offset(); MOZ_ASSERT(offset < wasm::OffsetGuardLimit); Register ptr = ToRegister(lir->ptrCopy()); - if (offset) - masm.ma_add(Imm32(offset), ptr); + if (offset) { + ScratchRegisterScope scratch(masm); + masm.ma_add(Imm32(offset), ptr, scratch); + } // Add HeapReg to ptr, so we can use base+index addressing in the byte loads. masm.ma_add(HeapReg, ptr); MIRType mirType = mir->value()->type(); memoryBarrier(mir->barrierAfter()); @@ -2625,39 +2681,42 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LA if (isFloat) { VFPRegister vd(ToFloatRegister(ins->value())); Address addr(HeapReg, ptrImm); if (size == 32) masm.storeFloat32(vd, addr); else masm.storeDouble(vd, addr); } else { + ScratchRegisterScope scratch(masm); masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, Imm32(ptrImm), - ToRegister(ins->value()), Offset, Assembler::Always); + ToRegister(ins->value()), scratch, Offset, Assembler::Always); } } else { Register ptrReg = ToRegister(ptr); Assembler::Condition cond = Assembler::Always; if (mir->needsBoundsCheck()) { BufferOffset cmp = masm.as_cmp(ptrReg, Imm8(0)); masm.append(wasm::BoundsCheck(cmp.getOffset())); cond = Assembler::Below; } if (isFloat) { + ScratchRegisterScope scratch(masm); FloatRegister value = ToFloatRegister(ins->value()); if (size == 32) value = value.singleOverlay(); - masm.ma_vstr(value, HeapReg, ptrReg, 0, 0, Assembler::Below); + masm.ma_vstr(value, HeapReg, ptrReg, scratch, 0, Assembler::Below); } else { + ScratchRegisterScope scratch(masm); Register value = ToRegister(ins->value()); - masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg, value, Offset, cond); + masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg, value, scratch, Offset, cond); } } } void CodeGeneratorARM::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins) { MAsmJSCompareExchangeHeap* mir = ins->mir(); @@ -2836,23 +2895,27 @@ CodeGeneratorARM::visitAsmJSAtomicBinopC } } void CodeGeneratorARM::visitAsmJSPassStackArg(LAsmJSPassStackArg* ins) { const MAsmJSPassStackArg* mir = ins->mir(); Address dst(StackPointer, mir->spOffset()); + ScratchRegisterScope scratch(masm); + SecondScratchRegisterScope scratch2(masm); + if (ins->arg()->isConstant()) { - masm.ma_storeImm(Imm32(ToInt32(ins->arg())), dst); + masm.ma_mov(Imm32(ToInt32(ins->arg())), scratch); + masm.ma_str(scratch, dst, scratch2); } else { if (ins->arg()->isGeneralReg()) - masm.ma_str(ToRegister(ins->arg()), dst); + masm.ma_str(ToRegister(ins->arg()), dst, scratch); else - masm.ma_vstr(ToFloatRegister(ins->arg()), dst); + masm.ma_vstr(ToFloatRegister(ins->arg()), dst, scratch); } } void CodeGeneratorARM::visitUDiv(LUDiv* ins) { Register lhs = ToRegister(ins->lhs()); Register rhs = ToRegister(ins->rhs()); @@ -2861,17 +2924,17 @@ CodeGeneratorARM::visitUDiv(LUDiv* ins) Label done; generateUDivModZeroCheck(rhs, output, &done, ins->snapshot(), ins->mir()); masm.ma_udiv(lhs, rhs, output); // Check for large unsigned result - represent as double. if (!ins->mir()->isTruncated()) { MOZ_ASSERT(ins->mir()->fallible()); - masm.ma_cmp(output, Imm32(0)); + masm.as_cmp(output, Imm8(0)); bailoutIf(Assembler::LessThan, ins->snapshot()); } // Check for non-zero remainder if not truncating to int. if (!ins->mir()->canTruncateRemainder()) { MOZ_ASSERT(ins->mir()->fallible()); { ScratchRegisterScope scratch(masm); @@ -2890,38 +2953,41 @@ CodeGeneratorARM::visitUMod(LUMod* ins) { Register lhs = ToRegister(ins->lhs()); Register rhs = ToRegister(ins->rhs()); Register output = ToRegister(ins->output()); Label done; generateUDivModZeroCheck(rhs, output, &done, ins->snapshot(), ins->mir()); - masm.ma_umod(lhs, rhs, output); + { + ScratchRegisterScope scratch(masm); + masm.ma_umod(lhs, rhs, output, scratch); + } // Check for large unsigned result - represent as double. if (!ins->mir()->isTruncated()) { MOZ_ASSERT(ins->mir()->fallible()); - masm.ma_cmp(output, Imm32(0)); + masm.as_cmp(output, Imm8(0)); bailoutIf(Assembler::LessThan, ins->snapshot()); } if (done.used()) masm.bind(&done); } template<class T> void CodeGeneratorARM::generateUDivModZeroCheck(Register rhs, Register output, Label* done, LSnapshot* snapshot, T* mir) { if (!mir) return; if (mir->canBeDivideByZero()) { - masm.ma_cmp(rhs, Imm32(0)); + masm.as_cmp(rhs, Imm8(0)); if (mir->isTruncated()) { if (mir->trapOnError()) { masm.ma_b(wasm::JumpTarget::IntegerDivideByZero, Assembler::Equal); } else { Label skip; masm.ma_b(&skip, Assembler::NotEqual); // Infinity|0 == 0 masm.ma_mov(Imm32(0), output); @@ -2962,98 +3028,108 @@ CodeGeneratorARM::visitSoftUDivOrMod(LSo if (gen->compilingAsmJS()) masm.callWithABI(wasm::SymbolicAddress::aeabi_uidivmod); else 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()) { MOZ_ASSERT(div->fallible()); - masm.ma_cmp(r1, Imm32(0)); + masm.as_cmp(r1, Imm8(0)); bailoutIf(Assembler::NonZero, ins->snapshot()); } // Bailout for big unsigned results if ((div && !div->isTruncated()) || (mod && !mod->isTruncated())) { DebugOnly<bool> isFallible = (div && div->fallible()) || (mod && mod->fallible()); MOZ_ASSERT(isFallible); - masm.ma_cmp(output, Imm32(0)); + masm.as_cmp(output, Imm8(0)); bailoutIf(Assembler::LessThan, ins->snapshot()); } masm.bind(&done); } void CodeGeneratorARM::visitEffectiveAddress(LEffectiveAddress* ins) { const MEffectiveAddress* mir = ins->mir(); Register base = ToRegister(ins->base()); Register index = ToRegister(ins->index()); Register output = ToRegister(ins->output()); + + ScratchRegisterScope scratch(masm); + masm.as_add(output, base, lsl(index, mir->scale())); - masm.ma_add(Imm32(mir->displacement()), output); + masm.ma_add(Imm32(mir->displacement()), output, scratch); } void CodeGeneratorARM::visitWasmLoadGlobalVar(LWasmLoadGlobalVar* ins) { const MWasmLoadGlobalVar* mir = ins->mir(); unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; + + ScratchRegisterScope scratch(masm); + if (mir->type() == MIRType::Int32) { - masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr), ToRegister(ins->output())); + masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr), ToRegister(ins->output()), scratch); } else if (mir->type() == MIRType::Float32) { VFPRegister vd(ToFloatRegister(ins->output())); - masm.ma_vldr(Address(GlobalReg, addr), vd.singleOverlay()); + masm.ma_vldr(Address(GlobalReg, addr), vd.singleOverlay(), scratch); } else { MOZ_ASSERT(mir->type() == MIRType::Double); - masm.ma_vldr(Address(GlobalReg, addr), ToFloatRegister(ins->output())); + masm.ma_vldr(Address(GlobalReg, addr), ToFloatRegister(ins->output()), scratch); } } void CodeGeneratorARM::visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64* ins) { const MWasmLoadGlobalVar* mir = ins->mir(); unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; MOZ_ASSERT(mir->type() == MIRType::Int64); Register64 output = ToOutRegister64(ins); - masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr + INT64LOW_OFFSET), output.low); - masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr + INT64HIGH_OFFSET), output.high); + ScratchRegisterScope scratch(masm); + masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr + INT64LOW_OFFSET), output.low, scratch); + masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr + INT64HIGH_OFFSET), output.high, scratch); } void CodeGeneratorARM::visitWasmStoreGlobalVar(LWasmStoreGlobalVar* ins) { const MWasmStoreGlobalVar* mir = ins->mir(); MIRType type = mir->value()->type(); + ScratchRegisterScope scratch(masm); + unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; if (type == MIRType::Int32) { - masm.ma_dtr(IsStore, GlobalReg, Imm32(addr), ToRegister(ins->value())); + masm.ma_dtr(IsStore, GlobalReg, Imm32(addr), ToRegister(ins->value()), scratch); } else if (type == MIRType::Float32) { VFPRegister vd(ToFloatRegister(ins->value())); - masm.ma_vstr(vd.singleOverlay(), Address(GlobalReg, addr)); + masm.ma_vstr(vd.singleOverlay(), Address(GlobalReg, addr), scratch); } else { MOZ_ASSERT(type == MIRType::Double); - masm.ma_vstr(ToFloatRegister(ins->value()), Address(GlobalReg, addr)); + masm.ma_vstr(ToFloatRegister(ins->value()), Address(GlobalReg, addr), scratch); } } void CodeGeneratorARM::visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64* ins) { const MWasmStoreGlobalVar* mir = ins->mir(); unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; MOZ_ASSERT (mir->value()->type() == MIRType::Int64); Register64 input = ToRegister64(ins->value()); - masm.ma_dtr(IsStore, GlobalReg, Imm32(addr + INT64LOW_OFFSET), input.low); - masm.ma_dtr(IsStore, GlobalReg, Imm32(addr + INT64HIGH_OFFSET), input.high); + ScratchRegisterScope scratch(masm); + masm.ma_dtr(IsStore, GlobalReg, Imm32(addr + INT64LOW_OFFSET), input.low, scratch); + masm.ma_dtr(IsStore, GlobalReg, Imm32(addr + INT64HIGH_OFFSET), input.high, scratch); } void CodeGeneratorARM::visitNegI(LNegI* ins) { Register input = ToRegister(ins->input()); masm.ma_neg(input, ToRegister(ins->output())); } @@ -3123,52 +3199,53 @@ CodeGeneratorARM::visitWasmTruncateToInt masm.compareFloat(input, input); else MOZ_CRASH("unexpected type in visitWasmTruncateToInt32"); masm.ma_b(ool->entry(), Assembler::VFP_Unordered); } ScratchDoubleScope scratchScope(masm); + ScratchRegisterScope scratchReg(masm); FloatRegister scratch = scratchScope.uintOverlay(); // ARM conversion instructions clamp the value to ensure it fits within the // target's type bounds, so every time we see those, we need to check the // input. if (mir->isUnsigned()) { if (fromType == MIRType::Double) masm.ma_vcvt_F64_U32(input, scratch); else if (fromType == MIRType::Float32) masm.ma_vcvt_F32_U32(input, scratch); else MOZ_CRASH("unexpected type in visitWasmTruncateToInt32"); masm.ma_vxfer(scratch, output); // int32_t(UINT32_MAX) == -1. - masm.ma_cmp(output, Imm32(-1)); - masm.ma_cmp(output, Imm32(0), Assembler::NotEqual); + masm.ma_cmp(output, Imm32(-1), scratchReg); + masm.as_cmp(output, Imm8(0), Assembler::NotEqual); masm.ma_b(ool->entry(), Assembler::Equal); masm.bind(ool->rejoin()); return; } scratch = scratchScope.sintOverlay(); if (fromType == MIRType::Double) masm.ma_vcvt_F64_I32(input, scratch); else if (fromType == MIRType::Float32) masm.ma_vcvt_F32_I32(input, scratch); else MOZ_CRASH("unexpected type in visitWasmTruncateToInt32"); masm.ma_vxfer(scratch, output); - masm.ma_cmp(output, Imm32(INT32_MAX)); - masm.ma_cmp(output, Imm32(INT32_MIN), Assembler::NotEqual); + masm.ma_cmp(output, Imm32(INT32_MAX), scratchReg); + masm.ma_cmp(output, Imm32(INT32_MIN), scratchReg, Assembler::NotEqual); masm.ma_b(ool->entry(), Assembler::Equal); masm.bind(ool->rejoin()); } void CodeGeneratorARM::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir) { @@ -3194,18 +3271,19 @@ CodeGeneratorARM::visitWasmTruncateToInt masm.passABIArg(inputDouble, MoveOp::DOUBLE); if (lir->mir()->isUnsigned()) masm.callWithABI(wasm::SymbolicAddress::TruncateDoubleToUint64); else masm.callWithABI(wasm::SymbolicAddress::TruncateDoubleToInt64); masm.Pop(input); - masm.ma_cmp(output.high, Imm32(0x80000000)); - masm.ma_cmp(output.low, Imm32(0x00000000), Assembler::Equal); + ScratchRegisterScope scratch(masm); + masm.ma_cmp(output.high, Imm32(0x80000000), scratch); + masm.as_cmp(output.low, Imm8(0x00000000), Assembler::Equal); masm.ma_b(ool->entry(), Assembler::Equal); masm.bind(ool->rejoin()); MOZ_ASSERT(ReturnReg64 == output); } void @@ -3331,21 +3409,23 @@ CodeGeneratorARM::visitCopySignF(LCopySi FloatRegister output = ToFloatRegister(ins->getDef(0)); Register lhsi = ToRegister(ins->getTemp(0)); Register rhsi = ToRegister(ins->getTemp(1)); masm.ma_vxfer(lhs, lhsi); masm.ma_vxfer(rhs, rhsi); + ScratchRegisterScope scratch(masm); + // Clear lhs's sign. - masm.ma_and(Imm32(INT32_MAX), lhsi, lhsi); + masm.ma_and(Imm32(INT32_MAX), lhsi, lhsi, scratch); // Keep rhs's sign. - masm.ma_and(Imm32(INT32_MIN), rhsi, rhsi); + masm.ma_and(Imm32(INT32_MIN), rhsi, rhsi, scratch); // Combine. masm.ma_orr(lhsi, rhsi, rhsi); masm.ma_vxfer(rhsi, output); } void @@ -3357,21 +3437,23 @@ CodeGeneratorARM::visitCopySignD(LCopySi Register lhsi = ToRegister(ins->getTemp(0)); Register rhsi = ToRegister(ins->getTemp(1)); // Manipulate high words of double inputs. masm.as_vxfer(lhsi, InvalidReg, lhs, Assembler::FloatToCore, Assembler::Always, 1); masm.as_vxfer(rhsi, InvalidReg, rhs, Assembler::FloatToCore, Assembler::Always, 1); + ScratchRegisterScope scratch(masm); + // Clear lhs's sign. - masm.ma_and(Imm32(INT32_MAX), lhsi, lhsi); + masm.ma_and(Imm32(INT32_MAX), lhsi, lhsi, scratch); // Keep rhs's sign. - masm.ma_and(Imm32(INT32_MIN), rhsi, rhsi); + masm.ma_and(Imm32(INT32_MIN), rhsi, rhsi, scratch); // Combine. masm.ma_orr(lhsi, rhsi, rhsi); // Reconstruct the output. masm.as_vxfer(lhsi, InvalidReg, lhs, Assembler::FloatToCore, Assembler::Always, 0); masm.ma_vxfer(lhsi, rhsi, output); } @@ -3675,23 +3757,24 @@ void CodeGeneratorARM::visitAsmSelectI64(LAsmSelectI64* lir) { Register cond = ToRegister(lir->condExpr()); const LInt64Allocation falseExpr = lir->falseExpr(); Register64 out = ToOutRegister64(lir); MOZ_ASSERT(ToRegister64(lir->trueExpr()) == out, "true expr is reused for input"); - masm.ma_cmp(cond, Imm32(0)); + masm.as_cmp(cond, Imm8(0)); if (falseExpr.low().isRegister()) { masm.ma_mov(ToRegister(falseExpr.low()), out.low, LeaveCC, Assembler::Equal); masm.ma_mov(ToRegister(falseExpr.high()), out.high, LeaveCC, Assembler::Equal); } else { - masm.ma_ldr(ToAddress(falseExpr.low()), out.low, Offset, Assembler::Equal); - masm.ma_ldr(ToAddress(falseExpr.high()), out.high, Offset, Assembler::Equal); + ScratchRegisterScope scratch(masm); + masm.ma_ldr(ToAddress(falseExpr.low()), out.low, scratch, Offset, Assembler::Equal); + masm.ma_ldr(ToAddress(falseExpr.high()), out.high, scratch, Offset, Assembler::Equal); } } void CodeGeneratorARM::visitAsmReinterpretFromI64(LAsmReinterpretFromI64* lir) { MOZ_ASSERT(lir->mir()->type() == MIRType::Double); MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Int64); @@ -3742,13 +3825,13 @@ CodeGeneratorARM::visitCtzI64(LCtzI64* l masm.move32(Imm32(0), output.high); } void CodeGeneratorARM::visitTestI64AndBranch(LTestI64AndBranch* lir) { Register64 input = ToRegister64(lir->getInt64Operand(0)); - masm.ma_cmp(input.high, Imm32(0)); + masm.as_cmp(input.high, Imm8(0)); jumpToBlock(lir->ifTrue(), Assembler::NonZero); - masm.ma_cmp(input.low, Imm32(0)); + masm.as_cmp(input.low, Imm8(0)); emitBranch(Assembler::NonZero, lir->ifTrue(), lir->ifFalse()); }
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h +++ b/js/src/jit/arm/MacroAssembler-arm-inl.h @@ -65,46 +65,52 @@ void MacroAssembler::and32(Register src, Register dest) { ma_and(src, dest, SetCC); } void MacroAssembler::and32(Imm32 imm, Register dest) { - ma_and(imm, dest, SetCC); + ScratchRegisterScope scratch(*this); + ma_and(imm, dest, scratch, SetCC); } void MacroAssembler::and32(Imm32 imm, const Address& dest) { ScratchRegisterScope scratch(*this); - load32(dest, scratch); - ma_and(imm, scratch); - store32(scratch, dest); + SecondScratchRegisterScope scratch2(*this); + + ma_ldr(dest, scratch, scratch2); + ma_and(imm, scratch, scratch2); + ma_str(scratch, dest, scratch2); } void MacroAssembler::and32(const Address& src, Register dest) { ScratchRegisterScope scratch(*this); - load32(src, scratch); + SecondScratchRegisterScope scratch2(*this); + + ma_ldr(src, scratch, scratch2); ma_and(scratch, dest, SetCC); } void MacroAssembler::andPtr(Register src, Register dest) { ma_and(src, dest); } void MacroAssembler::andPtr(Imm32 imm, Register dest) { - ma_and(imm, dest); + ScratchRegisterScope scratch(*this); + ma_and(imm, dest, scratch); } void MacroAssembler::and64(Imm64 imm, Register64 dest) { if (imm.low().value != int32_t(0xFFFFFFFF)) and32(imm.low(), dest.low); if (imm.hi().value != int32_t(0xFFFFFFFF)) @@ -133,38 +139,42 @@ void MacroAssembler::or32(Register src, Register dest) { ma_orr(src, dest); } void MacroAssembler::or32(Imm32 imm, Register dest) { - ma_orr(imm, dest); + ScratchRegisterScope scratch(*this); + ma_orr(imm, dest, scratch); } void MacroAssembler::or32(Imm32 imm, const Address& dest) { ScratchRegisterScope scratch(*this); - load32(dest, scratch); - ma_orr(imm, scratch); - store32(scratch, dest); + SecondScratchRegisterScope scratch2(*this); + + ma_ldr(dest, scratch, scratch2); + ma_orr(imm, scratch, scratch2); + ma_str(scratch, dest, scratch2); } void MacroAssembler::orPtr(Register src, Register dest) { ma_orr(src, dest); } void MacroAssembler::orPtr(Imm32 imm, Register dest) { - ma_orr(imm, dest); + ScratchRegisterScope scratch(*this); + ma_orr(imm, dest, scratch); } void MacroAssembler::and64(Register64 src, Register64 dest) { and32(src.low, dest.low); and32(src.high, dest.high); } @@ -187,109 +197,121 @@ void MacroAssembler::xor32(Register src, Register dest) { ma_eor(src, dest, SetCC); } void MacroAssembler::xor32(Imm32 imm, Register dest) { - ma_eor(imm, dest, SetCC); + ScratchRegisterScope scratch(*this); + ma_eor(imm, dest, scratch, SetCC); } void MacroAssembler::xorPtr(Register src, Register dest) { ma_eor(src, dest); } void MacroAssembler::xorPtr(Imm32 imm, Register dest) { - ma_eor(imm, dest); + ScratchRegisterScope scratch(*this); + ma_eor(imm, dest, scratch); } // =============================================================== // Arithmetic functions void MacroAssembler::add32(Register src, Register dest) { ma_add(src, dest, SetCC); } void MacroAssembler::add32(Imm32 imm, Register dest) { - ma_add(imm, dest, SetCC); + ScratchRegisterScope scratch(*this); + ma_add(imm, dest, scratch, SetCC); } void MacroAssembler::add32(Imm32 imm, const Address& dest) { ScratchRegisterScope scratch(*this); - load32(dest, scratch); - ma_add(imm, scratch, SetCC); - store32(scratch, dest); + SecondScratchRegisterScope scratch2(*this); + + ma_ldr(dest, scratch, scratch2); + ma_add(imm, scratch, scratch2, SetCC); + ma_str(scratch, dest, scratch2); } void MacroAssembler::addPtr(Register src, Register dest) { ma_add(src, dest); } void MacroAssembler::addPtr(Imm32 imm, Register dest) { - ma_add(imm, dest); + ScratchRegisterScope scratch(*this); + ma_add(imm, dest, scratch); } void MacroAssembler::addPtr(ImmWord imm, Register dest) { addPtr(Imm32(imm.value), dest); } void MacroAssembler::addPtr(Imm32 imm, const Address& dest) { ScratchRegisterScope scratch(*this); - loadPtr(dest, scratch); - addPtr(imm, scratch); - storePtr(scratch, dest); + SecondScratchRegisterScope scratch2(*this); + + ma_ldr(dest, scratch, scratch2); + ma_add(imm, scratch, scratch2); + ma_str(scratch, dest, scratch2); } void MacroAssembler::addPtr(const Address& src, Register dest) { ScratchRegisterScope scratch(*this); - load32(src, scratch); + SecondScratchRegisterScope scratch2(*this); + + ma_ldr(src, scratch, scratch2); ma_add(scratch, dest, SetCC); } void MacroAssembler::add64(Register64 src, Register64 dest) { ma_add(src.low, dest.low, SetCC); ma_adc(src.high, dest.high); } void MacroAssembler::add64(Imm32 imm, Register64 dest) { - ma_add(imm, dest.low, SetCC); - ma_adc(Imm32(0), dest.high, LeaveCC); + ScratchRegisterScope scratch(*this); + ma_add(imm, dest.low, scratch, SetCC); + as_adc(dest.high, dest.high, Imm8(0), LeaveCC); } void MacroAssembler::add64(Imm64 imm, Register64 dest) { - ma_add(imm.low(), dest.low, SetCC); - ma_adc(imm.hi(), dest.high, LeaveCC); + ScratchRegisterScope scratch(*this); + ma_add(imm.low(), dest.low, scratch, SetCC); + ma_adc(imm.hi(), dest.high, scratch, LeaveCC); } void MacroAssembler::addDouble(FloatRegister src, FloatRegister dest) { ma_vadd(dest, src, dest); } @@ -303,68 +325,77 @@ void MacroAssembler::sub32(Register src, Register dest) { ma_sub(src, dest, SetCC); } void MacroAssembler::sub32(Imm32 imm, Register dest) { - ma_sub(imm, dest, SetCC); + ScratchRegisterScope scratch(*this); + ma_sub(imm, dest, scratch, SetCC); } void MacroAssembler::sub32(const Address& src, Register dest) { ScratchRegisterScope scratch(*this); - load32(src, scratch); + SecondScratchRegisterScope scratch2(*this); + + ma_ldr(src, scratch, scratch2); ma_sub(scratch, dest, SetCC); } void MacroAssembler::subPtr(Register src, Register dest) { ma_sub(src, dest); } void MacroAssembler::subPtr(Register src, const Address& dest) { ScratchRegisterScope scratch(*this); - loadPtr(dest, scratch); + SecondScratchRegisterScope scratch2(*this); + + ma_ldr(dest, scratch, scratch2); ma_sub(src, scratch); - storePtr(scratch, dest); + ma_str(scratch, dest, scratch2); } void MacroAssembler::subPtr(Imm32 imm, Register dest) { - ma_sub(imm, dest); + ScratchRegisterScope scratch(*this); + ma_sub(imm, dest, scratch); } void MacroAssembler::subPtr(const Address& addr, Register dest) { ScratchRegisterScope scratch(*this); - loadPtr(addr, scratch); + SecondScratchRegisterScope scratch2(*this); + + ma_ldr(addr, scratch, scratch2); ma_sub(scratch, dest); } void MacroAssembler::sub64(Register64 src, Register64 dest) { ma_sub(src.low, dest.low, SetCC); ma_sbc(src.high, dest.high, LeaveCC); } void MacroAssembler::sub64(Imm64 imm, Register64 dest) { - ma_sub(imm.low(), dest.low, SetCC); - ma_sbc(imm.hi(), dest.high, LeaveCC); + ScratchRegisterScope scratch(*this); + ma_sub(imm.low(), dest.low, scratch, SetCC); + ma_sbc(imm.hi(), dest.high, scratch, LeaveCC); } void MacroAssembler::subDouble(FloatRegister src, FloatRegister dest) { ma_vsub(dest, src, dest); } @@ -383,52 +414,56 @@ MacroAssembler::mul32(Register rhs, Regi void MacroAssembler::mul64(Imm64 imm, const Register64& dest) { // LOW32 = LOW(LOW(dest) * LOW(imm)); // HIGH32 = LOW(HIGH(dest) * LOW(imm)) [multiply imm into upper bits] // + LOW(LOW(dest) * HIGH(imm)) [multiply dest into upper bits] // + HIGH(LOW(dest) * LOW(imm)) [carry] + ScratchRegisterScope scratch(*this); + SecondScratchRegisterScope scratch2(*this); + // HIGH(dest) = LOW(HIGH(dest) * LOW(imm)); - ma_mov(Imm32(imm.value & 0xFFFFFFFFL), ScratchRegister); - as_mul(dest.high, dest.high, ScratchRegister); + ma_mov(Imm32(imm.value & 0xFFFFFFFFL), scratch); + as_mul(dest.high, dest.high, scratch); // high:low = LOW(dest) * LOW(imm); - as_umull(secondScratchReg_, ScratchRegister, dest.low, ScratchRegister); + as_umull(scratch2, scratch, dest.low, scratch); // HIGH(dest) += high; - as_add(dest.high, dest.high, O2Reg(secondScratchReg_)); + as_add(dest.high, dest.high, O2Reg(scratch2)); // HIGH(dest) += LOW(LOW(dest) * HIGH(imm)); if (((imm.value >> 32) & 0xFFFFFFFFL) == 5) - as_add(secondScratchReg_, dest.low, lsl(dest.low, 2)); + as_add(scratch2, dest.low, lsl(dest.low, 2)); else MOZ_CRASH("Not supported imm"); - as_add(dest.high, dest.high, O2Reg(secondScratchReg_)); + as_add(dest.high, dest.high, O2Reg(scratch2)); // LOW(dest) = low; - ma_mov(ScratchRegister, dest.low); + ma_mov(scratch, dest.low); } void MacroAssembler::mul64(Imm64 imm, const Register64& dest, const Register temp) { // LOW32 = LOW(LOW(dest) * LOW(src)); (1) // HIGH32 = LOW(HIGH(dest) * LOW(src)) [multiply src into upper bits] (2) // + LOW(LOW(dest) * HIGH(src)) [multiply dest into upper bits] (3) // + HIGH(LOW(dest) * LOW(src)) [carry] (4) MOZ_ASSERT(temp != dest.high && temp != dest.low); // Compute mul64 - ma_mul(dest.high, imm.low(), dest.high); // (2) - ma_mul(dest.low, imm.hi(), temp); // (3) + ScratchRegisterScope scratch(*this); + ma_mul(dest.high, imm.low(), dest.high, scratch); // (2) + ma_mul(dest.low, imm.hi(), temp, scratch); // (3) ma_add(dest.high, temp, temp); - ma_umull(dest.low, imm.low(), dest.high, dest.low); // (4) + (1) + ma_umull(dest.low, imm.low(), dest.high, dest.low, scratch); // (4) + (1) ma_add(temp, dest.high, dest.high); } void MacroAssembler::mul64(const Register64& src, const Register64& dest, const Register temp) { // LOW32 = LOW(LOW(dest) * LOW(src)); (1) // HIGH32 = LOW(HIGH(dest) * LOW(src)) [multiply src into upper bits] (2) @@ -462,39 +497,44 @@ void MacroAssembler::mulDouble(FloatRegister src, FloatRegister dest) { ma_vmul(dest, src, dest); } void MacroAssembler::mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) { - movePtr(imm, ScratchRegister); - loadDouble(Address(ScratchRegister, 0), ScratchDoubleReg); - mulDouble(ScratchDoubleReg, dest); + ScratchRegisterScope scratch(*this); + ScratchDoubleScope scratchDouble(*this); + + movePtr(imm, scratch); + ma_vldr(Operand(Address(scratch, 0)).toVFPAddr(), scratchDouble); + mulDouble(scratchDouble, dest); } void MacroAssembler::quotient32(Register rhs, Register srcDest, bool isUnsigned) { MOZ_ASSERT(HasIDIV()); if (isUnsigned) ma_udiv(srcDest, rhs, srcDest); else ma_sdiv(srcDest, rhs, srcDest); } void MacroAssembler::remainder32(Register rhs, Register srcDest, bool isUnsigned) { MOZ_ASSERT(HasIDIV()); + + ScratchRegisterScope scratch(*this); if (isUnsigned) - ma_umod(srcDest, rhs, srcDest); + ma_umod(srcDest, rhs, srcDest, scratch); else - ma_smod(srcDest, rhs, srcDest); + ma_smod(srcDest, rhs, srcDest, scratch); } void MacroAssembler::divFloat32(FloatRegister src, FloatRegister dest) { ma_vdiv_f32(dest, src, dest); } @@ -509,34 +549,34 @@ MacroAssembler::inc64(AbsoluteAddress de { ScratchRegisterScope scratch(*this); ma_strd(r0, r1, EDtrAddr(sp, EDtrOffImm(-8)), PreIndex); ma_mov(Imm32((int32_t)dest.addr), scratch); ma_ldrd(EDtrAddr(scratch, EDtrOffImm(0)), r0, r1); - ma_add(Imm32(1), r0, SetCC); - ma_adc(Imm32(0), r1, LeaveCC); + as_add(r0, r0, Imm8(1), SetCC); + as_adc(r1, r1, Imm8(0), LeaveCC); ma_strd(r0, r1, EDtrAddr(scratch, EDtrOffImm(0))); ma_ldrd(EDtrAddr(sp, EDtrOffImm(8)), r0, r1, PostIndex); } void MacroAssembler::neg32(Register reg) { ma_neg(reg, reg, SetCC); } void MacroAssembler::neg64(Register64 reg) { - ma_rsb(Imm32(0), reg.low, SetCC); - ma_rsc(Imm32(0), reg.high); + as_rsb(reg.low, reg.low, Imm8(0), SetCC); + as_rsc(reg.high, reg.high, Imm8(0)); } void MacroAssembler::negateDouble(FloatRegister reg) { ma_vneg(reg, reg); } @@ -626,23 +666,23 @@ MacroAssembler::lshift64(Imm32 imm, Regi void MacroAssembler::lshift64(Register unmaskedShift, Register64 dest) { // dest.high = dest.high << shift | dest.low << shift - 32 | dest.low >> 32 - shift // Note: one of the two dest.low shift will always yield zero due to negative shift. ScratchRegisterScope shift(*this); - ma_and(Imm32(0x3f), unmaskedShift, shift); + as_and(shift, unmaskedShift, Imm8(0x3f)); as_mov(dest.high, lsl(dest.high, shift)); - ma_sub(shift, Imm32(32), shift); + as_sub(shift, shift, Imm8(32)); as_orr(dest.high, dest.high, lsl(dest.low, shift)); ma_neg(shift, shift); as_orr(dest.high, dest.high, lsr(dest.low, shift)); - ma_and(Imm32(0x3f), unmaskedShift, shift); + as_and(shift, unmaskedShift, Imm8(0x3f)); as_mov(dest.low, lsl(dest.low, shift)); } void MacroAssembler::lshift32(Register src, Register dest) { ma_lsl(src, dest, dest); } @@ -705,29 +745,29 @@ MacroAssembler::rshift64Arithmetic(Regis Label proceed; // dest.low = dest.low >>> shift | dest.high <<< 32 - shift // if (shift - 32 >= 0) // dest.low |= dest.high >>> shift - 32 // Note: Negative shifts yield a zero as result, except for the signed // right shift. Therefore we need to test for it and only do it if // it isn't negative. + ScratchRegisterScope shift(*this); - ScratchRegisterScope shift(*this); - ma_and(Imm32(0x3f), unmaskedShift, shift); + as_and(shift, unmaskedShift, Imm8(0x3f)); as_mov(dest.low, lsr(dest.low, shift)); - ma_rsb(shift, Imm32(32), shift); + as_rsb(shift, shift, Imm8(32)); as_orr(dest.low, dest.low, lsl(dest.high, shift)); ma_neg(shift, shift, SetCC); ma_b(&proceed, Signed); as_orr(dest.low, dest.low, asr(dest.high, shift)); bind(&proceed); - ma_and(Imm32(0x3f), unmaskedShift, shift); + as_and(shift, unmaskedShift, Imm8(0x3f)); as_mov(dest.high, asr(dest.high, shift)); } void MacroAssembler::rshift32Arithmetic(Register src, Register dest) { ma_asr(src, dest, dest); } @@ -759,23 +799,23 @@ MacroAssembler::rshift64(Imm32 imm, Regi void MacroAssembler::rshift64(Register unmaskedShift, Register64 dest) { // dest.low = dest.low >> shift | dest.high >> shift - 32 | dest.high << 32 - shift // Note: one of the two dest.high shifts will always yield zero due to negative shift. ScratchRegisterScope shift(*this); - ma_and(Imm32(0x3f), unmaskedShift, shift); + as_and(shift, unmaskedShift, Imm8(0x3f)); as_mov(dest.low, lsr(dest.low, shift)); - ma_sub(shift, Imm32(32), shift); + as_sub(shift, shift, Imm8(32)); as_orr(dest.low, dest.low, lsr(dest.high, shift)); ma_neg(shift, shift); as_orr(dest.low, dest.low, lsl(dest.high, shift)); - ma_and(Imm32(0x3f), unmaskedShift, shift); + as_and(shift, unmaskedShift, Imm8(0x3f)); as_mov(dest.high, lsr(dest.high, shift)); } // =============================================================== // Rotate functions void MacroAssembler::rotateLeft(Imm32 count, Register input, Register dest) { @@ -783,17 +823,18 @@ MacroAssembler::rotateLeft(Imm32 count, ma_rol(count, input, dest); else ma_mov(input, dest); } void MacroAssembler::rotateLeft(Register count, Register input, Register dest) { - ma_rol(count, input, dest); + ScratchRegisterScope scratch(*this); + ma_rol(count, input, dest, scratch); } void MacroAssembler::rotateLeft64(Imm32 count, Register64 input, Register64 dest, Register temp) { MOZ_ASSERT(temp == InvalidReg); MOZ_ASSERT(input.low != dest.high && input.high != dest.low); @@ -828,45 +869,44 @@ MacroAssembler::rotateLeft64(Register sh MOZ_ASSERT(temp != src.low && temp != src.high); MOZ_ASSERT(shift != src.low && shift != src.high); MOZ_ASSERT(temp != InvalidReg); ScratchRegisterScope shift_value(*this); Label high, done; ma_mov(src.high, temp); - ma_and(Imm32(0x3f), shift, shift_value); - - ma_cmp(shift_value, Imm32(32)); + as_and(shift_value, shift, Imm8(0x3f)); + as_cmp(shift_value, Imm8(32)); ma_b(&high, GreaterThanOrEqual); // high = high << shift | low >> 32 - shift // low = low << shift | high >> 32 - shift as_mov(dest.high, lsl(src.high, shift_value)); - ma_rsb(Imm32(32), shift_value); + as_rsb(shift_value, shift_value, Imm8(32)); as_orr(dest.high, dest.high, lsr(src.low, shift_value)); - ma_rsb(Imm32(32), shift_value); + as_rsb(shift_value, shift_value, Imm8(32)); as_mov(dest.low, lsl(src.low, shift_value)); - ma_rsb(Imm32(32), shift_value); + as_rsb(shift_value, shift_value, Imm8(32)); as_orr(dest.low, dest.low, lsr(temp, shift_value)); ma_b(&done); // A 32 - 64 shift is a 0 - 32 shift in the other direction. bind(&high); - ma_rsb(Imm32(64), shift_value); + as_rsb(shift_value, shift_value, Imm8(64)); as_mov(dest.high, lsr(src.high, shift_value)); - ma_rsb(Imm32(32), shift_value); + as_rsb(shift_value, shift_value, Imm8(32)); as_orr(dest.high, dest.high, lsl(src.low, shift_value)); - ma_rsb(Imm32(32), shift_value); + as_rsb(shift_value, shift_value, Imm8(32)); as_mov(dest.low, lsr(src.low, shift_value)); - ma_rsb(Imm32(32), shift_value); + as_rsb(shift_value, shift_value, Imm8(32)); as_orr(dest.low, dest.low, lsl(temp, shift_value)); bind(&done); } void MacroAssembler::rotateRight(Imm32 count, Register input, Register dest) { @@ -919,45 +959,44 @@ MacroAssembler::rotateRight64(Register s MOZ_ASSERT(temp != src.low && temp != src.high); MOZ_ASSERT(shift != src.low && shift != src.high); MOZ_ASSERT(temp != InvalidReg); ScratchRegisterScope shift_value(*this); Label high, done; ma_mov(src.high, temp); - ma_and(Imm32(0x3f), shift, shift_value); - - ma_cmp(shift_value, Imm32(32)); + as_and(shift_value, shift, Imm8(0x3f)); + as_cmp(shift_value, Imm8(32)); ma_b(&high, GreaterThanOrEqual); // high = high >> shift | low << 32 - shift // low = low >> shift | high << 32 - shift as_mov(dest.high, lsr(src.high, shift_value)); - ma_rsb(Imm32(32), shift_value); + as_rsb(shift_value, shift_value, Imm8(32)); as_orr(dest.high, dest.high, lsl(src.low, shift_value)); - ma_rsb(Imm32(32), shift_value); + as_rsb(shift_value, shift_value, Imm8(32)); as_mov(dest.low, lsr(src.low, shift_value)); - ma_rsb(Imm32(32), shift_value); + as_rsb(shift_value, shift_value, Imm8(32)); as_orr(dest.low, dest.low, lsl(temp, shift_value)); ma_b(&done); // A 32 - 64 shift is a 0 - 32 shift in the other direction. bind(&high); - ma_rsb(Imm32(64), shift_value); + as_rsb(shift_value, shift_value, Imm8(64)); as_mov(dest.high, lsl(src.high, shift_value)); - ma_rsb(Imm32(32), shift_value); + as_rsb(shift_value, shift_value, Imm8(32)); as_orr(dest.high, dest.high, lsr(src.low, shift_value)); - ma_rsb(Imm32(32), shift_value); + as_rsb(shift_value, shift_value, Imm8(32)); as_mov(dest.low, lsl(src.low, shift_value)); - ma_rsb(Imm32(32), shift_value); + as_rsb(shift_value, shift_value, Imm8(32)); as_orr(dest.low, dest.low, lsr(temp, shift_value)); bind(&done); } // =============================================================== // Bit counting functions @@ -966,63 +1005,67 @@ MacroAssembler::clz32(Register src, Regi { ma_clz(src, dest); } void MacroAssembler::clz64(Register64 src, Register dest) { ScratchRegisterScope scratch(*this); + ma_clz(src.high, scratch); - ma_cmp(scratch, Imm32(32)); + as_cmp(scratch, Imm8(32)); ma_mov(scratch, dest, LeaveCC, NotEqual); ma_clz(src.low, dest, Equal); - ma_add(Imm32(32), dest, LeaveCC, Equal); + as_add(dest, dest, Imm8(32), LeaveCC, Equal); } void MacroAssembler::ctz32(Register src, Register dest, bool knownNotZero) { - ma_ctz(src, dest); + ScratchRegisterScope scratch(*this); + ma_ctz(src, dest, scratch); } void MacroAssembler::ctz64(Register64 src, Register dest) { Label done, high; - - ma_cmp(src.low, Imm32(0)); + as_cmp(src.low, Imm8(0)); ma_b(&high, Equal); ctz32(src.low, dest, /* knownNotZero = */ true); ma_b(&done); bind(&high); ctz32(src.high, dest, /* knownNotZero = */ false); - ma_add(Imm32(32), dest); + as_add(dest, dest, Imm8(32)); bind(&done); } void MacroAssembler::popcnt32(Register input, Register output, Register tmp) { // Equivalent to GCC output of mozilla::CountPopulation32() + ScratchRegisterScope scratch(*this); + if (input != output) ma_mov(input, output); as_mov(tmp, asr(output, 1)); - ma_and(Imm32(0x55555555), tmp); + ma_and(Imm32(0x55555555), tmp, scratch); ma_sub(output, tmp, output); as_mov(tmp, asr(output, 2)); - ma_and(Imm32(0x33333333), output); - ma_and(Imm32(0x33333333), tmp); + ma_mov(Imm32(0x33333333), scratch); + ma_and(scratch, output); + ma_and(scratch, tmp); ma_add(output, tmp, output); as_add(output, output, lsr(output, 4)); - ma_and(Imm32(0xF0F0F0F), output); + ma_and(Imm32(0xF0F0F0F), output, scratch); as_add(output, output, lsl(output, 8)); as_add(output, output, lsl(output, 16)); as_mov(output, asr(output, 24)); } void MacroAssembler::popcnt64(Register64 src, Register64 dest, Register tmp) { @@ -1053,70 +1096,103 @@ MacroAssembler::branch32(Condition cond, ma_cmp(lhs, rhs); ma_b(label, cond); } template <class L> void MacroAssembler::branch32(Condition cond, Register lhs, Imm32 rhs, L label) { - ma_cmp(lhs, rhs); + ScratchRegisterScope scratch(*this); + + ma_cmp(lhs, rhs, scratch); ma_b(label, cond); } void MacroAssembler::branch32(Condition cond, const Address& lhs, Register rhs, Label* label) { ScratchRegisterScope scratch(*this); - load32(lhs, scratch); - branch32(cond, scratch, rhs, label); + SecondScratchRegisterScope scratch2(*this); + + ma_ldr(lhs, scratch, scratch2); + ma_cmp(scratch, rhs); + ma_b(label, cond); } void MacroAssembler::branch32(Condition cond, const Address& lhs, Imm32 rhs, Label* label) { - // branch32 will use ScratchRegister. - AutoRegisterScope scratch(*this, secondScratchReg_); - load32(lhs, scratch); - branch32(cond, scratch, rhs, label); + ScratchRegisterScope scratch(*this); + SecondScratchRegisterScope scratch2(*this); + + ma_ldr(lhs, scratch, scratch2); + ma_cmp(scratch, rhs, scratch2); + ma_b(label, cond); } void MacroAssembler::branch32(Condition cond, const AbsoluteAddress& lhs, Register rhs, Label* label) { - AutoRegisterScope scratch2(*this, secondScratchReg_); - loadPtr(lhs, scratch2); // ma_cmp will use the scratch register. - ma_cmp(scratch2, rhs); + ScratchRegisterScope scratch(*this); + + // Load into scratch. + movePtr(ImmWord(uintptr_t(lhs.addr)), scratch); + ma_ldr(DTRAddr(scratch, DtrOffImm(0)), scratch); + + ma_cmp(scratch, rhs); ma_b(label, cond); } void MacroAssembler::branch32(Condition cond, const AbsoluteAddress& lhs, Imm32 rhs, Label* label) { - AutoRegisterScope scratch2(*this, secondScratchReg_); - loadPtr(lhs, scratch2); // ma_cmp will use the scratch register. - ma_cmp(scratch2, rhs); + ScratchRegisterScope scratch(*this); + SecondScratchRegisterScope scratch2(*this); + + // Load into scratch. + movePtr(ImmWord(uintptr_t(lhs.addr)), scratch); + ma_ldr(DTRAddr(scratch, DtrOffImm(0)), scratch); + + ma_cmp(scratch, rhs, scratch2); ma_b(label, cond); } void MacroAssembler::branch32(Condition cond, const BaseIndex& lhs, Imm32 rhs, Label* label) { - // branch32 will use ScratchRegister. - AutoRegisterScope scratch2(*this, secondScratchReg_); - load32(lhs, scratch2); + SecondScratchRegisterScope scratch2(*this); + { + ScratchRegisterScope scratch(*this); + + Register base = lhs.base; + uint32_t scale = Imm32::ShiftOf(lhs.scale).value; + + // Load lhs into scratch2. + if (lhs.offset != 0) { + ma_add(base, Imm32(lhs.offset), scratch, scratch2); + ma_ldr(DTRAddr(scratch, DtrRegImmShift(lhs.index, LSL, scale)), scratch2); + } else { + ma_ldr(DTRAddr(base, DtrRegImmShift(lhs.index, LSL, scale)), scratch2); + } + } branch32(cond, scratch2, rhs, label); } void MacroAssembler::branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, Label* label) { ScratchRegisterScope scratch(*this); - loadPtr(lhs, scratch); - branch32(cond, scratch, rhs, label); + SecondScratchRegisterScope scratch2(*this); + + movePtr(lhs, scratch); + ma_ldr(DTRAddr(scratch, DtrOffImm(0)), scratch); + + ma_cmp(scratch, rhs, scratch2); + ma_b(label, cond); } void MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label) { MOZ_ASSERT(cond == Assembler::NotEqual, "other condition codes not supported"); @@ -1292,68 +1368,71 @@ void MacroAssembler::branchPtr(Condition cond, const Address& lhs, ImmPtr rhs, Label* label) { branchPtr(cond, lhs, ImmWord(uintptr_t(rhs.value)), label); } void MacroAssembler::branchPtr(Condition cond, const Address& lhs, ImmGCPtr rhs, Label* label) { - AutoRegisterScope scratch2(*this, secondScratchReg_); + SecondScratchRegisterScope scratch2(*this); loadPtr(lhs, scratch2); branchPtr(cond, scratch2, rhs, label); } void MacroAssembler::branchPtr(Condition cond, const Address& lhs, ImmWord rhs, Label* label) { - AutoRegisterScope scratch2(*this, secondScratchReg_); + SecondScratchRegisterScope scratch2(*this); loadPtr(lhs, scratch2); branchPtr(cond, scratch2, rhs, label); } void MacroAssembler::branchPtr(Condition cond, const AbsoluteAddress& lhs, Register rhs, Label* label) { - ScratchRegisterScope scratch(*this); - loadPtr(lhs, scratch); - branchPtr(cond, scratch, rhs, label); + SecondScratchRegisterScope scratch2(*this); + loadPtr(lhs, scratch2); + branchPtr(cond, scratch2, rhs, label); } void MacroAssembler::branchPtr(Condition cond, const AbsoluteAddress& lhs, ImmWord rhs, Label* label) { - ScratchRegisterScope scratch(*this); - loadPtr(lhs, scratch); - branchPtr(cond, scratch, rhs, label); + SecondScratchRegisterScope scratch2(*this); + loadPtr(lhs, scratch2); + branchPtr(cond, scratch2, rhs, label); } void MacroAssembler::branchPtr(Condition cond, wasm::SymbolicAddress lhs, Register rhs, Label* label) { - ScratchRegisterScope scratch(*this); - loadPtr(lhs, scratch); - branchPtr(cond, scratch, rhs, label); + SecondScratchRegisterScope scratch2(*this); + loadPtr(lhs, scratch2); + branchPtr(cond, scratch2, rhs, label); } template <typename T> -CodeOffsetJump +inline CodeOffsetJump MacroAssembler::branchPtrWithPatch(Condition cond, Register lhs, T rhs, RepatchLabel* label) { - ma_cmp(lhs, rhs); + cmpPtr(lhs, rhs); return jumpWithPatch(label, cond); } template <typename T> -CodeOffsetJump +inline CodeOffsetJump MacroAssembler::branchPtrWithPatch(Condition cond, Address lhs, T rhs, RepatchLabel* label) { - AutoRegisterScope scratch2(*this, secondScratchReg_); - ma_ldr(lhs, scratch2); - ma_cmp(scratch2, rhs); + SecondScratchRegisterScope scratch2(*this); + { + ScratchRegisterScope scratch(*this); + ma_ldr(lhs, scratch2, scratch); + } + cmpPtr(scratch2, rhs); return jumpWithPatch(label, cond); } void MacroAssembler::branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label) { branchPtr(cond, lhs, rhs, label); } @@ -1386,21 +1465,23 @@ void MacroAssembler::branchTruncateFloat32MaybeModUint32(FloatRegister src, Register dest, Label* fail) { branchTruncateFloat32ToInt32(src, dest, fail); } void MacroAssembler::branchTruncateFloat32ToInt32(FloatRegister src, Register dest, Label* fail) { - ScratchFloat32Scope scratch(*this); - ma_vcvt_F32_I32(src, scratch.sintOverlay()); - ma_vxfer(scratch, dest); - ma_cmp(dest, Imm32(0x7fffffff)); - ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual); + ScratchFloat32Scope scratchFloat32(*this); + ScratchRegisterScope scratch(*this); + + ma_vcvt_F32_I32(src, scratchFloat32.sintOverlay()); + ma_vxfer(scratchFloat32, dest); + ma_cmp(dest, Imm32(0x7fffffff), scratch); + ma_cmp(dest, Imm32(0x80000000), scratch, Assembler::NotEqual); ma_b(fail, Assembler::Equal); } void MacroAssembler::branchDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs, Label* label) { compareDouble(lhs, rhs); @@ -1435,86 +1516,87 @@ MacroAssembler::branchTruncateDoubleMayb // was clamped to INT_MIN/INT_MAX, and we can test it. NOTE: if the value // really was supposed to be INT_MAX / INT_MIN then it will be wrong. // // 2. Convert the floating point value to an integer, if it did not fit, then it // set one or two bits in the fpcsr. Check those. void MacroAssembler::branchTruncateDoubleToInt32(FloatRegister src, Register dest, Label* fail) { - ScratchDoubleScope scratch(*this); - FloatRegister scratchSIntReg = scratch.sintOverlay(); + ScratchDoubleScope scratchDouble(*this); + FloatRegister scratchSIntReg = scratchDouble.sintOverlay(); + ScratchRegisterScope scratch(*this); ma_vcvt_F64_I32(src, scratchSIntReg); ma_vxfer(scratchSIntReg, dest); - ma_cmp(dest, Imm32(0x7fffffff)); - ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual); + ma_cmp(dest, Imm32(0x7fffffff), scratch); + ma_cmp(dest, Imm32(0x80000000), scratch, Assembler::NotEqual); ma_b(fail, Assembler::Equal); } template <typename T, typename L> void MacroAssembler::branchAdd32(Condition cond, T src, Register dest, L label) { add32(src, dest); as_b(label, cond); } template <typename T> void MacroAssembler::branchSub32(Condition cond, T src, Register dest, Label* label) { - ma_sub(src, dest, SetCC); + sub32(src, dest); j(cond, label); } void MacroAssembler::decBranchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label) { - ma_sub(rhs, lhs, SetCC); + ScratchRegisterScope scratch(*this); + ma_sub(rhs, lhs, scratch, SetCC); as_b(label, cond); } template <class L> void MacroAssembler::branchTest32(Condition cond, Register lhs, Register rhs, L label) { MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned); // x86 likes test foo, foo rather than cmp foo, #0. // Convert the former into the latter. if (lhs == rhs && (cond == Zero || cond == NonZero)) - ma_cmp(lhs, Imm32(0)); + as_cmp(lhs, Imm8(0)); else ma_tst(lhs, rhs); ma_b(label, cond); } template <class L> void MacroAssembler::branchTest32(Condition cond, Register lhs, Imm32 rhs, L label) { MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned); - ma_tst(lhs, rhs); + ScratchRegisterScope scratch(*this); + ma_tst(lhs, rhs, scratch); ma_b(label, cond); } void MacroAssembler::branchTest32(Condition cond, const Address& lhs, Imm32 rhs, Label* label) { - // branchTest32 will use ScratchRegister. - AutoRegisterScope scratch2(*this, secondScratchReg_); + SecondScratchRegisterScope scratch2(*this); load32(lhs, scratch2); branchTest32(cond, scratch2, rhs, label); } void MacroAssembler::branchTest32(Condition cond, const AbsoluteAddress& lhs, Imm32 rhs, Label* label) { - // branchTest32 will use ScratchRegister. - AutoRegisterScope scratch2(*this, secondScratchReg_); + SecondScratchRegisterScope scratch2(*this); load32(lhs, scratch2); branchTest32(cond, scratch2, rhs, label); } template <class L> void MacroAssembler::branchTestPtr(Condition cond, Register lhs, Register rhs, L label) { @@ -1533,21 +1615,23 @@ MacroAssembler::branchTestPtr(Condition branchTest32(cond, lhs, rhs, label); } template <class L> void MacroAssembler::branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp, L label) { + ScratchRegisterScope scratch(*this); + if (cond == Assembler::Zero) { MOZ_ASSERT(lhs.low == rhs.low); MOZ_ASSERT(lhs.high == rhs.high); - ma_orr(lhs.low, lhs.high, ScratchRegister); - branchTestPtr(cond, ScratchRegister, ScratchRegister, label); + ma_orr(lhs.low, lhs.high, scratch); + branchTestPtr(cond, scratch, scratch, label); } else { MOZ_CRASH("Unsupported condition"); } } void MacroAssembler::branchTestUndefined(Condition cond, Register tag, Label* label) { @@ -1920,35 +2004,41 @@ MacroAssembler::branchTestMagic(Conditio branch32(cond, ToPayload(valaddr), Imm32(why), label); } // ======================================================================== // Memory access primitives. void MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& addr) { - ma_vstr(src, addr); + ScratchRegisterScope scratch(*this); + ma_vstr(src, addr, scratch); } void MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& addr) { + ScratchRegisterScope scratch(*this); + SecondScratchRegisterScope scratch2(*this); uint32_t scale = Imm32::ShiftOf(addr.scale).value; - ma_vstr(src, addr.base, addr.index, scale, addr.offset); + ma_vstr(src, addr.base, addr.index, scratch, scratch2, scale, addr.offset); } void MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& addr) { - ma_vstr(src.asSingle(), addr); + ScratchRegisterScope scratch(*this); + ma_vstr(src.asSingle(), addr, scratch); } void MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr) { + ScratchRegisterScope scratch(*this); + SecondScratchRegisterScope scratch2(*this); uint32_t scale = Imm32::ShiftOf(addr.scale).value; - ma_vstr(src.asSingle(), addr.base, addr.index, scale, addr.offset); + ma_vstr(src.asSingle(), addr.base, addr.index, scratch, scratch2, scale, addr.offset); } void MacroAssembler::storeFloat32x3(FloatRegister src, const Address& dest) { MOZ_CRASH("NYI"); } void
--- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -40,48 +40,49 @@ isValueDTRDCandidate(ValueOperand& val) return true; } void MacroAssemblerARM::convertBoolToInt32(Register source, Register dest) { // Note that C++ bool is only 1 byte, so zero extend it to clear the // higher-order bits. - ma_and(Imm32(0xff), source, dest); + as_and(dest, source, Imm8(0xff)); } void MacroAssemblerARM::convertInt32ToDouble(Register src, FloatRegister dest_) { // Direct conversions aren't possible. VFPRegister dest = VFPRegister(dest_); as_vxfer(src, InvalidReg, dest.sintOverlay(), CoreToFloat); as_vcvt(dest, dest.sintOverlay()); } void MacroAssemblerARM::convertInt32ToDouble(const Address& src, FloatRegister dest) { ScratchDoubleScope scratch(asMasm()); - ma_vldr(src, scratch); + SecondScratchRegisterScope scratch2(asMasm()); + ma_vldr(src, scratch, scratch2); as_vcvt(dest, VFPRegister(scratch).sintOverlay()); } void MacroAssemblerARM::convertInt32ToDouble(const BaseIndex& src, FloatRegister dest) { Register base = src.base; uint32_t scale = Imm32::ShiftOf(src.scale).value; ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); if (src.offset != 0) { - ma_mov(base, scratch); + ma_add(base, Imm32(src.offset), scratch, scratch2); base = scratch; - ma_add(Imm32(src.offset), base); } ma_ldr(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), scratch); convertInt32ToDouble(scratch, dest); } void MacroAssemblerARM::convertUInt32ToDouble(Register src, FloatRegister dest_) { @@ -98,22 +99,27 @@ MacroAssemblerARMCompat::convertUInt64To { return false; } void MacroAssemblerARMCompat::convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp) { MOZ_ASSERT(temp == Register::Invalid()); + ScratchDoubleScope scratchDouble(asMasm()); + convertUInt32ToDouble(src.high, dest); - movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), ScratchRegister); - loadDouble(Address(ScratchRegister, 0), ScratchDoubleReg); - asMasm().mulDouble(ScratchDoubleReg, dest); - convertUInt32ToDouble(src.low, ScratchDoubleReg); - asMasm().addDouble(ScratchDoubleReg, dest); + { + ScratchRegisterScope scratch(asMasm()); + movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), scratch); + ma_vldr(Operand(Address(scratch, 0)).toVFPAddr(), scratchDouble); + } + asMasm().mulDouble(scratchDouble, dest); + convertUInt32ToDouble(src.low, scratchDouble); + asMasm().addDouble(scratchDouble, dest); } void MacroAssemblerARM::convertUInt32ToFloat32(Register src, FloatRegister dest_) { // Direct conversions aren't possible. VFPRegister dest = VFPRegister(dest_); as_vxfer(src, InvalidReg, dest.uintOverlay(), CoreToFloat); @@ -132,33 +138,35 @@ void MacroAssemblerARM::convertDoubleToF void MacroAssemblerARM::convertDoubleToInt32(FloatRegister src, Register dest, Label* fail, bool negativeZeroCheck) { // Convert the floating point value to an integer, if it did not fit, then // when we convert it *back* to a float, it will have a different value, // which we can test. ScratchDoubleScope scratchDouble(asMasm()); + ScratchRegisterScope scratch(asMasm()); + FloatRegister scratchSIntReg = scratchDouble.sintOverlay(); ma_vcvt_F64_I32(src, scratchSIntReg); // Move the value into the dest register. ma_vxfer(scratchSIntReg, dest); ma_vcvt_I32_F64(scratchSIntReg, scratchDouble); ma_vcmp(src, scratchDouble); as_vmrs(pc); ma_b(fail, Assembler::VFP_NotEqualOrUnordered); if (negativeZeroCheck) { - ma_cmp(dest, Imm32(0)); + as_cmp(dest, Imm8(0)); // Test and bail for -0.0, when integer result is 0. Move the top word // of the double into the output reg, if it is non-zero, then the // original value was -0.0. as_vxfer(dest, InvalidReg, src, FloatToCore, Assembler::Equal, 1); - ma_cmp(dest, Imm32(0x80000000), Assembler::Equal); + ma_cmp(dest, Imm32(0x80000000), scratch, Assembler::Equal); ma_b(fail, Assembler::Equal); } } // Checks whether a float32 is representable as a 32-bit integer. If so, the // integer is written to the output register. Otherwise, a bailout is taken to // the given snapshot. This function overwrites the scratch float register. void @@ -166,40 +174,41 @@ MacroAssemblerARM::convertFloat32ToInt32 Label* fail, bool negativeZeroCheck) { // Converting the floating point value to an integer and then converting it // back to a float32 would not work, as float to int32 conversions are // clamping (e.g. float(INT32_MAX + 1) would get converted into INT32_MAX // and then back to float(INT32_MAX + 1)). If this ever happens, we just // bail out. ScratchFloat32Scope scratchFloat(asMasm()); + ScratchRegisterScope scratch(asMasm()); FloatRegister ScratchSIntReg = scratchFloat.sintOverlay(); ma_vcvt_F32_I32(src, ScratchSIntReg); // Store the result ma_vxfer(ScratchSIntReg, dest); ma_vcvt_I32_F32(ScratchSIntReg, scratchFloat); ma_vcmp(src, scratchFloat); as_vmrs(pc); ma_b(fail, Assembler::VFP_NotEqualOrUnordered); // Bail out in the clamped cases. - ma_cmp(dest, Imm32(0x7fffffff)); - ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual); + ma_cmp(dest, Imm32(0x7fffffff), scratch); + ma_cmp(dest, Imm32(0x80000000), scratch, Assembler::NotEqual); ma_b(fail, Assembler::Equal); if (negativeZeroCheck) { - ma_cmp(dest, Imm32(0)); + as_cmp(dest, Imm8(0)); // Test and bail for -0.0, when integer result is 0. Move the float into // the output reg, and if it is non-zero then the original value was // -0.0 as_vxfer(dest, InvalidReg, VFPRegister(src).singleOverlay(), FloatToCore, Assembler::Equal, 0); - ma_cmp(dest, Imm32(0x80000000), Assembler::Equal); + ma_cmp(dest, Imm32(0x80000000), scratch, Assembler::Equal); ma_b(fail, Assembler::Equal); } } void MacroAssemblerARM::convertFloat32ToDouble(FloatRegister src, FloatRegister dest) { MOZ_ASSERT(dest.isDouble()); @@ -214,17 +223,18 @@ MacroAssemblerARM::convertInt32ToFloat32 as_vxfer(src, InvalidReg, dest.sintOverlay(), CoreToFloat); as_vcvt(dest.singleOverlay(), dest.sintOverlay()); } void MacroAssemblerARM::convertInt32ToFloat32(const Address& src, FloatRegister dest) { ScratchFloat32Scope scratch(asMasm()); - ma_vldr(src, scratch); + SecondScratchRegisterScope scratch2(asMasm()); + ma_vldr(src, scratch, scratch2); as_vcvt(dest, VFPRegister(scratch).sintOverlay()); } bool MacroAssemblerARM::alu_dbl(Register src1, Imm32 imm, Register dest, ALUOp op, SBit s, Condition c) { if ((s == SetCC && ! condsAreSafe(op)) || !can_dbl(op)) @@ -241,22 +251,23 @@ MacroAssemblerARM::alu_dbl(Register src1 // don't do second operation if first operation overflowed. This preserves // the overflow condition code. Unfortunately, it is horribly brittle. as_alu(dest, src1, Operand2(both.fst), interop, LeaveCC, c); as_alu(dest, dest, Operand2(both.snd), op, s, c); return true; } void -MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest, +MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest, AutoRegisterScope& scratch, ALUOp op, SBit s, Condition c) { // ma_mov should be used for moves. MOZ_ASSERT(op != OpMov); MOZ_ASSERT(op != OpMvn); + MOZ_ASSERT(src1 != scratch); // As it turns out, if you ask for a compare-like instruction you *probably* // want it to set condition codes. if (dest == InvalidReg) MOZ_ASSERT(s == SetCC); // The operator gives us the ability to determine how this can be used. Imm8 imm8 = Imm8(imm.value); @@ -264,24 +275,22 @@ MacroAssemblerARM::ma_alu(Register src1, if (!imm8.invalid) { as_alu(dest, src1, imm8, op, s, c); return; } // One instruction, negated: Imm32 negImm = imm; Register negDest; - ALUOp negOp = ALUNeg(op, dest, &negImm, &negDest); + ALUOp negOp = ALUNeg(op, dest, scratch, &negImm, &negDest); Imm8 negImm8 = Imm8(negImm.value); - // 'add r1, r2, -15' can be replaced with 'sub r1, r2, 15'. For bonus - // points, dest can be replaced (nearly always invalid => ScratchRegister) + // 'add r1, r2, -15' can be replaced with 'sub r1, r2, 15'. + // The dest can be replaced (InvalidReg => scratch). // This is useful if we wish to negate tst. tst has an invalid (aka not - // used) dest, but its negation is bic *requires* a dest. We can accomodate, - // but it will need to clobber *something*, and the scratch register isn't - // being used, so... + // used) dest, but its negation bic requires a dest. if (negOp != OpInvalid && !negImm8.invalid) { as_alu(negDest, src1, negImm8, negOp, s, c); return; } // Start by attempting to generate a two instruction form. Some things // cannot be made into two-inst forms correctly. Namely, adds dest, src, // 0xffff. Since we want the condition codes (and don't know which ones @@ -291,29 +300,16 @@ MacroAssemblerARM::ma_alu(Register src1, // instruction variant. if (alu_dbl(src1, imm, dest, op, s, c)) return; // And try with its negative. if (negOp != OpInvalid && alu_dbl(src1, negImm, negDest, negOp, s, c)) return; - // Often this code is called with dest as the ScratchRegister. The register - // is logically owned by the caller after this call. - const Register& scratch = ScratchRegister; - MOZ_ASSERT(src1 != scratch); -#ifdef DEBUG - if (dest != scratch) { - // If the destination register is not the scratch register, double check - // that the current function does not erase the content of the scratch - // register. - ScratchRegisterScope assertScratch(asMasm()); - } -#endif - ma_mov(imm, scratch, c); as_alu(dest, src1, O2Reg(scratch), op, s, c); } void MacroAssemblerARM::ma_alu(Register src1, Operand op2, Register dest, ALUOp op, SBit s, Assembler::Condition c) { @@ -394,22 +390,19 @@ MacroAssemblerARM::ma_mov(Imm32 imm, Reg // Try mov with Imm8 operand. Imm8 imm8 = Imm8(imm.value); if (!imm8.invalid) { as_alu(dest, InvalidReg, imm8, OpMov, LeaveCC, c); return; } // Try mvn with Imm8 operand. - Imm32 negImm = imm; - Register negDest; - ALUOp negOp = ALUNeg(OpMov, dest, &negImm, &negDest); - Imm8 negImm8 = Imm8(negImm.value); - if (negOp != OpInvalid && !negImm8.invalid) { - as_alu(negDest, InvalidReg, negImm8, negOp, LeaveCC, c); + Imm8 negImm8 = Imm8(~imm.value); + if (!negImm8.invalid) { + as_alu(dest, InvalidReg, negImm8, OpMvn, LeaveCC, c); return; } // Try movw/movt. if (HasMOVWT()) { // ARMv7 supports movw/movt. movw zero-extends its 16 bit argument, // so we can set the register this way. movt leaves the bottom 16 // bits in tact, so we always need a movw. @@ -490,20 +483,19 @@ MacroAssemblerARM::ma_asr(Register shift void MacroAssemblerARM::ma_ror(Register shift, Register src, Register dst) { as_mov(dst, ror(src, shift)); } void -MacroAssemblerARM::ma_rol(Register shift, Register src, Register dst) -{ - ScratchRegisterScope scratch(asMasm()); - ma_rsb(shift, Imm32(32), scratch); +MacroAssemblerARM::ma_rol(Register shift, Register src, Register dst, AutoRegisterScope& scratch) +{ + as_rsb(scratch, shift, Imm8(32)); as_mov(dst, ror(src, scratch)); } // Move not (dest <- ~src) void MacroAssemblerARM::ma_mvn(Register src1, Register dest, SBit s, Assembler::Condition c) { as_alu(dest, InvalidReg, O2Reg(src1), OpMvn, s, c); @@ -526,33 +518,34 @@ MacroAssemblerARM::ma_and(Register src, void MacroAssemblerARM::ma_and(Register src1, Register src2, Register dest, SBit s, Assembler::Condition c) { as_and(dest, src1, O2Reg(src2), s, c); } void -MacroAssemblerARM::ma_and(Imm32 imm, Register dest, SBit s, Assembler::Condition c) -{ - ma_alu(dest, imm, dest, OpAnd, s, c); -} - -void -MacroAssemblerARM::ma_and(Imm32 imm, Register src1, Register dest, +MacroAssemblerARM::ma_and(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Assembler::Condition c) { - ma_alu(src1, imm, dest, OpAnd, s, c); + ma_alu(dest, imm, dest, scratch, OpAnd, s, c); +} + +void +MacroAssemblerARM::ma_and(Imm32 imm, Register src1, Register dest, AutoRegisterScope& scratch, + SBit s, Assembler::Condition c) +{ + ma_alu(src1, imm, dest, scratch, OpAnd, s, c); } // Bit clear (dest <- dest & ~imm) or (dest <- src1 & ~src2). void -MacroAssemblerARM::ma_bic(Imm32 imm, Register dest, SBit s, Assembler::Condition c) -{ - ma_alu(dest, imm, dest, OpBic, s, c); +MacroAssemblerARM::ma_bic(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Assembler::Condition c) +{ + ma_alu(dest, imm, dest, scratch, OpBic, s, c); } // Exclusive or. void MacroAssemblerARM::ma_eor(Register src, Register dest, SBit s, Assembler::Condition c) { ma_eor(dest, src, dest, s, c); } @@ -560,26 +553,26 @@ MacroAssemblerARM::ma_eor(Register src, void MacroAssemblerARM::ma_eor(Register src1, Register src2, Register dest, SBit s, Assembler::Condition c) { as_eor(dest, src1, O2Reg(src2), s, c); } void -MacroAssemblerARM::ma_eor(Imm32 imm, Register dest, SBit s, Assembler::Condition c) -{ - ma_alu(dest, imm, dest, OpEor, s, c); -} - -void -MacroAssemblerARM::ma_eor(Imm32 imm, Register src1, Register dest, +MacroAssemblerARM::ma_eor(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Assembler::Condition c) +{ + ma_alu(dest, imm, dest, scratch, OpEor, s, c); +} + +void +MacroAssemblerARM::ma_eor(Imm32 imm, Register src1, Register dest, AutoRegisterScope& scratch, SBit s, Assembler::Condition c) { - ma_alu(src1, imm, dest, OpEor, s, c); + ma_alu(src1, imm, dest, scratch, OpEor, s, c); } // Or. void MacroAssemblerARM::ma_orr(Register src, Register dest, SBit s, Assembler::Condition c) { ma_orr(dest, src, dest, s, c); } @@ -587,53 +580,53 @@ MacroAssemblerARM::ma_orr(Register src, void MacroAssemblerARM::ma_orr(Register src1, Register src2, Register dest, SBit s, Assembler::Condition c) { as_orr(dest, src1, O2Reg(src2), s, c); } void -MacroAssemblerARM::ma_orr(Imm32 imm, Register dest, SBit s, Assembler::Condition c) -{ - ma_alu(dest, imm, dest, OpOrr, s, c); -} - -void -MacroAssemblerARM::ma_orr(Imm32 imm, Register src1, Register dest, +MacroAssemblerARM::ma_orr(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Assembler::Condition c) +{ + ma_alu(dest, imm, dest, scratch, OpOrr, s, c); +} + +void +MacroAssemblerARM::ma_orr(Imm32 imm, Register src1, Register dest, AutoRegisterScope& scratch, SBit s, Assembler::Condition c) { - ma_alu(src1, imm, dest, OpOrr, s, c); + ma_alu(src1, imm, dest, scratch, OpOrr, s, c); } // Arithmetic-based ops. // Add with carry. void -MacroAssemblerARM::ma_adc(Imm32 imm, Register dest, SBit s, Condition c) -{ - ma_alu(dest, imm, dest, OpAdc, s, c); +MacroAssemblerARM::ma_adc(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c) +{ + ma_alu(dest, imm, dest, scratch, OpAdc, s, c); } void MacroAssemblerARM::ma_adc(Register src, Register dest, SBit s, Condition c) { as_alu(dest, dest, O2Reg(src), OpAdc, s, c); } void MacroAssemblerARM::ma_adc(Register src1, Register src2, Register dest, SBit s, Condition c) { as_alu(dest, src1, O2Reg(src2), OpAdc, s, c); } // Add. void -MacroAssemblerARM::ma_add(Imm32 imm, Register dest, SBit s, Condition c) -{ - ma_alu(dest, imm, dest, OpAdd, s, c); +MacroAssemblerARM::ma_add(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c) +{ + ma_alu(dest, imm, dest, scratch, OpAdd, s, c); } void MacroAssemblerARM::ma_add(Register src1, Register dest, SBit s, Condition c) { ma_alu(dest, O2Reg(src1), dest, OpAdd, s, c); } @@ -645,45 +638,45 @@ MacroAssemblerARM::ma_add(Register src1, void MacroAssemblerARM::ma_add(Register src1, Operand op, Register dest, SBit s, Condition c) { ma_alu(src1, op, dest, OpAdd, s, c); } void -MacroAssemblerARM::ma_add(Register src1, Imm32 op, Register dest, SBit s, Condition c) -{ - ma_alu(src1, op, dest, OpAdd, s, c); +MacroAssemblerARM::ma_add(Register src1, Imm32 op, Register dest, AutoRegisterScope& scratch, SBit s, Condition c) +{ + ma_alu(src1, op, dest, scratch, OpAdd, s, c); } // Subtract with carry. void -MacroAssemblerARM::ma_sbc(Imm32 imm, Register dest, SBit s, Condition c) -{ - ma_alu(dest, imm, dest, OpSbc, s, c); +MacroAssemblerARM::ma_sbc(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c) +{ + ma_alu(dest, imm, dest, scratch, OpSbc, s, c); } void MacroAssemblerARM::ma_sbc(Register src1, Register dest, SBit s, Condition c) { as_alu(dest, dest, O2Reg(src1), OpSbc, s, c); } void MacroAssemblerARM::ma_sbc(Register src1, Register src2, Register dest, SBit s, Condition c) { as_alu(dest, src1, O2Reg(src2), OpSbc, s, c); } // Subtract. void -MacroAssemblerARM::ma_sub(Imm32 imm, Register dest, SBit s, Condition c) -{ - ma_alu(dest, imm, dest, OpSub, s, c); +MacroAssemblerARM::ma_sub(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c) +{ + ma_alu(dest, imm, dest, scratch, OpSub, s, c); } void MacroAssemblerARM::ma_sub(Register src1, Register dest, SBit s, Condition c) { ma_alu(dest, Operand(src1), dest, OpSub, s, c); } @@ -695,51 +688,51 @@ MacroAssemblerARM::ma_sub(Register src1, void MacroAssemblerARM::ma_sub(Register src1, Operand op, Register dest, SBit s, Condition c) { ma_alu(src1, op, dest, OpSub, s, c); } void -MacroAssemblerARM::ma_sub(Register src1, Imm32 op, Register dest, SBit s, Condition c) -{ - ma_alu(src1, op, dest, OpSub, s, c); +MacroAssemblerARM::ma_sub(Register src1, Imm32 op, Register dest, AutoRegisterScope& scratch, SBit s, Condition c) +{ + ma_alu(src1, op, dest, scratch, OpSub, s, c); } // Reverse subtract. void -MacroAssemblerARM::ma_rsb(Imm32 imm, Register dest, SBit s, Condition c) -{ - ma_alu(dest, imm, dest, OpRsb, s, c); +MacroAssemblerARM::ma_rsb(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c) +{ + ma_alu(dest, imm, dest, scratch, OpRsb, s, c); } void MacroAssemblerARM::ma_rsb(Register src1, Register dest, SBit s, Condition c) { as_alu(dest, src1, O2Reg(dest), OpRsb, s, c); } void MacroAssemblerARM::ma_rsb(Register src1, Register src2, Register dest, SBit s, Condition c) { as_alu(dest, src1, O2Reg(src2), OpRsb, s, c); } void -MacroAssemblerARM::ma_rsb(Register src1, Imm32 op2, Register dest, SBit s, Condition c) -{ - ma_alu(src1, op2, dest, OpRsb, s, c); +MacroAssemblerARM::ma_rsb(Register src1, Imm32 op2, Register dest, AutoRegisterScope& scratch, SBit s, Condition c) +{ + ma_alu(src1, op2, dest, scratch, OpRsb, s, c); } // Reverse subtract with carry. void -MacroAssemblerARM::ma_rsc(Imm32 imm, Register dest, SBit s, Condition c) -{ - ma_alu(dest, imm, dest, OpRsc, s, c); +MacroAssemblerARM::ma_rsc(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c) +{ + ma_alu(dest, imm, dest, scratch, OpRsc, s, c); } void MacroAssemblerARM::ma_rsc(Register src1, Register dest, SBit s, Condition c) { as_alu(dest, dest, O2Reg(src1), OpRsc, s, c); } @@ -747,102 +740,109 @@ void MacroAssemblerARM::ma_rsc(Register src1, Register src2, Register dest, SBit s, Condition c) { as_alu(dest, src1, O2Reg(src2), OpRsc, s, c); } // Compares/tests. // Compare negative (sets condition codes as src1 + src2 would). void -MacroAssemblerARM::ma_cmn(Register src1, Imm32 imm, Condition c) -{ - ma_alu(src1, imm, InvalidReg, OpCmn, SetCC, c); +MacroAssemblerARM::ma_cmn(Register src1, Imm32 imm, AutoRegisterScope& scratch, Condition c) +{ + ma_alu(src1, imm, InvalidReg, scratch, OpCmn, SetCC, c); } void MacroAssemblerARM::ma_cmn(Register src1, Register src2, Condition c) { as_alu(InvalidReg, src2, O2Reg(src1), OpCmn, SetCC, c); } void MacroAssemblerARM::ma_cmn(Register src1, Operand op, Condition c) { MOZ_CRASH("Feature NYI"); } // Compare (src - src2). void -MacroAssemblerARM::ma_cmp(Register src1, Imm32 imm, Condition c) -{ - ma_alu(src1, imm, InvalidReg, OpCmp, SetCC, c); -} - -void -MacroAssemblerARM::ma_cmp(Register src1, ImmWord ptr, Condition c) -{ - ma_cmp(src1, Imm32(ptr.value), c); -} - -void -MacroAssemblerARM::ma_cmp(Register src1, ImmGCPtr ptr, Condition c) -{ - ScratchRegisterScope scratch(asMasm()); +MacroAssemblerARM::ma_cmp(Register src1, Imm32 imm, AutoRegisterScope& scratch, Condition c) +{ + ma_alu(src1, imm, InvalidReg, scratch, OpCmp, SetCC, c); +} + +void +MacroAssemblerARM::ma_cmp(Register src1, ImmTag tag, Condition c) +{ + // ImmTag comparisons can always be done without use of a scratch register. + Imm8 negtag = Imm8(-tag.value); + MOZ_ASSERT(!negtag.invalid); + as_cmn(src1, negtag, c); +} + +void +MacroAssemblerARM::ma_cmp(Register src1, ImmWord ptr, AutoRegisterScope& scratch, Condition c) +{ + ma_cmp(src1, Imm32(ptr.value), scratch, c); +} + +void +MacroAssemblerARM::ma_cmp(Register src1, ImmGCPtr ptr, AutoRegisterScope& scratch, Condition c) +{ ma_mov(ptr, scratch); ma_cmp(src1, scratch, c); } void -MacroAssemblerARM::ma_cmp(Register src1, Operand op, Condition c) +MacroAssemblerARM::ma_cmp(Register src1, Operand op, AutoRegisterScope& scratch, + AutoRegisterScope& scratch2, Condition c) { switch (op.getTag()) { case Operand::OP2: as_cmp(src1, op.toOp2(), c); break; - case Operand::MEM: { - ScratchRegisterScope scratch(asMasm()); - ma_ldr(op.toAddress(), scratch); + case Operand::MEM: + ma_ldr(op.toAddress(), scratch, scratch2); as_cmp(src1, O2Reg(scratch), c); break; - } default: MOZ_CRASH("trying to compare FP and integer registers"); } } void MacroAssemblerARM::ma_cmp(Register src1, Register src2, Condition c) { as_cmp(src1, O2Reg(src2), c); } // Test for equality, (src1 ^ src2). void -MacroAssemblerARM::ma_teq(Register src1, Imm32 imm, Condition c) -{ - ma_alu(src1, imm, InvalidReg, OpTeq, SetCC, c); +MacroAssemblerARM::ma_teq(Register src1, Imm32 imm, AutoRegisterScope& scratch, Condition c) +{ + ma_alu(src1, imm, InvalidReg, scratch, OpTeq, SetCC, c); } void MacroAssemblerARM::ma_teq(Register src1, Register src2, Condition c) { as_tst(src1, O2Reg(src2), c); } void MacroAssemblerARM::ma_teq(Register src1, Operand op, Condition c) { as_teq(src1, op.toOp2(), c); } // Test (src1 & src2). void -MacroAssemblerARM::ma_tst(Register src1, Imm32 imm, Condition c) -{ - ma_alu(src1, imm, InvalidReg, OpTst, SetCC, c); +MacroAssemblerARM::ma_tst(Register src1, Imm32 imm, AutoRegisterScope& scratch, Condition c) +{ + ma_alu(src1, imm, InvalidReg, scratch, OpTst, SetCC, c); } void MacroAssemblerARM::ma_tst(Register src1, Register src2, Condition c) { as_tst(src1, O2Reg(src2), c); } @@ -854,81 +854,79 @@ MacroAssemblerARM::ma_tst(Register src1, void MacroAssemblerARM::ma_mul(Register src1, Register src2, Register dest) { as_mul(dest, src1, src2); } void -MacroAssemblerARM::ma_mul(Register src1, Imm32 imm, Register dest) -{ - ScratchRegisterScope scratch(asMasm()); +MacroAssemblerARM::ma_mul(Register src1, Imm32 imm, Register dest, AutoRegisterScope& scratch) +{ ma_mov(imm, scratch); as_mul(dest, src1, scratch); } Assembler::Condition -MacroAssemblerARM::ma_check_mul(Register src1, Register src2, Register dest, Condition cond) -{ - ScratchRegisterScope scratch(asMasm()); - - // TODO: this operation is illegal on armv6 and earlier if src2 == - // ScratchRegister or src2 == dest. +MacroAssemblerARM::ma_check_mul(Register src1, Register src2, Register dest, + AutoRegisterScope& scratch, Condition cond) +{ + // TODO: this operation is illegal on armv6 and earlier + // if src2 == scratch or src2 == dest. if (cond == Equal || cond == NotEqual) { as_smull(scratch, dest, src1, src2, SetCC); return cond; } if (cond == Overflow) { as_smull(scratch, dest, src1, src2); as_cmp(scratch, asr(dest, 31)); return NotEqual; } MOZ_CRASH("Condition NYI"); } Assembler::Condition -MacroAssemblerARM::ma_check_mul(Register src1, Imm32 imm, Register dest, Condition cond) -{ - ScratchRegisterScope scratch(asMasm()); - +MacroAssemblerARM::ma_check_mul(Register src1, Imm32 imm, Register dest, + AutoRegisterScope& scratch, Condition cond) +{ ma_mov(imm, scratch); + if (cond == Equal || cond == NotEqual) { as_smull(scratch, dest, scratch, src1, SetCC); return cond; } if (cond == Overflow) { as_smull(scratch, dest, scratch, src1); as_cmp(scratch, asr(dest, 31)); return NotEqual; } MOZ_CRASH("Condition NYI"); } void -MacroAssemblerARM::ma_umull(Register src1, Imm32 imm, Register destHigh, Register destLow) -{ - ScratchRegisterScope scratch(asMasm()); +MacroAssemblerARM::ma_umull(Register src1, Imm32 imm, Register destHigh, Register destLow, + AutoRegisterScope& scratch) +{ ma_mov(imm, scratch); as_umull(destHigh, destLow, src1, scratch); } void MacroAssemblerARM::ma_umull(Register src1, Register src2, Register destHigh, Register destLow) { as_umull(destHigh, destLow, src1, src2); } void MacroAssemblerARM::ma_mod_mask(Register src, Register dest, Register hold, Register tmp, - int32_t shift) + AutoRegisterScope& scratch, AutoRegisterScope& scratch2, int32_t shift) { // We wish to compute x % (1<<y) - 1 for a known constant, y. // // 1. Let b = (1<<y) and C = (1<<y)-1, then think of the 32 bit dividend as // a number in base b, namely c_0*1 + c_1*b + c_2*b^2 ... c_n*b^n // // 2. Since both addition and multiplication commute with modulus: // x % C == (c_0 + c_1*b + ... + c_n*b^n) % C == @@ -939,78 +937,70 @@ MacroAssemblerARM::ma_mod_mask(Register // // Each c_n can easily be computed by a shift/bitextract, and the modulus // can be maintained by simply subtracting by C whenever the number gets // over C. int32_t mask = (1 << shift) - 1; Label head; // Register 'hold' holds -1 if the value was negative, 1 otherwise. The - // ScratchRegister holds the remaining bits that have not been processed lr + // scratch reg holds the remaining bits that have not been processed lr // serves as a temporary location to store extracted bits into as well as // holding the trial subtraction as a temp value dest is the accumulator // (and holds the final result) // // Move the whole value into tmp, setting the codition codes so we can muck // with them later. - // - // Note that we cannot use ScratchRegister in place of tmp here, as ma_and - // below on certain architectures move the mask into ScratchRegister before - // performing the bitwise and. as_mov(tmp, O2Reg(src), SetCC); // Zero out the dest. ma_mov(Imm32(0), dest); // Set the hold appropriately. ma_mov(Imm32(1), hold); ma_mov(Imm32(-1), hold, Signed); - ma_rsb(Imm32(0), tmp, SetCC, Signed); + as_rsb(tmp, tmp, Imm8(0), SetCC, Signed); // Begin the main loop. bind(&head); { - AutoRegisterScope scratch2(asMasm(), secondScratchReg_); - - // Extract the bottom bits into lr. - ma_and(Imm32(mask), tmp, scratch2); + // Extract the bottom bits. + ma_and(Imm32(mask), tmp, scratch, scratch2); // Add those bits to the accumulator. - ma_add(scratch2, dest, dest); + ma_add(scratch, dest, dest); // Do a trial subtraction, this is the same operation as cmp, but we store // the dest. - ma_sub(dest, Imm32(mask), scratch2, SetCC); + ma_sub(dest, Imm32(mask), scratch, scratch2, SetCC); // If (sum - C) > 0, store sum - C back into sum, thus performing a modulus. - ma_mov(scratch2, dest, LeaveCC, NotSigned); + ma_mov(scratch, dest, LeaveCC, NotSigned); // Get rid of the bits that we extracted before, and set the condition codes. as_mov(tmp, lsr(tmp, shift), SetCC); // If the shift produced zero, finish, otherwise, continue in the loop. ma_b(&head, NonZero); } // Check the hold to see if we need to negate the result. Hold can only be // 1 or -1, so this will never set the 0 flag. - ma_cmp(hold, Imm32(0)); + as_cmp(hold, Imm8(0)); // If the hold was non-zero, negate the result to be in line with what JS // wants this will set the condition codes if we try to negate. - ma_rsb(Imm32(0), dest, SetCC, Signed); + as_rsb(dest, dest, Imm8(0), SetCC, Signed); // Since the Zero flag is not set by the compare, we can *only* set the Zero // flag in the rsb, so Zero is set iff we negated zero (e.g. the result of // the computation was -0.0). } void -MacroAssemblerARM::ma_smod(Register num, Register div, Register dest) -{ - ScratchRegisterScope scratch(asMasm()); +MacroAssemblerARM::ma_smod(Register num, Register div, Register dest, AutoRegisterScope& scratch) +{ as_sdiv(scratch, num, div); as_mls(dest, num, scratch, div); } void -MacroAssemblerARM::ma_umod(Register num, Register div, Register dest) -{ - ScratchRegisterScope scratch(asMasm()); +MacroAssemblerARM::ma_umod(Register num, Register div, Register dest, AutoRegisterScope& scratch) +{ as_udiv(scratch, num, div); as_mls(dest, num, scratch, div); } // Division void MacroAssemblerARM::ma_sdiv(Register num, Register div, Register dest, Condition cond) { @@ -1026,60 +1016,52 @@ MacroAssemblerARM::ma_udiv(Register num, // Miscellaneous instructions. void MacroAssemblerARM::ma_clz(Register src, Register dest, Condition cond) { as_clz(dest, src, cond); } void -MacroAssemblerARM::ma_ctz(Register src, Register dest) +MacroAssemblerARM::ma_ctz(Register src, Register dest, AutoRegisterScope& scratch) { // int c = __clz(a & -a); // return a ? 31 - c : c; - - ScratchRegisterScope scratch(asMasm()); as_rsb(scratch, src, Imm8(0), SetCC); as_and(dest, src, O2Reg(scratch), LeaveCC); as_clz(dest, dest); as_rsb(dest, dest, Imm8(0x1F), LeaveCC, Assembler::NotEqual); } // Memory. // Shortcut for when we know we're transferring 32 bits of data. void MacroAssemblerARM::ma_dtr(LoadStore ls, Register rn, Imm32 offset, Register rt, - Index mode, Assembler::Condition cc) -{ - ma_dataTransferN(ls, 32, true, rn, offset, rt, mode, cc); -} - -void -MacroAssemblerARM::ma_dtr(LoadStore ls, Register rn, Register rm, Register rt, - Index mode, Assembler::Condition cc) -{ - MOZ_CRASH("Feature NYI"); + AutoRegisterScope& scratch, Index mode, Assembler::Condition cc) +{ + ma_dataTransferN(ls, 32, true, rn, offset, rt, scratch, mode, cc); +} + +void +MacroAssemblerARM::ma_dtr(LoadStore ls, Register rt, const Address& addr, + AutoRegisterScope& scratch, Index mode, Condition cc) +{ + ma_dataTransferN(ls, 32, true, addr.base, Imm32(addr.offset), rt, scratch, mode, cc); } void MacroAssemblerARM::ma_str(Register rt, DTRAddr addr, Index mode, Condition cc) { as_dtr(IsStore, 32, mode, rt, addr, cc); } void -MacroAssemblerARM::ma_dtr(LoadStore ls, Register rt, const Address& addr, Index mode, Condition cc) -{ - ma_dataTransferN(ls, 32, true, addr.base, Imm32(addr.offset), rt, mode, cc); -} - -void -MacroAssemblerARM::ma_str(Register rt, const Address& addr, Index mode, Condition cc) -{ - ma_dtr(IsStore, rt, addr, mode, cc); +MacroAssemblerARM::ma_str(Register rt, const Address& addr, AutoRegisterScope& scratch, Index mode, Condition cc) +{ + ma_dtr(IsStore, rt, addr, scratch, mode, cc); } void MacroAssemblerARM::ma_strd(Register rt, DebugOnly<Register> rt2, EDtrAddr addr, Index mode, Condition cc) { MOZ_ASSERT((rt.code() & 1) == 0); MOZ_ASSERT(rt2.value.code() == rt.code() + 1); as_extdtr(IsStore, 64, true, mode, rt, addr, cc); @@ -1087,19 +1069,19 @@ MacroAssemblerARM::ma_strd(Register rt, void MacroAssemblerARM::ma_ldr(DTRAddr addr, Register rt, Index mode, Condition cc) { as_dtr(IsLoad, 32, mode, rt, addr, cc); } void -MacroAssemblerARM::ma_ldr(const Address& addr, Register rt, Index mode, Condition cc) -{ - ma_dtr(IsLoad, rt, addr, mode, cc); +MacroAssemblerARM::ma_ldr(const Address& addr, Register rt, AutoRegisterScope& scratch, Index mode, Condition cc) +{ + ma_dtr(IsLoad, rt, addr, scratch, mode, cc); } void MacroAssemblerARM::ma_ldrb(DTRAddr addr, Register rt, Index mode, Condition cc) { as_dtr(IsLoad, 8, mode, rt, addr, cc); } @@ -1142,50 +1124,64 @@ void MacroAssemblerARM::ma_strb(Register rt, DTRAddr addr, Index mode, Condition cc) { as_dtr(IsStore, 8, mode, rt, addr, cc); } // Specialty for moving N bits of data, where n == 8,16,32,64. BufferOffset MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned, - Register rn, Register rm, Register rt, - Index mode, Assembler::Condition cc, unsigned shiftAmount) -{ + Register rn, Register rm, Register rt, AutoRegisterScope& scratch, + Index mode, Assembler::Condition cc, Scale scale) +{ + MOZ_ASSERT(size == 8 || size == 16 || size == 32 || size == 64); + if (size == 32 || (size == 8 && !IsSigned)) - return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrRegImmShift(rm, LSL, shiftAmount)), cc); - - ScratchRegisterScope scratch(asMasm()); - - if (shiftAmount != 0) { - MOZ_ASSERT(rn != scratch); - MOZ_ASSERT(rt != scratch); - ma_lsl(Imm32(shiftAmount), rm, scratch); + return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrRegImmShift(rm, LSL, scale)), cc); + + if (scale != TimesOne) { + ma_lsl(Imm32(scale), rm, scratch); rm = scratch; } return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffReg(rm)), cc); } +// No scratch register is required if scale is TimesOne. BufferOffset MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned, - Register rn, Imm32 offset, Register rt, + Register rn, Register rm, Register rt, Index mode, Assembler::Condition cc) { + MOZ_ASSERT(size == 8 || size == 16 || size == 32 || size == 64); + if (size == 32 || (size == 8 && !IsSigned)) + return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrRegImmShift(rm, LSL, TimesOne)), cc); + return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffReg(rm)), cc); +} + + +BufferOffset +MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned, + Register rn, Imm32 offset, Register rt, AutoRegisterScope& scratch, + Index mode, Assembler::Condition cc) +{ + MOZ_ASSERT(!(ls == IsLoad && mode == PostIndex && rt == pc), + "Large-offset PostIndex loading into PC requires special logic: see ma_popn_pc()."); + int off = offset.value; // We can encode this as a standard ldr. if (size == 32 || (size == 8 && !IsSigned) ) { if (off < 4096 && off > -4096) { // This encodes as a single instruction, Emulating mode's behavior // in a multi-instruction sequence is not necessary. return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrOffImm(off)), cc); } - // We cannot encode this offset in a a single ldr. For mode == index, + // We cannot encode this offset in a single ldr. For mode == index, // try to encode it as |add scratch, base, imm; ldr dest, [scratch, +offset]|. // This does not wark for mode == PreIndex or mode == PostIndex. // PreIndex is simple, just do the add into the base register first, // then do a PreIndex'ed load. PostIndexed loads can be tricky. // Normally, doing the load with an index of 0, then doing an add would // work, but if the destination is the PC, you don't get to execute the // instruction after the branch, which will lead to the base register // not being updated correctly. Explicitly handle this case, without @@ -1193,56 +1189,23 @@ MacroAssemblerARM::ma_dataTransferN(Load // mode == Offset // add scratch, base, offset_hi // ldr dest, [scratch, +offset_lo] // // mode == PreIndex // add base, base, offset_hi // ldr dest, [base, +offset_lo]! - // - // mode == PostIndex, dest == pc - // ldr scratch, [base] - // add base, base, offset_hi - // add base, base, offset_lo - // mov dest, scratch - // PostIndex with the pc as the destination needs to be handled - // specially, since in the code below, the write into 'dest' is going to - // alter the control flow, so the following instruction would never get - // emitted. - // - // mode == PostIndex, dest != pc - // ldr dest, [base], offset_lo - // add base, base, offset_hi - - if (rt == pc && mode == PostIndex && ls == IsLoad) { - ScratchRegisterScope scratch(asMasm()); - ma_mov(rn, scratch); - ma_alu(rn, offset, rn, OpAdd); - return as_dtr(IsLoad, size, Offset, pc, DTRAddr(scratch, DtrOffImm(0)), cc); - } - - // Often this code is called with rt as the ScratchRegister. - // The register is logically owned by the caller, so we cannot ask - // for exclusive ownership here. If full checking is desired, - // this function should take an explicit scratch register argument. - const Register& scratch = ScratchRegister; - MOZ_ASSERT(rn != scratch); int bottom = off & 0xfff; int neg_bottom = 0x1000 - bottom; - // For a regular offset, base == ScratchRegister does what we want. - // Modify the scratch register, leaving the actual base unscathed. - Register base = scratch; - // For the preindex case, we want to just re-use rn as the base - // register, so when the base register is updated *before* the load, rn - // is updated. - if (mode == PreIndex) - base = rn; + + MOZ_ASSERT(rn != scratch); MOZ_ASSERT(mode != PostIndex); + // At this point, both off - bottom and off + neg_bottom will be // reasonable-ish quantities. // // Note a neg_bottom of 0x1000 can not be encoded as an immediate // negative offset in the instruction and this occurs when bottom is // zero, so this case is guarded against below. if (off < 0) { Operand2 sub_off = Imm8(-(off - bottom)); // sub_off = bottom - off @@ -1279,18 +1242,16 @@ MacroAssemblerARM::ma_dataTransferN(Load as_add(scratch, rn, sub_off, LeaveCC, cc); return as_dtr(ls, size, Offset, rt, DTRAddr(scratch, DtrOffImm(-neg_bottom)), cc); } } ma_mov(offset, scratch); return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrRegImmShift(scratch, LSL, 0))); } else { - ScratchRegisterScope scratch(asMasm()); - // Should attempt to use the extended load/store instructions. if (off < 256 && off > -256) return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffImm(off)), cc); // We cannot encode this offset in a single extldr. Try to encode it as // an add scratch, base, imm; extldr dest, [scratch, +offset]. int bottom = off & 0xff; int neg_bottom = 0x100 - bottom; @@ -1346,31 +1307,48 @@ MacroAssemblerARM::ma_dataTransferN(Load ma_mov(offset, scratch); return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffReg(scratch)), cc); } } void MacroAssemblerARM::ma_pop(Register r) { - ma_dtr(IsLoad, sp, Imm32(4), r, PostIndex); + as_dtr(IsLoad, 32, PostIndex, r, DTRAddr(sp, DtrOffImm(4))); +} + +void +MacroAssemblerARM::ma_popn_pc(Imm32 n, AutoRegisterScope& scratch, AutoRegisterScope& scratch2) +{ + // pc <- [sp]; sp += n + int32_t nv = n.value; + + if (nv < 4096 && nv >= -4096) { + as_dtr(IsLoad, 32, PostIndex, pc, DTRAddr(sp, DtrOffImm(nv))); + } else { + ma_mov(sp, scratch); + ma_add(Imm32(n), sp, scratch2); + as_dtr(IsLoad, 32, Offset, pc, DTRAddr(scratch, DtrOffImm(0))); + } } void MacroAssemblerARM::ma_push(Register r) { - // Pushing sp is not well defined: use two instructions. - if (r == sp) { - ScratchRegisterScope scratch(asMasm()); - ma_mov(sp, scratch); - ma_dtr(IsStore, sp, Imm32(-4), scratch, PreIndex); - return; - } - - ma_dtr(IsStore, sp, Imm32(-4), r, PreIndex); + MOZ_ASSERT(r != sp, "Use ma_push_sp()."); + as_dtr(IsStore, 32, PreIndex, r, DTRAddr(sp, DtrOffImm(-4))); +} + +void +MacroAssemblerARM::ma_push_sp(Register r, AutoRegisterScope& scratch) +{ + // Pushing sp is not well-defined: use two instructions. + MOZ_ASSERT(r == sp); + ma_mov(sp, scratch); + as_dtr(IsStore, 32, PreIndex, scratch, DTRAddr(sp, DtrOffImm(-4))); } void MacroAssemblerARM::ma_vpop(VFPRegister r) { startFloatTransferM(IsLoad, sp, IA, WriteBack); transferFloatReg(r); finishFloatTransfer(); @@ -1720,26 +1698,25 @@ MacroAssemblerARM::ma_vxfer(Register src void MacroAssemblerARM::ma_vxfer(Register src1, Register src2, FloatRegister dest, Condition cc) { as_vxfer(src1, src2, VFPRegister(dest), CoreToFloat, cc); } BufferOffset -MacroAssemblerARM::ma_vdtr(LoadStore ls, const Address& addr, VFPRegister rt, Condition cc) +MacroAssemblerARM::ma_vdtr(LoadStore ls, const Address& addr, VFPRegister rt, + AutoRegisterScope& scratch, Condition cc) { int off = addr.offset; MOZ_ASSERT((off & 3) == 0); Register base = addr.base; if (off > -1024 && off < 1024) return as_vdtr(ls, rt, Operand(addr).toVFPAddr(), cc); - ScratchRegisterScope scratch(asMasm()); - // We cannot encode this offset in a a single ldr. Try to encode it as an // add scratch, base, imm; ldr dest, [scratch, +offset]. int bottom = off & (0xff << 2); int neg_bottom = (0x100 << 2) - bottom; // At this point, both off - bottom and off + neg_bottom will be // reasonable-ish quantities. // // Note a neg_bottom of 0x400 can not be encoded as an immediate negative @@ -1775,60 +1752,70 @@ MacroAssemblerARM::ma_vdtr(LoadStore ls, if (!sub_off.invalid && bottom != 0) { // Guarded against by: bottom != 0 MOZ_ASSERT(neg_bottom < 0x400); // sub_off = neg_bottom + off as_add(scratch, base, sub_off, LeaveCC, cc); return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(-neg_bottom)), cc); } } - ma_add(base, Imm32(off), scratch, LeaveCC, cc); + + // Safe to use scratch as dest, since ma_add() overwrites dest at the end + // and can't use it as internal scratch since it may also == base. + ma_add(base, Imm32(off), scratch, scratch, LeaveCC, cc); return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(0)), cc); } BufferOffset MacroAssemblerARM::ma_vldr(VFPAddr addr, VFPRegister dest, Condition cc) { return as_vdtr(IsLoad, dest, addr, cc); } BufferOffset -MacroAssemblerARM::ma_vldr(const Address& addr, VFPRegister dest, Condition cc) -{ - return ma_vdtr(IsLoad, addr, dest, cc); +MacroAssemblerARM::ma_vldr(const Address& addr, VFPRegister dest, AutoRegisterScope& scratch, Condition cc) +{ + return ma_vdtr(IsLoad, addr, dest, scratch, cc); } BufferOffset -MacroAssemblerARM::ma_vldr(VFPRegister src, Register base, Register index, int32_t shift, - Condition cc) -{ - ScratchRegisterScope scratch(asMasm()); +MacroAssemblerARM::ma_vldr(VFPRegister src, Register base, Register index, AutoRegisterScope& scratch, + int32_t shift, Condition cc) +{ as_add(scratch, base, lsl(index, shift), LeaveCC, cc); - return ma_vldr(Address(scratch, 0), src, cc); + return as_vdtr(IsLoad, src, Operand(Address(scratch, 0)).toVFPAddr(), cc); } BufferOffset MacroAssemblerARM::ma_vstr(VFPRegister src, VFPAddr addr, Condition cc) { return as_vdtr(IsStore, src, addr, cc); } BufferOffset -MacroAssemblerARM::ma_vstr(VFPRegister src, const Address& addr, Condition cc) -{ - return ma_vdtr(IsStore, addr, src, cc); +MacroAssemblerARM::ma_vstr(VFPRegister src, const Address& addr, AutoRegisterScope& scratch, Condition cc) +{ + return ma_vdtr(IsStore, addr, src, scratch, cc); } BufferOffset -MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, int32_t shift, - int32_t offset, Condition cc) -{ - ScratchRegisterScope scratch(asMasm()); +MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, AutoRegisterScope& scratch, + AutoRegisterScope& scratch2, int32_t shift, int32_t offset, Condition cc) +{ as_add(scratch, base, lsl(index, shift), LeaveCC, cc); - return ma_vstr(src, Address(scratch, offset), cc); + return ma_vstr(src, Address(scratch, offset), scratch2, cc); +} + +// Without an offset, no second scratch register is necessary. +BufferOffset +MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, AutoRegisterScope& scratch, + int32_t shift, Condition cc) +{ + as_add(scratch, base, lsl(index, shift), LeaveCC, cc); + return as_vdtr(IsStore, src, Operand(Address(scratch, 0)).toVFPAddr(), cc); } bool MacroAssemblerARMCompat::buildOOLFakeExitFrame(void* fakeReturnAddr) { DebugOnly<uint32_t> initialDepth = asMasm().framePushed(); uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS, ExitFrameLayout::Size()); @@ -1892,117 +1879,126 @@ MacroAssemblerARMCompat::movePtr(wasm::S { append(AsmJSAbsoluteAddress(CodeOffset(currentOffset()), imm)); ma_movPatchable(Imm32(-1), dest, Always); } void MacroAssemblerARMCompat::load8ZeroExtend(const Address& address, Register dest) { - ma_dataTransferN(IsLoad, 8, false, address.base, Imm32(address.offset), dest); + ScratchRegisterScope scratch(asMasm()); + ma_dataTransferN(IsLoad, 8, false, address.base, Imm32(address.offset), dest, scratch); } void MacroAssemblerARMCompat::load8ZeroExtend(const BaseIndex& src, Register dest) { Register base = src.base; uint32_t scale = Imm32::ShiftOf(src.scale).value; + ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); + if (src.offset == 0) { ma_ldrb(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest); } else { - ScratchRegisterScope scratch(asMasm()); - ma_add(base, Imm32(src.offset), scratch); + ma_add(base, Imm32(src.offset), scratch, scratch2); ma_ldrb(DTRAddr(scratch, DtrRegImmShift(src.index, LSL, scale)), dest); } } void MacroAssemblerARMCompat::load8SignExtend(const Address& address, Register dest) { - ma_dataTransferN(IsLoad, 8, true, address.base, Imm32(address.offset), dest); + ScratchRegisterScope scratch(asMasm()); + ma_dataTransferN(IsLoad, 8, true, address.base, Imm32(address.offset), dest, scratch); } void MacroAssemblerARMCompat::load8SignExtend(const BaseIndex& src, Register dest) { Register index = src.index; ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); // ARMv7 does not have LSL on an index register with an extended load. if (src.scale != TimesOne) { ma_lsl(Imm32::ShiftOf(src.scale), index, scratch); index = scratch; } if (src.offset != 0) { if (index != scratch) { ma_mov(index, scratch); index = scratch; } - ma_add(Imm32(src.offset), index); + ma_add(Imm32(src.offset), index, scratch2); } ma_ldrsb(EDtrAddr(src.base, EDtrOffReg(index)), dest); } void MacroAssemblerARMCompat::load16ZeroExtend(const Address& address, Register dest) { - ma_dataTransferN(IsLoad, 16, false, address.base, Imm32(address.offset), dest); + ScratchRegisterScope scratch(asMasm()); + ma_dataTransferN(IsLoad, 16, false, address.base, Imm32(address.offset), dest, scratch); } void MacroAssemblerARMCompat::load16ZeroExtend(const BaseIndex& src, Register dest) { Register index = src.index; ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); // ARMv7 does not have LSL on an index register with an extended load. if (src.scale != TimesOne) { ma_lsl(Imm32::ShiftOf(src.scale), index, scratch); index = scratch; } if (src.offset != 0) { if (index != scratch) { ma_mov(index, scratch); index = scratch; } - ma_add(Imm32(src.offset), index); + ma_add(Imm32(src.offset), index, scratch2); } ma_ldrh(EDtrAddr(src.base, EDtrOffReg(index)), dest); } void MacroAssemblerARMCompat::load16SignExtend(const Address& address, Register dest) { - ma_dataTransferN(IsLoad, 16, true, address.base, Imm32(address.offset), dest); + ScratchRegisterScope scratch(asMasm()); + ma_dataTransferN(IsLoad, 16, true, address.base, Imm32(address.offset), dest, scratch); } void MacroAssemblerARMCompat::load16SignExtend(const BaseIndex& src, Register dest) { Register index = src.index; ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); // We don't have LSL on index register yet. if (src.scale != TimesOne) { ma_lsl(Imm32::ShiftOf(src.scale), index, scratch); index = scratch; } if (src.offset != 0) { if (index != scratch) { ma_mov(index, scratch); index = scratch; } - ma_add(Imm32(src.offset), index); + ma_add(Imm32(src.offset), index, scratch2); } ma_ldrsh(EDtrAddr(src.base, EDtrOffReg(index)), dest); } void MacroAssemblerARMCompat::load32(const Address& address, Register dest) { loadPtr(address, dest); @@ -2018,34 +2014,35 @@ void MacroAssemblerARMCompat::load32(AbsoluteAddress address, Register dest) { loadPtr(address, dest); } void MacroAssemblerARMCompat::loadPtr(const Address& address, Register dest) { - ma_ldr(address, dest); + ScratchRegisterScope scratch(asMasm()); + ma_ldr(address, dest, scratch); } void MacroAssemblerARMCompat::loadPtr(const BaseIndex& src, Register dest) { Register base = src.base; uint32_t scale = Imm32::ShiftOf(src.scale).value; + ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); + if (src.offset != 0) { - ScratchRegisterScope scratch(asMasm()); - ma_mov(base, scratch); - ma_add(Imm32(src.offset), scratch); + ma_add(base, Imm32(src.offset), scratch, scratch2); ma_ldr(DTRAddr(scratch, DtrRegImmShift(src.index, LSL, scale)), dest); - return; + } else { + ma_ldr(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest); } - - ma_ldr(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest); } void MacroAssemblerARMCompat::loadPtr(AbsoluteAddress address, Register dest) { MOZ_ASSERT(dest != pc); // Use dest as a scratch register. movePtr(ImmWord(uintptr_t(address.addr)), dest); loadPtr(Address(dest, 0), dest); @@ -2057,160 +2054,202 @@ MacroAssemblerARMCompat::loadPtr(wasm::S MOZ_ASSERT(dest != pc); // Use dest as a scratch register. movePtr(address, dest); loadPtr(Address(dest, 0), dest); } void MacroAssemblerARMCompat::loadPrivate(const Address& address, Register dest) { - ma_ldr(ToPayload(address), dest); + ScratchRegisterScope scratch(asMasm()); + ma_ldr(ToPayload(address), dest, scratch); } void MacroAssemblerARMCompat::loadDouble(const Address& address, FloatRegister dest) { - ma_vldr(address, dest); + ScratchRegisterScope scratch(asMasm()); + ma_vldr(address, dest, scratch); } void MacroAssemblerARMCompat::loadDouble(const BaseIndex& src, FloatRegister dest) { // VFP instructions don't even support register Base + register Index modes, // so just add the index, then handle the offset like normal. Register base = src.base; Register index = src.index; uint32_t scale = Imm32::ShiftOf(src.scale).value; int32_t offset = src.offset; ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); + as_add(scratch, base, lsl(index, scale)); - ma_vldr(Address(scratch, offset), dest); + ma_vldr(Address(scratch, offset), dest, scratch2); } void MacroAssemblerARMCompat::loadFloatAsDouble(const Address& address, FloatRegister dest) { + ScratchRegisterScope scratch(asMasm()); + VFPRegister rt = dest; - ma_vldr(address, rt.singleOverlay()); + ma_vldr(address, rt.singleOverlay(), scratch); as_vcvt(rt, rt.singleOverlay()); } void MacroAssemblerARMCompat::loadFloatAsDouble(const BaseIndex& src, FloatRegister dest) { // VFP instructions don't even support register Base + register Index modes, // so just add the index, then handle the offset like normal. Register base = src.base; Register index = src.index; uint32_t scale = Imm32::ShiftOf(src.scale).value; int32_t offset = src.offset; VFPRegister rt = dest; ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); + as_add(scratch, base, lsl(index, scale)); - ma_vldr(Address(scratch, offset), rt.singleOverlay()); + ma_vldr(Address(scratch, offset), rt.singleOverlay(), scratch2); as_vcvt(rt, rt.singleOverlay()); } void MacroAssemblerARMCompat::loadFloat32(const Address& address, FloatRegister dest) { - ma_vldr(address, VFPRegister(dest).singleOverlay()); + ScratchRegisterScope scratch(asMasm()); + ma_vldr(address, VFPRegister(dest).singleOverlay(), scratch); } void MacroAssemblerARMCompat::loadFloat32(const BaseIndex& src, FloatRegister dest) { // VFP instructions don't even support register Base + register Index modes, // so just add the index, then handle the offset like normal. Register base = src.base; Register index = src.index; uint32_t scale = Imm32::ShiftOf(src.scale).value; int32_t offset = src.offset; ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); + as_add(scratch, base, lsl(index, scale)); - ma_vldr(Address(scratch, offset), VFPRegister(dest).singleOverlay()); + ma_vldr(Address(scratch, offset), VFPRegister(dest).singleOverlay(), scratch2); } void MacroAssemblerARMCompat::store8(Imm32 imm, const Address& address) { - AutoRegisterScope scratch2(asMasm(), secondScratchReg_); + SecondScratchRegisterScope scratch2(asMasm()); ma_mov(imm, scratch2); store8(scratch2, address); } void MacroAssemblerARMCompat::store8(Register src, const Address& address) { - ma_dataTransferN(IsStore, 8, false, address.base, Imm32(address.offset), src); + ScratchRegisterScope scratch(asMasm()); + ma_dataTransferN(IsStore, 8, false, address.base, Imm32(address.offset), src, scratch); } void MacroAssemblerARMCompat::store8(Imm32 imm, const BaseIndex& dest) { - AutoRegisterScope scratch2(asMasm(), secondScratchReg_); - ma_mov(imm, scratch2); - store8(scratch2, dest); + Register base = dest.base; + uint32_t scale = Imm32::ShiftOf(dest.scale).value; + + ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); + + if (dest.offset != 0) { + ma_add(base, Imm32(dest.offset), scratch, scratch2); + ma_mov(imm, scratch2); + ma_strb(scratch2, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale))); + } else { + ma_mov(imm, scratch2); + ma_strb(scratch2, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale))); + } } void MacroAssemblerARMCompat::store8(Register src, const BaseIndex& dest) { Register base = dest.base; uint32_t scale = Imm32::ShiftOf(dest.scale).value; ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); if (dest.offset != 0) { - ma_add(base, Imm32(dest.offset), scratch); - base = scratch; + ma_add(base, Imm32(dest.offset), scratch, scratch2); + ma_strb(src, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale))); + } else { + ma_strb(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale))); } - ma_strb(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale))); } void MacroAssemblerARMCompat::store16(Imm32 imm, const Address& address) { - AutoRegisterScope scratch2(asMasm(), secondScratchReg_); + SecondScratchRegisterScope scratch2(asMasm()); ma_mov(imm, scratch2); store16(scratch2, address); } void MacroAssemblerARMCompat::store16(Register src, const Address& address) { - ma_dataTransferN(IsStore, 16, false, address.base, Imm32(address.offset), src); + ScratchRegisterScope scratch(asMasm()); + ma_dataTransferN(IsStore, 16, false, address.base, Imm32(address.offset), src, scratch); } void MacroAssemblerARMCompat::store16(Imm32 imm, const BaseIndex& dest) { - AutoRegisterScope scratch2(asMasm(), secondScratchReg_); + Register index = dest.index; + + ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); + + // We don't have LSL on index register yet. + if (dest.scale != TimesOne) { + ma_lsl(Imm32::ShiftOf(dest.scale), index, scratch); + index = scratch; + } + + if (dest.offset != 0) { + ma_add(index, Imm32(dest.offset), scratch, scratch2); + index = scratch; + } + ma_mov(imm, scratch2); - store16(scratch2, dest); + ma_strh(scratch2, EDtrAddr(dest.base, EDtrOffReg(index))); } void MacroAssemblerARMCompat::store16(Register src, const BaseIndex& address) { Register index = address.index; ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); // We don't have LSL on index register yet. if (address.scale != TimesOne) { ma_lsl(Imm32::ShiftOf(address.scale), index, scratch); index = scratch; } if (address.offset != 0) { - ma_add(index, Imm32(address.offset), scratch); + ma_add(index, Imm32(address.offset), scratch, scratch2); index = scratch; } ma_strh(src, EDtrAddr(address.base, EDtrOffReg(index))); } void MacroAssemblerARMCompat::store32(Register src, AbsoluteAddress address) { @@ -2221,42 +2260,57 @@ void MacroAssemblerARMCompat::store32(Register src, const Address& address) { storePtr(src, address); } void MacroAssemblerARMCompat::store32(Imm32 src, const Address& address) { - AutoRegisterScope scratch2(asMasm(), secondScratchReg_); - move32(src, scratch2); - storePtr(scratch2, address); + ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); + move32(src, scratch); + ma_str(scratch, address, scratch2); } void MacroAssemblerARMCompat::store32(Imm32 imm, const BaseIndex& dest) { + Register base = dest.base; + uint32_t scale = Imm32::ShiftOf(dest.scale).value; + ScratchRegisterScope scratch(asMasm()); - ma_mov(imm, scratch); - store32(scratch, dest); + SecondScratchRegisterScope scratch2(asMasm()); + + if (dest.offset != 0) { + ma_add(base, Imm32(dest.offset), scratch, scratch2); + ma_mov(imm, scratch2); + ma_str(scratch2, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale))); + } else { + ma_mov(imm, scratch); + ma_str(scratch, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale))); + } + } void MacroAssemblerARMCompat::store32(Register src, const BaseIndex& dest) { Register base = dest.base; uint32_t scale = Imm32::ShiftOf(dest.scale).value; - AutoRegisterScope scratch2(asMasm(), secondScratchReg_); + ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); if (dest.offset != 0) { - ma_add(base, Imm32(dest.offset), scratch2); - base = scratch2; + ma_add(base, Imm32(dest.offset), scratch, scratch2); + ma_str(src, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale))); + } else { + ma_str(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale))); } - ma_str(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale))); } template <typename T> void MacroAssemblerARMCompat::storePtr(ImmWord imm, T address) { ScratchRegisterScope scratch(asMasm()); movePtr(imm, scratch); @@ -2286,31 +2340,32 @@ MacroAssemblerARMCompat::storePtr(ImmGCP } template void MacroAssemblerARMCompat::storePtr<Address>(ImmGCPtr imm, Address address); template void MacroAssemblerARMCompat::storePtr<BaseIndex>(ImmGCPtr imm, BaseIndex address); void MacroAssemblerARMCompat::storePtr(Register src, const Address& address) { - ma_str(src, address); + SecondScratchRegisterScope scratch2(asMasm()); + ma_str(src, address, scratch2); } void MacroAssemblerARMCompat::storePtr(Register src, const BaseIndex& address) { store32(src, address); } void MacroAssemblerARMCompat::storePtr(Register src, AbsoluteAddress dest) { ScratchRegisterScope scratch(asMasm()); movePtr(ImmWord(uintptr_t(dest.addr)), scratch); - storePtr(src, Address(scratch, 0)); + ma_str(src, DTRAddr(scratch, DtrOffImm(0))); } // Note: this function clobbers the input register. void MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output) { if (HasVFPv3()) { Label notSplit; @@ -2326,33 +2381,32 @@ MacroAssembler::clampDoubleToUint8(Float } // Move the fixed point value into an integer register. { ScratchFloat32Scope scratchFloat(*this); as_vxfer(output, InvalidReg, scratchFloat.uintOverlay(), FloatToCore); } + ScratchRegisterScope scratch(*this); + // See if this value *might* have been an exact integer after adding // 0.5. This tests the 1/2 through 1/16,777,216th places, but 0.5 needs // to be tested out to the 1/140,737,488,355,328th place. - ma_tst(output, Imm32(0x00ffffff)); + ma_tst(output, Imm32(0x00ffffff), scratch); // Convert to a uint8 by shifting out all of the fraction bits. ma_lsr(Imm32(24), output, output); // If any of the bottom 24 bits were non-zero, then we're good, since // this number can't be exactly XX.0 ma_b(¬Split, NonZero); - { - ScratchRegisterScope scratch(*this); - as_vxfer(scratch, InvalidReg, input, FloatToCore); - ma_cmp(scratch, Imm32(0)); - } + as_vxfer(scratch, InvalidReg, input, FloatToCore); + as_cmp(scratch, Imm8(0)); // If the lower 32 bits of the double were 0, then this was an exact number, // and it should be even. - ma_bic(Imm32(1), output, LeaveCC, Zero); + as_bic(output, output, Imm8(1), LeaveCC, Zero); bind(¬Split); } else { ScratchDoubleScope scratchDouble(*this); MOZ_ASSERT(input != scratchDouble); loadConstantDouble(0.5, scratchDouble); Label outOfRange; ma_vcmpz(input); @@ -2360,115 +2414,120 @@ MacroAssembler::clampDoubleToUint8(Float ma_vadd(input, scratchDouble, input); // Do the conversion to an integer. as_vcvt(VFPRegister(scratchDouble).uintOverlay(), VFPRegister(input)); // Copy the converted value out. as_vxfer(output, InvalidReg, scratchDouble, FloatToCore); as_vmrs(pc); ma_mov(Imm32(0), output, Overflow); // NaN => 0 ma_b(&outOfRange, Overflow); // NaN - ma_cmp(output, Imm32(0xff)); + as_cmp(output, Imm8(0xff)); ma_mov(Imm32(0xff), output, Above); ma_b(&outOfRange, Above); // Convert it back to see if we got the same value back. as_vcvt(scratchDouble, VFPRegister(scratchDouble).uintOverlay()); // Do the check. as_vcmp(scratchDouble, input); as_vmrs(pc); - ma_bic(Imm32(1), output, LeaveCC, Zero); + as_bic(output, output, Imm8(1), LeaveCC, Zero); bind(&outOfRange); } } void MacroAssemblerARMCompat::cmp32(Register lhs, Imm32 rhs) { - MOZ_ASSERT(lhs != ScratchRegister); - ma_cmp(lhs, rhs); + ScratchRegisterScope scratch(asMasm()); + ma_cmp(lhs, rhs, scratch); } void MacroAssemblerARMCompat::cmp32(Register lhs, Register rhs) { ma_cmp(lhs, rhs); } void MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmWord rhs) { - MOZ_ASSERT(lhs != ScratchRegister); - ma_cmp(lhs, Imm32(rhs.value)); + cmp32(lhs, Imm32(rhs.value)); } void MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmPtr rhs) { - return cmpPtr(lhs, ImmWord(uintptr_t(rhs.value))); + cmpPtr(lhs, ImmWord(uintptr_t(rhs.value))); } void MacroAssemblerARMCompat::cmpPtr(Register lhs, Register rhs) { ma_cmp(lhs, rhs); } void MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmGCPtr rhs) { - ma_cmp(lhs, rhs); + ScratchRegisterScope scratch(asMasm()); + ma_cmp(lhs, rhs, scratch); } void MacroAssemblerARMCompat::cmpPtr(Register lhs, Imm32 rhs) { - ma_cmp(lhs, rhs); + cmp32(lhs, rhs); } void MacroAssemblerARMCompat::cmpPtr(const Address& lhs, Register rhs) { ScratchRegisterScope scratch(asMasm()); - loadPtr(lhs, scratch); - cmpPtr(scratch, rhs); + SecondScratchRegisterScope scratch2(asMasm()); + ma_ldr(lhs, scratch, scratch2); + ma_cmp(scratch, rhs); } void MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmWord rhs) { - AutoRegisterScope scratch2(asMasm(), secondScratchReg_); - loadPtr(lhs, scratch2); - ma_cmp(scratch2, Imm32(rhs.value)); + ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); + ma_ldr(lhs, scratch, scratch2); + ma_cmp(scratch, Imm32(rhs.value), scratch2); } void MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmPtr rhs) { cmpPtr(lhs, ImmWord(uintptr_t(rhs.value))); } void MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmGCPtr rhs) { - AutoRegisterScope scratch2(asMasm(), secondScratchReg_); - loadPtr(lhs, scratch2); - ma_cmp(scratch2, rhs); + ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); + ma_ldr(lhs, scratch, scratch2); + ma_cmp(scratch, rhs, scratch2); } void MacroAssemblerARMCompat::cmpPtr(const Address& lhs, Imm32 rhs) { - AutoRegisterScope scratch2(asMasm(), secondScratchReg_); - loadPtr(lhs, scratch2); - ma_cmp(scratch2, rhs); + ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); + ma_ldr(lhs, scratch, scratch2); + ma_cmp(scratch, rhs, scratch2); } void MacroAssemblerARMCompat::setStackArg(Register reg, uint32_t arg) { - ma_dataTransferN(IsStore, 32, true, sp, Imm32(arg * sizeof(intptr_t)), reg); + ScratchRegisterScope scratch(asMasm()); + ma_dataTransferN(IsStore, 32, true, sp, Imm32(arg * sizeof(intptr_t)), reg, scratch); } void MacroAssemblerARMCompat::minMaxDouble(FloatRegister srcDest, FloatRegister second, bool canBeNaN, bool isMax) { FloatRegister first = srcDest; @@ -2605,17 +2664,18 @@ MacroAssemblerARMCompat::testBoolean(Ass return cond; } Assembler::Condition MacroAssemblerARMCompat::testDouble(Assembler::Condition cond, const ValueOperand& value) { MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); Assembler::Condition actual = (cond == Equal) ? Below : AboveOrEqual; - ma_cmp(value.typeReg(), ImmTag(JSVAL_TAG_CLEAR)); + ScratchRegisterScope scratch(asMasm()); + ma_cmp(value.typeReg(), ImmTag(JSVAL_TAG_CLEAR), scratch); return actual; } Assembler::Condition MacroAssemblerARMCompat::testNull(Assembler::Condition cond, const ValueOperand& value) { MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); ma_cmp(value.typeReg(), ImmType(JSVAL_TYPE_NULL)); @@ -2965,40 +3025,43 @@ MacroAssemblerARMCompat::unboxNonDouble( { if (operand.payloadReg() != dest) ma_mov(operand.payloadReg(), dest); } void MacroAssemblerARMCompat::unboxNonDouble(const Address& src, Register dest) { - ma_ldr(ToPayload(src), dest); + ScratchRegisterScope scratch(asMasm()); + ma_ldr(ToPayload(src), dest, scratch); } void MacroAssemblerARMCompat::unboxNonDouble(const BaseIndex& src, Register dest) { ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); ma_alu(src.base, lsl(src.index, src.scale), scratch, OpAdd); - ma_ldr(Address(scratch, src.offset), dest); + ma_ldr(Address(scratch, src.offset), dest, scratch2); } void MacroAssemblerARMCompat::unboxDouble(const ValueOperand& operand, FloatRegister dest) { MOZ_ASSERT(dest.isDouble()); as_vxfer(operand.payloadReg(), operand.typeReg(), VFPRegister(dest), CoreToFloat); } void MacroAssemblerARMCompat::unboxDouble(const Address& src, FloatRegister dest) { MOZ_ASSERT(dest.isDouble()); - ma_vldr(src, dest); + ScratchRegisterScope scratch(asMasm()); + ma_vldr(src, dest, scratch); } void MacroAssemblerARMCompat::unboxValue(const ValueOperand& src, AnyRegister dest) { if (dest.isFloat()) { Label notInt32, end; asMasm().branchTestInt32(Assembler::NotEqual, src, ¬Int32); @@ -3031,17 +3094,17 @@ MacroAssemblerARMCompat::boxNonDouble(JS ma_mov(ImmType(type), dest.typeReg()); } void MacroAssemblerARMCompat::boolValueToDouble(const ValueOperand& operand, FloatRegister dest) { VFPRegister d = VFPRegister(dest); loadConstantDouble(1.0, dest); - ma_cmp(operand.payloadReg(), Imm32(0)); + as_cmp(operand.payloadReg(), Imm8(0)); // If the source is 0, then subtract the dest from itself, producing 0. as_vsub(d, d, d, Equal); } void MacroAssemblerARMCompat::int32ValueToDouble(const ValueOperand& operand, FloatRegister dest) { VFPRegister vfpdest = VFPRegister(dest); @@ -3053,17 +3116,17 @@ MacroAssemblerARMCompat::int32ValueToDou as_vcvt(vfpdest, scratch.sintOverlay()); } void MacroAssemblerARMCompat::boolValueToFloat32(const ValueOperand& operand, FloatRegister dest) { VFPRegister d = VFPRegister(dest).singleOverlay(); loadConstantFloat32(1.0, dest); - ma_cmp(operand.payloadReg(), Imm32(0)); + as_cmp(operand.payloadReg(), Imm8(0)); // If the source is 0, then subtract the dest from itself, producing 0. as_vsub(d, d, d, Equal); } void MacroAssemblerARMCompat::int32ValueToFloat32(const ValueOperand& operand, FloatRegister dest) { // Transfer the integral value to a floating point register. @@ -3089,27 +3152,31 @@ MacroAssemblerARMCompat::loadConstantFlo void MacroAssemblerARMCompat::loadInt32OrDouble(const Address& src, FloatRegister dest) { Label notInt32, end; // If it's an int, convert to a double. { ScratchRegisterScope scratch(asMasm()); - - ma_ldr(ToType(src), scratch); + SecondScratchRegisterScope scratch2(asMasm()); + + ma_ldr(ToType(src), scratch, scratch2); asMasm().branchTestInt32(Assembler::NotEqual, scratch, ¬Int32); - ma_ldr(ToPayload(src), scratch); + ma_ldr(ToPayload(src), scratch, scratch2); convertInt32ToDouble(scratch, dest); ma_b(&end); } // Not an int, just load as double. bind(¬Int32); - ma_vldr(src, dest); + { + ScratchRegisterScope scratch(asMasm()); + ma_vldr(src, dest, scratch); + } bind(&end); } void MacroAssemblerARMCompat::loadInt32OrDouble(Register base, Register index, FloatRegister dest, int32_t shift) { Label notInt32, end; @@ -3118,30 +3185,30 @@ MacroAssemblerARMCompat::loadInt32OrDoub ScratchRegisterScope scratch(asMasm()); // If it's an int, convert it to double. ma_alu(base, lsl(index, shift), scratch, OpAdd); // Since we only have one scratch register, we need to stomp over it with // the tag. - ma_ldr(Address(scratch, NUNBOX32_TYPE_OFFSET), scratch); + ma_ldr(DTRAddr(scratch, DtrOffImm(NUNBOX32_TYPE_OFFSET)), scratch); asMasm().branchTestInt32(Assembler::NotEqual, scratch, ¬Int32); // Implicitly requires NUNBOX32_PAYLOAD_OFFSET == 0: no offset provided ma_ldr(DTRAddr(base, DtrRegImmShift(index, LSL, shift)), scratch); convertInt32ToDouble(scratch, dest); ma_b(&end); // Not an int, just load as double. bind(¬Int32); // First, recompute the offset that had been stored in the scratch register // since the scratch register was overwritten loading in the type. ma_alu(base, lsl(index, shift), scratch, OpAdd); - ma_vldr(Address(scratch, 0), dest); + ma_vldr(VFPAddr(scratch, VFPOffImm(0)), dest); bind(&end); } void MacroAssemblerARMCompat::loadConstantDouble(double dp, FloatRegister dest) { loadConstantDouble(wasm::RawF64(dp), dest); } @@ -3174,24 +3241,26 @@ MacroAssemblerARMCompat::testDoubleTruth as_vmrs(pc); as_cmp(r0, O2Reg(r0), Overflow); return truthy ? NonZero : Zero; } Register MacroAssemblerARMCompat::extractObject(const Address& address, Register scratch) { - ma_ldr(ToPayload(address), scratch); + SecondScratchRegisterScope scratch2(asMasm()); + ma_ldr(ToPayload(address), scratch, scratch2); return scratch; } Register MacroAssemblerARMCompat::extractTag(const Address& address, Register scratch) { - ma_ldr(ToType(address), scratch); + SecondScratchRegisterScope scratch2(asMasm()); + ma_ldr(ToType(address), scratch, scratch2); return scratch; } Register MacroAssemblerARMCompat::extractTag(const BaseIndex& address, Register scratch) { ma_alu(address.base, lsl(address.index, address.scale), scratch, OpAdd, LeaveCC); return extractTag(Address(scratch, address.offset), scratch); @@ -3215,18 +3284,19 @@ MacroAssemblerARMCompat::moveValue(const } ///////////////////////////////////////////////////////////////// // X86/X64-common (ARM too now) interface. ///////////////////////////////////////////////////////////////// void MacroAssemblerARMCompat::storeValue(ValueOperand val, const Address& dst) { - ma_str(val.payloadReg(), ToPayload(dst)); - ma_str(val.typeReg(), ToType(dst)); + SecondScratchRegisterScope scratch2(asMasm()); + ma_str(val.payloadReg(), ToPayload(dst), scratch2); + ma_str(val.typeReg(), ToType(dst), scratch2); } void MacroAssemblerARMCompat::storeValue(ValueOperand val, const BaseIndex& dest) { ScratchRegisterScope scratch(asMasm()); if (isValueDTRDCandidate(val) && Abs(dest.offset) <= 255) { @@ -3321,21 +3391,23 @@ MacroAssemblerARMCompat::loadValue(Addre transferReg(val.typeReg()); finishDataTransfer(); return; } } // Ensure that loading the payload does not erase the pointer to the Value // in memory. if (type.base != val.payloadReg()) { - ma_ldr(payload, val.payloadReg()); - ma_ldr(type, val.typeReg()); + SecondScratchRegisterScope scratch2(asMasm()); + ma_ldr(payload, val.payloadReg(), scratch2); + ma_ldr(type, val.typeReg(), scratch2); } else { - ma_ldr(type, val.typeReg()); - ma_ldr(payload, val.payloadReg()); + SecondScratchRegisterScope scratch2(asMasm()); + ma_ldr(type, val.typeReg(), scratch2); + ma_ldr(payload, val.payloadReg(), scratch2); } } void MacroAssemblerARMCompat::tagValue(JSValueType type, Register payload, ValueOperand dest) { MOZ_ASSERT(dest.typeReg() != dest.payloadReg()); if (payload != dest.payloadReg()) @@ -3349,131 +3421,135 @@ MacroAssemblerARMCompat::pushValue(Value ma_push(val.typeReg()); ma_push(val.payloadReg()); } void MacroAssemblerARMCompat::pushValue(const Address& addr) { ScratchRegisterScope scratch(asMasm()); - ma_ldr(ToType(addr), scratch); + SecondScratchRegisterScope scratch2(asMasm()); + + ma_ldr(ToType(addr), scratch, scratch2); ma_push(scratch); - ma_ldr(ToPayloadAfterStackPush(addr), scratch); + ma_ldr(ToPayloadAfterStackPush(addr), scratch, scratch2); ma_push(scratch); } void MacroAssemblerARMCompat::popValue(ValueOperand val) { ma_pop(val.payloadReg()); ma_pop(val.typeReg()); } void MacroAssemblerARMCompat::storePayload(const Value& val, const Address& dest) { - AutoRegisterScope scratch2(asMasm(), secondScratchReg_); + ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); jsval_layout jv = JSVAL_TO_IMPL(val); if (val.isMarkable()) - ma_mov(ImmGCPtr((gc::Cell*)jv.s.payload.ptr), scratch2); + ma_mov(ImmGCPtr((gc::Cell*)jv.s.payload.ptr), scratch); else - ma_mov(Imm32(jv.s.payload.i32), scratch2); - ma_str(scratch2, ToPayload(dest)); + ma_mov(Imm32(jv.s.payload.i32), scratch); + ma_str(scratch, ToPayload(dest), scratch2); } void MacroAssemblerARMCompat::storePayload(Register src, const Address& dest) { - ma_str(src, ToPayload(dest)); + ScratchRegisterScope scratch(asMasm()); + ma_str(src, ToPayload(dest), scratch); } void MacroAssemblerARMCompat::storePayload(const Value& val, const BaseIndex& dest) { unsigned shift = ScaleToShift(dest.scale); ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); jsval_layout jv = JSVAL_TO_IMPL(val); if (val.isMarkable()) ma_mov(ImmGCPtr((gc::Cell*)jv.s.payload.ptr), scratch); else ma_mov(Imm32(jv.s.payload.i32), scratch); // If NUNBOX32_PAYLOAD_OFFSET is not zero, the memory operand [base + index // << shift + imm] cannot be encoded into a single instruction, and cannot // be integrated into the as_dtr call. JS_STATIC_ASSERT(NUNBOX32_PAYLOAD_OFFSET == 0); // If an offset is used, modify the base so that a [base + index << shift] // instruction format can be used. if (dest.offset != 0) - ma_add(dest.base, Imm32(dest.offset), dest.base); + ma_add(dest.base, Imm32(dest.offset), dest.base, scratch2); as_dtr(IsStore, 32, Offset, scratch, DTRAddr(dest.base, DtrRegImmShift(dest.index, LSL, shift))); // Restore the original value of the base, if necessary. if (dest.offset != 0) - ma_sub(dest.base, Imm32(dest.offset), dest.base); + ma_sub(dest.base, Imm32(dest.offset), dest.base, scratch); } void MacroAssemblerARMCompat::storePayload(Register src, const BaseIndex& dest) { unsigned shift = ScaleToShift(dest.scale); MOZ_ASSERT(shift < 32); + ScratchRegisterScope scratch(asMasm()); + // If NUNBOX32_PAYLOAD_OFFSET is not zero, the memory operand [base + index // << shift + imm] cannot be encoded into a single instruction, and cannot // be integrated into the as_dtr call. JS_STATIC_ASSERT(NUNBOX32_PAYLOAD_OFFSET == 0); // Save/restore the base if the BaseIndex has an offset, as above. if (dest.offset != 0) - ma_add(dest.base, Imm32(dest.offset), dest.base); + ma_add(dest.base, Imm32(dest.offset), dest.base, scratch); // Technically, shift > -32 can be handle by changing LSL to ASR, but should // never come up, and this is one less code path to get wrong. as_dtr(IsStore, 32, Offset, src, DTRAddr(dest.base, DtrRegImmShift(dest.index, LSL, shift))); if (dest.offset != 0) - ma_sub(dest.base, Imm32(dest.offset), dest.base); + ma_sub(dest.base, Imm32(dest.offset), dest.base, scratch); } void MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, const Address& dest) { - AutoRegisterScope scratch2(asMasm(), secondScratchReg_); - ma_mov(tag, scratch2); - ma_str(scratch2, ToType(dest)); + ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); + + ma_mov(tag, scratch); + ma_str(scratch, ToType(dest), scratch2); } void MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, const BaseIndex& dest) { Register base = dest.base; Register index = dest.index; unsigned shift = ScaleToShift(dest.scale); - MOZ_ASSERT(base != ScratchRegister); - MOZ_ASSERT(index != ScratchRegister); - - // A value needs to be store a value int base + index << shift + 4. - // ARM cannot handle this in a single operand, so a temp register is - // required. However, the scratch register is presently in use to hold the - // immediate that is being stored into said memory location. Work around - // this by modifying the base so the valid [base + index << shift] format - // can be used, then restore it. - ma_add(base, Imm32(NUNBOX32_TYPE_OFFSET + dest.offset), base); ScratchRegisterScope scratch(asMasm()); + SecondScratchRegisterScope scratch2(asMasm()); + + MOZ_ASSERT(base != scratch && base != scratch2); + MOZ_ASSERT(index != scratch && index != scratch2); + + ma_add(base, Imm32(dest.offset + NUNBOX32_TYPE_OFFSET), scratch2, scratch); ma_mov(tag, scratch); - ma_str(scratch, DTRAddr(base, DtrRegImmShift(index, LSL, shift))); - ma_sub(base, Imm32(NUNBOX32_TYPE_OFFSET + dest.offset), base); + ma_str(scratch, DTRAddr(scratch2, DtrRegImmShift(index, LSL, shift))); } void MacroAssemblerARM::ma_call(ImmPtr dest) { ma_movPatchable(dest, CallReg, Always); as_blx(CallReg); } @@ -3523,78 +3599,94 @@ MacroAssemblerARMCompat::checkStackAlign } void MacroAssemblerARMCompat::handleFailureWithHandlerTail(void* handler) { // Reserve space for exception information. int size = (sizeof(ResumeFromException) + 7) & ~7; - ma_sub(Imm32(size), sp); + Imm8 size8(size); + as_sub(sp, sp, size8); ma_mov(sp, r0); // Call the handler. asMasm().setupUnalignedABICall(r1); asMasm().passABIArg(r0); asMasm().callWithABI(handler); Label entryFrame; Label catch_; Label finally; Label return_; Label bailout; - ma_ldr(Address(sp, offsetof(ResumeFromException, kind)), r0); + { + ScratchRegisterScope scratch(asMasm()); + ma_ldr(Address(sp, offsetof(ResumeFromException, kind)), r0, scratch); + } + asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame); asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_CATCH), &catch_); asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_FINALLY), &finally); asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_); asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout); breakpoint(); // Invalid kind. // No exception handler. Load the error value, load the new stack pointer // and return from the entry frame. bind(&entryFrame); moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); - ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp); + { + ScratchRegisterScope scratch(asMasm()); + ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp, scratch); + } // We're going to be returning by the ion calling convention, which returns // by ??? (for now, I think ldr pc, [sp]!) as_dtr(IsLoad, 32, PostIndex, pc, DTRAddr(sp, DtrOffImm(4))); // If we found a catch handler, this must be a baseline frame. Restore state // and jump to the catch block. bind(&catch_); - ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r0); - ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11); - ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp); + { + ScratchRegisterScope scratch(asMasm()); + ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r0, scratch); + ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11, scratch); + ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp, scratch); + } jump(r0); // If we found a finally block, this must be a baseline frame. Push two // values expected by JSOP_RETSUB: BooleanValue(true) and the exception. bind(&finally); ValueOperand exception = ValueOperand(r1, r2); loadValue(Operand(sp, offsetof(ResumeFromException, exception)), exception); - - ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r0); - ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11); - ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp); + { + ScratchRegisterScope scratch(asMasm()); + ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r0, scratch); + ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11, scratch); + ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp, scratch); + } pushValue(BooleanValue(true)); pushValue(exception); jump(r0); // Only used in debug mode. Return BaselineFrame->returnValue() to the // caller. bind(&return_); - ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11); - ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp); + { + ScratchRegisterScope scratch(asMasm()); + ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11, scratch); + ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp, scratch); + } loadValue(Address(r11, BaselineFrame::reverseOffsetOfReturnValue()), JSReturnOperand); ma_mov(r11, sp); pop(r11); // If profiling is enabled, then update the lastProfilingFrame to refer to caller // frame before returning. { Label skipProfilingInstrumentation; @@ -3606,29 +3698,34 @@ MacroAssemblerARMCompat::handleFailureWi bind(&skipProfilingInstrumentation); } ret(); // If we are bailing out to baseline to handle an exception, jump to the // bailout tail stub. bind(&bailout); - ma_ldr(Address(sp, offsetof(ResumeFromException, bailoutInfo)), r2); - ma_mov(Imm32(BAILOUT_RETURN_OK), r0); - ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r1); + { + ScratchRegisterScope scratch(asMasm()); + ma_ldr(Address(sp, offsetof(ResumeFromException, bailoutInfo)), r2, scratch); + ma_mov(Imm32(BAILOUT_RETURN_OK), r0); + ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r1, scratch); + } jump(r1); } Assembler::Condition MacroAssemblerARMCompat::testStringTruthy(bool truthy, const ValueOperand& value) { Register string = value.payloadReg(); ScratchRegisterScope scratch(asMasm()); - ma_dtr(IsLoad, string, Imm32(JSString::offsetOfLength()), scratch); - ma_cmp(scratch, Imm32(0)); + SecondScratchRegisterScope scratch2(asMasm()); + + ma_dtr(IsLoad, string, Imm32(JSString::offsetOfLength()), scratch, scratch2); + as_cmp(scratch, Imm8(0)); return truthy ? Assembler::NotEqual : Assembler::Equal; } void MacroAssemblerARMCompat::floor(FloatRegister input, Register output, Label* bail) { Label handleZero; Label handleNeg; @@ -3651,31 +3748,31 @@ MacroAssemblerARMCompat::floor(FloatRegi ma_mov(output, output, SetCC); ma_b(bail, Signed); ma_b(&fin); bind(&handleZero); // Move the top word of the double into the output reg, if it is non-zero, // then the original value was -0.0. as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1); - ma_cmp(output, Imm32(0)); + as_cmp(output, Imm8(0)); ma_b(bail, NonZero); ma_b(&fin); bind(&handleNeg); // Negative case, negate, then start dancing. ma_vneg(input, input); ma_vcvt_F64_U32(input, scratchDouble.uintOverlay()); ma_vxfer(scratchDouble.uintOverlay(), output); ma_vcvt_U32_F64(scratchDouble.uintOverlay(), scratchDouble); compareDouble(scratchDouble, input); - ma_add(output, Imm32(1), output, LeaveCC, NotEqual); + as_add(output, output, Imm8(1), LeaveCC, NotEqual); // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the // result will still be a negative number. - ma_rsb(output, Imm32(0), output, SetCC); + as_rsb(output, output, Imm8(0), SetCC); // Flip the negated input back to its original value. ma_vneg(input, input); // If the result looks non-negative, then this value didn't actually fit // into the int range, and special handling is required. Zero is also caught // by this case, but floor of a negative number should never be zero. ma_b(bail, NotSigned); bind(&fin); @@ -3705,34 +3802,34 @@ MacroAssemblerARMCompat::floorf(FloatReg ma_mov(output, output, SetCC); ma_b(bail, Signed); ma_b(&fin); bind(&handleZero); // Move the top word of the double into the output reg, if it is non-zero, // then the original value was -0.0. as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore, Always, 0); - ma_cmp(output, Imm32(0)); + as_cmp(output, Imm8(0)); ma_b(bail, NonZero); ma_b(&fin); bind(&handleNeg); // Negative case, negate, then start dancing. { ScratchFloat32Scope scratch(asMasm()); ma_vneg_f32(input, input); ma_vcvt_F32_U32(input, scratch.uintOverlay()); ma_vxfer(VFPRegister(scratch).uintOverlay(), output); ma_vcvt_U32_F32(scratch.uintOverlay(), scratch); compareFloat(scratch, input); - ma_add(output, Imm32(1), output, LeaveCC, NotEqual); + as_add(output, output, Imm8(1), LeaveCC, NotEqual); } // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the // result will still be a negative number. - ma_rsb(output, Imm32(0), output, SetCC); + as_rsb(output, output, Imm8(0), SetCC); // Flip the negated input back to its original value. ma_vneg_f32(input, input); // If the result looks non-negative, then this value didn't actually fit // into the int range, and special handling is required. Zero is also caught // by this case, but floor of a negative number should never be zero. ma_b(bail, NotSigned); bind(&fin); @@ -3768,28 +3865,28 @@ MacroAssemblerARMCompat::ceil(FloatRegis ma_neg(output, output, SetCC); ma_b(bail, NotSigned); ma_b(&fin); // Test for 0.0 / -0.0: if the top word of the input double is not zero, // then it was -0 and we need to bail out. bind(&handleZero); as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1); - ma_cmp(output, Imm32(0)); + as_cmp(output, Imm8(0)); ma_b(bail, NonZero); ma_b(&fin); // We are in the ]0; +inf] range: truncate integer values, maybe add 1 for // non integer values, maybe bail if overflow. bind(&handlePos); ma_vcvt_F64_U32(input, ScratchUIntReg); ma_vxfer(ScratchUIntReg, output); ma_vcvt_U32_F64(ScratchUIntReg, scratchDouble); compareDouble(scratchDouble, input); - ma_add(output, Imm32(1), output, LeaveCC, NotEqual); + as_add(output, output, Imm8(1), LeaveCC, NotEqual); // Bail out if the add overflowed or the result is non positive. ma_mov(output, output, SetCC); ma_b(bail, Signed); ma_b(bail, Zero); bind(&fin); } @@ -3829,33 +3926,33 @@ MacroAssemblerARMCompat::ceilf(FloatRegi ma_b(bail, NotSigned); ma_b(&fin); } // Test for 0.0 / -0.0: if the top word of the input double is not zero, // then it was -0 and we need to bail out. bind(&handleZero); as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore, Always, 0); - ma_cmp(output, Imm32(0)); + as_cmp(output, Imm8(0)); ma_b(bail, NonZero); ma_b(&fin); // We are in the ]0; +inf] range: truncate integer values, maybe add 1 for // non integer values, maybe bail if overflow. bind(&handlePos); { ScratchDoubleScope scratchDouble(asMasm()); FloatRegister scratchFloat = scratchDouble.asSingle(); FloatRegister scratchUInt = scratchDouble.uintOverlay(); ma_vcvt_F32_U32(input, scratchUInt); ma_vxfer(scratchUInt, output); ma_vcvt_U32_F32(scratchUInt, scratchFloat); compareFloat(scratchFloat, input); - ma_add(output, Imm32(1), output, LeaveCC, NotEqual); + as_add(output, output, Imm8(1), LeaveCC, NotEqual); // Bail on overflow or non-positive result. ma_mov(output, output, SetCC); ma_b(bail, Signed); ma_b(bail, Zero); } bind(&fin); @@ -3921,17 +4018,17 @@ MacroAssemblerARMCompat::round(FloatRegi ma_mov(output, output, SetCC); ma_b(bail, Signed); ma_b(&fin); bind(&handleZero); // Move the top word of the double into the output reg, if it is non-zero, // then the original value was -0.0 as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1); - ma_cmp(output, Imm32(0)); + as_cmp(output, Imm8(0)); ma_b(bail, NonZero); ma_b(&fin); bind(&handleNeg); // Negative case, negate, then start dancing. This number may be positive, // since we added 0.5. // Add 0.5 to negative numbers, store the result into tmp @@ -3941,20 +4038,20 @@ MacroAssemblerARMCompat::round(FloatRegi ma_vcvt_F64_U32(tmp, scratchDouble.uintOverlay()); ma_vxfer(VFPRegister(scratchDouble).uintOverlay(), output); // -output is now a correctly rounded value, unless the original value was // exactly halfway between two integers, at which point, it has been rounded // away from zero, when it should be rounded towards \infty. ma_vcvt_U32_F64(scratchDouble.uintOverlay(), scratchDouble); compareDouble(scratchDouble, tmp); - ma_sub(output, Imm32(1), output, LeaveCC, Equal); + as_sub(output, output, Imm8(1), LeaveCC, Equal); // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the // result will still be a negative number. - ma_rsb(output, Imm32(0), output, SetCC); + as_rsb(output, output, Imm8(0), SetCC); // If the result looks non-negative, then this value didn't actually fit // into the int range, and special handling is required, or it was zero, // which means the result is actually -0.0 which also requires special // handling. ma_b(bail, NotSigned); bind(&fin); @@ -3998,17 +4095,17 @@ MacroAssemblerARMCompat::roundf(FloatReg ma_b(bail, Signed); ma_b(&fin); bind(&handleZero); // Move the whole float32 into the output reg, if it is non-zero, then the // original value was -0.0. as_vxfer(output, InvalidReg, input, FloatToCore, Always, 0); - ma_cmp(output, Imm32(0)); + as_cmp(output, Imm8(0)); ma_b(bail, NonZero); ma_b(&fin); bind(&handleNeg); // Add 0.5 to negative numbers, storing the result into tmp. ma_vneg_f32(input, tmp); loadConstantFloat32(0.5f, scratchFloat); @@ -4029,22 +4126,22 @@ MacroAssemblerARMCompat::roundf(FloatReg Label flipSign; ma_b(&flipSign, Equal); // -output is now a correctly rounded value, unless the original value was // exactly halfway between two integers, at which point, it has been rounded // away from zero, when it should be rounded towards \infty. ma_vcvt_U32_F32(tmp.uintOverlay(), tmp); compareFloat(tmp, scratchFloat); - ma_sub(output, Imm32(1), output, LeaveCC, Equal); + as_sub(output, output, Imm8(1), LeaveCC, Equal); // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the // result will still be a negative number. bind(&flipSign); - ma_rsb(output, Imm32(0), output, SetCC); + as_rsb(output, output, Imm8(0), SetCC); // If the result looks non-negative, then this value didn't actually fit // into the int range, and special handling is required, or it was zero, // which means the result is actually -0.0 which also requires special // handling. ma_b(bail, NotSigned); bind(&fin); @@ -4067,29 +4164,33 @@ namespace jit { template<> Register MacroAssemblerARMCompat::computePointer<BaseIndex>(const BaseIndex& src, Register r) { Register base = src.base; Register index = src.index; uint32_t scale = Imm32::ShiftOf(src.scale).value; int32_t offset = src.offset; + + ScratchRegisterScope scratch(asMasm()); + as_add(r, base, lsl(index, scale)); if (offset != 0) - ma_add(r, Imm32(offset), r); + ma_add(r, Imm32(offset), r, scratch); return r; } template<> Register MacroAssemblerARMCompat::computePointer<Address>(const Address& src, Register r) { + ScratchRegisterScope scratch(asMasm()); if (src.offset == 0) return src.base; - ma_add(src.base, Imm32(src.offset), r); + ma_add(src.base, Imm32(src.offset), r, scratch); return r; } } // namespace jit } // namespace js template<typename T> void @@ -4132,17 +4233,17 @@ template<typename T> void MacroAssemblerARMCompat::compareExchangeARMv7(int nbytes, bool signExtend, const T& mem, Register oldval, Register newval, Register output) { Label again; Label done; ma_dmb(BarrierST); - AutoRegisterScope scratch2(asMasm(), secondScratchReg_); + SecondScratchRegisterScope scratch2(asMasm()); Register ptr = computePointer(mem, scratch2); ScratchRegisterScope scratch(asMasm()); bind(&again); switch (nbytes) { case 1: as_ldrexb(output, ptr); @@ -4228,17 +4329,17 @@ template<typename T> void MacroAssemblerARMCompat::atomicExchangeARMv7(int nbytes, bool signExtend, const T& mem, Register value, Register output) { Label again; Label done; ma_dmb(BarrierST